diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
index 092d222..c9578a1 100644
--- a/example/test/widget_test.dart
+++ b/example/test/widget_test.dart
@@ -10,21 +10,4 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:example/main.dart';
-void main() {
- testWidgets('Counter increments smoke test', (WidgetTester tester) async {
- // Build our app and trigger a frame.
- await tester.pumpWidget(const MyApp());
-
- // Verify that our counter starts at 0.
- expect(find.text('0'), findsOneWidget);
- expect(find.text('1'), findsNothing);
-
- // Tap the '+' icon and trigger a frame.
- await tester.tap(find.byIcon(Icons.add));
- await tester.pump();
-
- // Verify that our counter has incremented.
- expect(find.text('0'), findsNothing);
- expect(find.text('1'), findsOneWidget);
- });
-}
+void main() {}
diff --git a/lib/real_time_chart.dart b/lib/real_time_chart.dart
index fe10296..d90a910 100644
--- a/lib/real_time_chart.dart
+++ b/lib/real_time_chart.dart
@@ -1,5 +1,3 @@
-library real_time_chart;
-
export 'src/chart_display.dart';
export 'src/point.dart';
-export 'src/live_chart.dart';
\ No newline at end of file
+export 'src/live_chart.dart';
diff --git a/lib/src/chart_display.dart b/lib/src/chart_display.dart
index d00db09..60b1c42 100644
--- a/lib/src/chart_display.dart
+++ b/lib/src/chart_display.dart
@@ -1 +1,20 @@
+/*
+ * Copyright (C) 2023 tajaouart.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Contact: developer@tajaouart.com
+ */
+
enum ChartDisplay { points, line }
diff --git a/lib/src/live_chart.dart b/lib/src/live_chart.dart
index 1dafa53..f2341e7 100644
--- a/lib/src/live_chart.dart
+++ b/lib/src/live_chart.dart
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2023 tajaouart.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Contact: developer@tajaouart.com
+ */
+
+
import 'dart:async';
import 'dart:math';
@@ -7,7 +27,9 @@ import 'chart_display.dart';
import 'point.dart';
class RealTimeGraph extends StatefulWidget {
- const RealTimeGraph({
+ final GlobalKey globalKey = GlobalKey();
+
+ RealTimeGraph({
this.updateDelay = const Duration(milliseconds: 50),
this.supportNegativeValuesDisplay = false,
this.displayMode = ChartDisplay.line,
@@ -24,7 +46,6 @@ class RealTimeGraph extends StatefulWidget {
this.minValue = 0,
this.speed = 1,
Key? key,
-
}) : super(key: key);
// Callback to build custom Y-axis text.
@@ -83,7 +104,7 @@ class RealTimeGraphState extends State
StreamSubscription? streamSubscription;
// List of data points to be displayed on the graph
- List> _data = [];
+ List> data = [];
// Timer to periodically update the data for visualization
Timer? timer;
@@ -101,31 +122,31 @@ class RealTimeGraphState extends State
// Start a periodic timer to update the data for visualization
timer = Timer.periodic(widget.updateDelay, (_) {
// delete data that is no longer displayed on the graph.
- _data.removeWhere((element) => element.x < canvasWidth * -1.5);
+ data.removeWhere((element) => element.x < canvasWidth * -1.5);
// Clone the data to avoid modifying the original list while iterating
- List> data = _data.map((e) => e).toList();
+ List> newData = data.map((e) => e).toList();
// Increment the x value of each data point
- for (var element in data) {
+ for (var element in newData) {
element.x = element.x - widget.speed;
}
// Trigger a rebuild with the updated data
setState(() {
- _data = data;
+ data = newData;
});
});
}
// Maximum value of the y-axis of the graph
double get maxValue {
- if (_data.isEmpty) {
+ if (data.isEmpty) {
return 0;
}
- final maxValue = _data.map((point) => point.y).reduce(max);
- final minValue = _data.map((point) => point.y).reduce(min);
+ final maxValue = data.map((point) => point.y).reduce(max);
+ final minValue = data.map((point) => point.y).reduce(min);
if (widget.supportNegativeValuesDisplay) {
if (maxValue > minValue.abs()) {
@@ -140,12 +161,12 @@ class RealTimeGraphState extends State
// Minimum value of the y-axis of the graph
double get minValue {
- if (_data.isEmpty) {
+ if (data.isEmpty) {
return 0;
}
- final maxValue = _data.map((point) => point.y).reduce(max);
- final minValue = _data.map((point) => point.y).reduce(min);
+ final maxValue = data.map((point) => point.y).reduce(max);
+ final minValue = data.map((point) => point.y).reduce(min);
if (widget.supportNegativeValuesDisplay) {
if (maxValue > minValue.abs()) {
@@ -182,6 +203,7 @@ class RealTimeGraphState extends State
// Display the y-axis line
if (widget.displayYAxisLines)
Container(
+ key: const Key('Y-Axis'),
color: widget.yAxisColor,
width: widget.axisStroke,
height: double.maxFinite,
@@ -207,13 +229,14 @@ class RealTimeGraphState extends State
child: ClipRRect(
child: RepaintBoundary(
child: CustomPaint(
+ key: Key(widget.displayMode.toString()),
size: Size(
constraints.maxWidth,
constraints.maxHeight,
),
painter: widget.displayMode == ChartDisplay.points
? _PointGraphPainter(
- data: _data,
+ data: data,
pointsSpacing: widget.pointsSpacing,
graphStroke: widget.graphStroke,
color: widget.graphColor,
@@ -221,7 +244,7 @@ class RealTimeGraphState extends State
widget.supportNegativeValuesDisplay,
)
: _LineGraphPainter(
- data: _data,
+ data: data,
graphStroke: widget.graphStroke,
color: widget.graphColor,
supportNegativeValuesDisplay:
@@ -240,6 +263,7 @@ class RealTimeGraphState extends State
? Alignment.center
: Alignment.bottomCenter,
child: Container(
+ key: const Key('X-Axis'),
color: widget.xAxisColor,
height: widget.axisStroke,
width: double.maxFinite,
@@ -259,9 +283,9 @@ class RealTimeGraphState extends State
);
}
- void _streamListener(double data) {
+ void _streamListener(double value) {
// Insert the new data point in the beginning of the list
- _data.insert(0, Point(0, data));
+ data.insert(0, Point(0, value));
}
@override
diff --git a/lib/src/point.dart b/lib/src/point.dart
index 4dd4205..de22f6e 100644
--- a/lib/src/point.dart
+++ b/lib/src/point.dart
@@ -1,3 +1,23 @@
+/*
+ * Copyright (C) 2023 tajaouart.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Contact: developer@tajaouart.com
+ */
+
+
class Point {
T x;
T y;
diff --git a/test/real_time_chart_test.dart b/test/real_time_chart_test.dart
index 32331b5..a9fc050 100644
--- a/test/real_time_chart_test.dart
+++ b/test/real_time_chart_test.dart
@@ -1,12 +1,219 @@
-import 'package:flutter_test/flutter_test.dart';
+/*
+ * Copyright (C) 2023 tajaouart.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * Contact: developer@tajaouart.com
+ */
+
+import 'dart:async';
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
import 'package:real_time_chart/real_time_chart.dart';
void main() {
- test('adds one to input values', () {
- final calculator = Calculator();
- expect(calculator.addOne(2), 3);
- expect(calculator.addOne(-7), -6);
- expect(calculator.addOne(0), 1);
+ testWidgets('Check Stream behaviour & initial values', (tester) async {
+ final streamController = StreamController();
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: RealTimeGraph(
+ key: const Key('RealTimeGraph'),
+ stream: streamController.stream,
+ supportNegativeValuesDisplay: true,
+ ),
+ ),
+ ),
+ );
+
+ final state = tester.state(find.byType(RealTimeGraph));
+
+ await tester.pumpAndSettle(const Duration(seconds: 1));
+
+ // Verify that the `streamSubscription` is created and active
+ expect(find.byType(RealTimeGraph), findsOneWidget);
+
+ expect(state.streamSubscription, isNotNull);
+ expect(state.streamSubscription!.isPaused, isFalse);
+
+ // Verify that the timer is created and active
+ expect(state.timer, isNotNull);
+ expect(state.timer!.isActive, isTrue);
+
+ // Verify that the data starts with an empty list
+ expect(state.data, isEmpty);
+
+ // Add data to the stream and verify that the graph updates
+ streamController.add(10.0);
+ await tester.pump();
+ expect(state.data, isNotEmpty);
+ expect(state.data[0].y, 10.0);
+
+ // Verify that the `minValue` and `maxValue` functions return the correct values
+ streamController.add(-5.0);
+ await tester.pumpAndSettle();
+
+ // min & max values are always symetrics
+ expect(state.minValue, -10.0);
+ expect(state.maxValue, 10.0);
+
+ streamController.close();
+ });
+
+ testWidgets('displayYAxisValues > true', (WidgetTester tester) async {
+ // Create a StreamController to simulate the stream data
+ final streamController = StreamController();
+
+ // Build the widget tree
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: RealTimeGraph(
+ displayYAxisValues: true,
+ axisTextBuilder: (double value) {
+ return Text(
+ '$value',
+ style: const TextStyle(color: Colors.purple),
+ );
+ },
+ stream: streamController.stream,
+ ),
+ ),
+ ),
+ );
+
+ // Add data to the stream
+ streamController.add(1.0);
+ streamController.add(2.0);
+
+ // Trigger a rebuild of the widget tree to reflect the changes
+ await tester.pumpAndSettle();
+
+ // Verify the y axis values after the stream update
+ expect(find.text('0.0'), findsOneWidget);
+ // median value.
+ expect(find.text('1.0'), findsOneWidget);
+ // max value
+ expect(find.text('2.0'), findsOneWidget);
+
+ // Verify that the text builder is used
+ final text = tester.widget(find.text('0.0'));
+ expect(text.style?.color, Colors.purple);
+ });
+
+ testWidgets('displayYAxisValues > false', (WidgetTester tester) async {
+ // Create a StreamController to simulate the stream data
+ final StreamController streamController =
+ StreamController();
+
+ // Build the widget tree
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: RealTimeGraph(
+ displayYAxisValues: false,
+ axisTextBuilder: (double value) => Text('$value'),
+ stream: streamController.stream,
+ ),
+ ),
+ ),
+ );
+
+ // Add data to the stream
+ streamController.add(1.0);
+ streamController.add(2.0);
+
+ // Trigger a rebuild of the widget tree to reflect the changes
+ await tester.pumpAndSettle();
+
+ // Verify the y axis values after the stream update
+ expect(find.text('0.0'), findsNothing);
+ // median value.
+ expect(find.text('1.0'), findsNothing);
+ // max value
+ expect(find.text('2.0'), findsNothing);
+ });
+
+ testWidgets('Axis stroke and color', (WidgetTester tester) async {
+ // Create a StreamController to simulate the stream data
+ final streamController = StreamController();
+
+ // Build the widget tree
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: RealTimeGraph(
+ displayYAxisValues: true,
+ xAxisColor: Colors.red,
+ yAxisColor: Colors.green,
+ axisStroke: 2.0,
+ stream: streamController.stream,
+ ),
+ ),
+ ),
+ );
+
+ // Test if the axis's colors
+ expect(
+ tester.widget(find.byKey(const Key('X-Axis')).first).color,
+ Colors.red,
+ );
+
+ expect(
+ tester.widget(find.byKey(const Key('Y-Axis')).first).color,
+ Colors.green,
+ );
+
+ // Test if the axis stroke is 2.0
+ final size1 = tester.getSize(find.byKey(const Key('X-Axis')));
+ final size2 = tester.getSize(find.byKey(const Key('Y-Axis')));
+ expect(size1.height, equals(2.0));
+ expect(size2.width, equals(2.0));
+ });
+
+ testWidgets('Verify behaviour of displayMode property ', (tester) async {
+ // Create a StreamController to simulate the stream data
+ final streamController = StreamController();
+
+ // Build the widget tree
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: RealTimeGraph(
+ displayMode: ChartDisplay.line,
+ stream: streamController.stream,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byKey(Key(ChartDisplay.line.toString())), findsOneWidget);
+
+ // Build the widget tree
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Scaffold(
+ body: RealTimeGraph(
+ displayMode: ChartDisplay.points,
+ stream: streamController.stream,
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byKey(Key(ChartDisplay.points.toString())), findsOneWidget);
});
}