11import 'dart:async' ;
22import 'dart:math' ;
33import 'package:flutter/material.dart' ;
4- import 'package:community_charts_flutter/community_charts_flutter.dart'
5- as charts;
4+ import 'package:community_charts_flutter/community_charts_flutter.dart' as charts;
65
76class RollingChart extends StatefulWidget {
87 final Stream <(int , double )> dataSteam;
98 final int timestampExponent; // e.g., 6 for microseconds to milliseconds
10- final int timeWindow; // in seconds
9+ final int timeWindow; // in milliseconds
1110
1211 const RollingChart ({
1312 super .key,
@@ -21,9 +20,8 @@ class RollingChart extends StatefulWidget {
2120}
2221
2322class _RollingChartState extends State <RollingChart > {
24- List <charts.Series <_ChartPoint , num >> _seriesList = [];
25- final List <_RawChartPoint > _rawData = [];
26- List <_ChartPoint > _normalizedData = [];
23+ List <charts.Series <_ChartPoint , int >> _seriesList = [];
24+ final List <_ChartPoint > _data = [];
2725 StreamSubscription ? _subscription;
2826
2927 @override
@@ -44,87 +42,56 @@ class _RollingChartState extends State<RollingChart> {
4442 void _listenToStream () {
4543 _subscription = widget.dataSteam.listen ((event) {
4644 final (timestamp, value) = event;
47-
45+
4846 setState (() {
49- _rawData .add (_RawChartPoint (timestamp, value));
50-
47+ _data .add (_ChartPoint (timestamp, value));
48+
5149 // Remove old data outside time window
52- final ticksPerSecond = pow (10 , - widget.timestampExponent).toDouble ();
53- final cutoffTime =
54- timestamp - (widget.timeWindow * ticksPerSecond).round ();
55- _rawData.removeWhere ((data) => data.timestamp < cutoffTime);
56-
50+ int cutoffTime = timestamp - (widget.timeWindow * pow (10 , - widget.timestampExponent) as int );
51+ _data.removeWhere ((data) => data.time < cutoffTime);
52+
5753 _updateSeries ();
5854 });
5955 });
6056 }
6157
6258 void _updateSeries () {
63- if (_rawData.isEmpty) {
64- _normalizedData = [];
65- _seriesList = [];
66- return ;
67- }
68-
69- final firstTimestamp = _rawData.first.timestamp;
70- final secondsPerTick = pow (10 , widget.timestampExponent).toDouble ();
71-
72- _normalizedData = _rawData
73- .map (
74- (point) => _ChartPoint (
75- (point.timestamp - firstTimestamp) * secondsPerTick,
76- point.value,
77- ),
78- )
79- .toList (growable: false );
80-
8159 _seriesList = [
82- charts.Series <_ChartPoint , num >(
83- id: 'Live Data' ,
84- colorFn: (_, __) => charts.MaterialPalette .red.shadeDefault,
85- domainFn: (_ChartPoint point, _) => point.timeSeconds ,
86- measureFn: (_ChartPoint point, _) => point.value,
87- data: _normalizedData ,
60+ charts.Series <_ChartPoint , int >(
61+ id: 'Live Data' ,
62+ colorFn: (_, __) => charts.MaterialPalette .red.shadeDefault,
63+ domainFn: (_ChartPoint point, _) => point.time ,
64+ measureFn: (_ChartPoint point, _) => point.value,
65+ data: List . of (_data) ,
8866 ),
8967 ];
9068 }
9169
9270 @override
9371 Widget build (BuildContext context) {
94- final filteredPoints = _normalizedData ;
72+ final filteredPoints = _data ;
9573
96- final xValues = filteredPoints.map ((e) => e.timeSeconds ).toList ();
74+ final xValues = filteredPoints.map ((e) => e.time ).toList ();
9775 final yValues = filteredPoints.map ((e) => e.value).toList ();
9876
99- final double xMin = 0 ;
100- final double xMax = max (
101- widget.timeWindow.toDouble (),
102- xValues.isNotEmpty ? xValues.reduce ((a, b) => a > b ? a : b) : 0 ,
103- );
77+ final int ? xMin = xValues.isNotEmpty ? xValues.reduce ((a, b) => a < b ? a : b) : null ;
78+ final int ? xMax = xValues.isNotEmpty ? xValues.reduce ((a, b) => a > b ? a : b) : null ;
10479
105- final double ? yMin =
106- yValues.isNotEmpty ? yValues.reduce ((a, b) => a < b ? a : b) : null ;
107- final double ? yMax =
108- yValues.isNotEmpty ? yValues.reduce ((a, b) => a > b ? a : b) : null ;
80+ final double ? yMin = yValues.isNotEmpty ? yValues.reduce ((a, b) => a < b ? a : b) : null ;
81+ final double ? yMax = yValues.isNotEmpty ? yValues.reduce ((a, b) => a > b ? a : b) : null ;
10982
11083 return charts.LineChart (
11184 _seriesList,
11285 animate: false ,
11386 domainAxis: charts.NumericAxisSpec (
114- viewport: charts.NumericExtents (xMin, xMax),
115- tickFormatterSpec: charts.BasicNumericTickFormatterSpec ((num ? value) {
116- if (value == null ) return '' ;
117- final rounded = value.roundToDouble ();
118- if ((value - rounded).abs () < 0.05 ) {
119- return '${rounded .toInt ()}s' ;
120- }
121- return '${value .toStringAsFixed (1 )}s' ;
122- }),
87+ viewport: xMin != null && xMax != null
88+ ? charts.NumericExtents (xMin, xMax)
89+ : null ,
12390 ),
12491 primaryMeasureAxis: charts.NumericAxisSpec (
12592 viewport: yMin != null && yMax != null
126- ? charts.NumericExtents (yMin, yMax)
127- : null ,
93+ ? charts.NumericExtents (yMin, yMax)
94+ : null ,
12895 ),
12996 );
13097 }
@@ -136,16 +103,9 @@ class _RollingChartState extends State<RollingChart> {
136103 }
137104}
138105
139- class _RawChartPoint {
140- final int timestamp;
141- final double value;
142-
143- _RawChartPoint (this .timestamp, this .value);
144- }
145-
146106class _ChartPoint {
147- final double timeSeconds ;
107+ final int time ;
148108 final double value;
149109
150- _ChartPoint (this .timeSeconds , this .value);
110+ _ChartPoint (this .time , this .value);
151111}
0 commit comments