Flutter折线图X轴添加事件标记的实现方案咨询
在Flutter心率折线图X轴标记单次/持续事件的实现方案
针对你需要的X轴事件标记需求,推荐使用charts_flutter(Google官方维护的图表库)来实现,以下是具体步骤:
一、单次事件(如运动开始)的标记
通过图表的注释组件在指定时间点添加自定义符号,核心是用SeriesAnnotation配合自定义SymbolRenderer:
1. 定义数据模型
import 'package:charts_flutter/flutter.dart' as charts; import 'package:flutter/material.dart'; // 心率数据模型 class HeartRateData { final DateTime time; final int rate; HeartRateData(this.time, this.rate); } // 单次事件模型 class SingleEvent { final DateTime time; final String label; SingleEvent(this.time, this.label); }
2. 实现图表与标记渲染
class HeartRateChart extends StatelessWidget { final List<HeartRateData> heartRateData; final List<SingleEvent> singleEvents; HeartRateChart({required this.heartRateData, required this.singleEvents}); @override Widget build(BuildContext context) { final seriesList = [ // 心率折线系列 charts.Series<HeartRateData, DateTime>( id: 'Heart Rate', colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault, domainFn: (data, _) => data.time, measureFn: (data, _) => data.rate, data: heartRateData, ), // 单次事件标记系列 charts.Series<SingleEvent, DateTime>( id: 'Events', domainFn: (event, _) => event.time, measureFn: (_, __) => 0, // 固定在X轴附近 data: singleEvents, annotations: singleEvents.map((event) => charts.SeriesAnnotation<DateTime>( domain: event.time, measure: 0, renderer: charts.PointAnnotationRenderer( symbolRenderer: _CustomEventSymbol(event.label), ), )).toList(), ), ]; return charts.TimeSeriesChart( seriesList, animate: false, domainAxis: charts.DateTimeAxisSpec( renderSpec: charts.SmallTickRendererSpec(labelRotation: 45), ), ); } } // 自定义符号渲染器:画红色圆点+标签 class _CustomEventSymbol extends charts.SymbolRenderer { final String label; _CustomEventSymbol(this.label); @override void paint(charts.ChartCanvas canvas, Rectangle<num> bounds, {charts.Color? fillColor, charts.Color? strokeColor, double? strokeWidth}) { // 绘制圆点标记 canvas.drawCircle( Point(bounds.centerLeft.x + bounds.width/2, bounds.top + bounds.height/2), bounds.width/2, charts.MaterialPalette.red.shadeDefault, fill: true, ); // 绘制事件标签 canvas.drawText( charts.TextElement( label, style: charts.TextStyleSpec(fontSize: 10, color: charts.MaterialPalette.black), ), bounds.centerLeft.x.round(), (bounds.top - 15).round(), alignment: charts.TextAnchor.middle, ); } @override Size get preferredSize => const Size(16, 16); }
二、持续事件(如睡眠)的标记
利用图表的RangeAnnotation组件,在X轴时间区间上绘制背景色块,直观展示持续时长:
1. 添加持续事件模型
// 持续事件模型 class DurationEvent { final DateTime startTime; final DateTime endTime; final String label; DurationEvent(this.startTime, this.endTime, this.label); }
2. 整合到图表中
修改HeartRateChart的参数和build方法,添加RangeAnnotation行为:
class HeartRateChart extends StatelessWidget { final List<HeartRateData> heartRateData; final List<SingleEvent> singleEvents; final List<DurationEvent> durationEvents; // 新增参数 HeartRateChart({ required this.heartRateData, required this.singleEvents, required this.durationEvents, }); @override Widget build(BuildContext context) { // ... 保留之前的seriesList代码 return charts.TimeSeriesChart( seriesList, animate: false, domainAxis: charts.DateTimeAxisSpec( renderSpec: charts.SmallTickRendererSpec(labelRotation: 45), ), // 添加持续事件标记 behaviors: [ charts.RangeAnnotation( durationEvents.map((event) => charts.RangeAnnotationSegment( event.startTime, event.endTime, charts.RangeAnnotationAxisType.domain, color: charts.MaterialPalette.green.shadeDefault.lighter, label: charts.RangeAnnotationLabel( event.label, position: charts.AnnotationLabelPosition.margin, ), )).toList(), ), ], ); } }
三、使用示例
// 模拟测试数据 final testHeartRate = [ HeartRateData(DateTime(2024,5,20,8),70), HeartRateData(DateTime(2024,5,20,9),72), HeartRateData(DateTime(2024,5,20,10),105), HeartRateData(DateTime(2024,5,20,11),85), HeartRateData(DateTime(2024,5,20,22),65), HeartRateData(DateTime(2024,5,21,6),68), ]; final testSingleEvents = [SingleEvent(DateTime(2024,5,20,9,30), '运动开始')]; final testDurationEvents = [DurationEvent(DateTime(2024,5,20,22), DateTime(2024,5,21,6), '睡眠')]; // 在页面中使用图表 @override Widget build(BuildContext context) { return Scaffold( body: Padding( padding: const EdgeInsets.all(16.0), child: HeartRateChart( heartRateData: testHeartRate, singleEvents: testSingleEvents, durationEvents: testDurationEvents, ), ), ); }
替代方案说明
如果使用syncfusion_flutter_charts,思路一致:
- 单次事件:用
TextAnnotation或ShapeAnnotation定位到指定时间点 - 持续事件:用
StripLine设置start和end时间,作为区间标记
内容的提问来源于stack exchange,提问作者pravin
相关产品推荐
相关产品推荐

