From 6475847cb02c66d7e45aefb3ab7cefdb54cae68e Mon Sep 17 00:00:00 2001 From: TAJAOUART Mounir Date: Thu, 9 Feb 2023 11:25:48 +0100 Subject: [PATCH] display dashed graph --- android/local.properties | 2 + example/lib/main.dart | 54 +++++----- example/pubspec.lock | 8 ++ example/pubspec.yaml | 70 +----------- ios/Flutter/Generated.xcconfig | 14 +++ ios/Flutter/flutter_export_environment.sh | 13 +++ lib/real_time_chart.dart | 7 +- lib/src/live_chart.dart | 123 ++++++++++++++++++++++ lib/src/point.dart | 16 +++ 9 files changed, 212 insertions(+), 95 deletions(-) create mode 100644 android/local.properties create mode 100644 ios/Flutter/Generated.xcconfig create mode 100755 ios/Flutter/flutter_export_environment.sh create mode 100644 lib/src/live_chart.dart create mode 100644 lib/src/point.dart diff --git a/android/local.properties b/android/local.properties new file mode 100644 index 0000000..9f03445 --- /dev/null +++ b/android/local.properties @@ -0,0 +1,2 @@ +sdk.dir=/Users/aksel/Library/Android/sdk +flutter.sdk=/Users/aksel/development/flutter \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index a3c2af9..be161bd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:real_time_chart/real_time_chart.dart'; void main() { runApp(const MyApp()); @@ -29,39 +30,44 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: Colors.black, appBar: AppBar( title: Text(widget.title), ), body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], + child: Container( + color: Colors.red, + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.width, + child: RealTimeGraph( + key: const Key('LiveGraph'), + stream: getDataStream(), + ), ), ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), ); } + + int count = 0; + bool up = true; + + Stream getDataStream() { + return Stream.periodic(const Duration(milliseconds: 10), (_) { + if (count >= 100) { + up = false; + } else if (count <= 0) { + up = true; + } + if (up) { + count++; + } else { + count--; + } + + return count * 1.0; + }); + } } diff --git a/example/pubspec.lock b/example/pubspec.lock index 7e7f2ca..c04adb9 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -123,6 +123,13 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + real_time_chart: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" sky_engine: dependency: transitive description: flutter @@ -186,3 +193,4 @@ packages: version: "2.1.4" sdks: dart: ">=2.19.2 <3.0.0" + flutter: ">=1.17.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 67acd0f..a68c705 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,90 +1,28 @@ name: example description: A new Flutter project. -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -# In Windows, build-name is used as the major, minor, and patch parts -# of the product and file versions while build-number is used as the build suffix. version: 1.0.0+1 environment: sdk: '>=2.19.2 <3.0.0' -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + real_time_chart: + path: ../ dev_dependencies: flutter_test: sdk: flutter - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. flutter_lints: ^2.0.0 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec -# The following section is specific to Flutter packages. + flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/ios/Flutter/Generated.xcconfig b/ios/Flutter/Generated.xcconfig new file mode 100644 index 0000000..e15e552 --- /dev/null +++ b/ios/Flutter/Generated.xcconfig @@ -0,0 +1,14 @@ +// This is a generated file; do not edit or check into version control. +FLUTTER_ROOT=/Users/aksel/development/flutter +FLUTTER_APPLICATION_PATH=/Users/aksel/Documents/GitHub/real_time_chart +COCOAPODS_PARALLEL_CODE_SIGN=true +FLUTTER_TARGET=lib/main.dart +FLUTTER_BUILD_DIR=build +FLUTTER_BUILD_NAME=0.0.1 +FLUTTER_BUILD_NUMBER=0.0.1 +EXCLUDED_ARCHS[sdk=iphonesimulator*]=i386 +EXCLUDED_ARCHS[sdk=iphoneos*]=armv7 +DART_OBFUSCATION=false +TRACK_WIDGET_CREATION=true +TREE_SHAKE_ICONS=false +PACKAGE_CONFIG=.dart_tool/package_config.json diff --git a/ios/Flutter/flutter_export_environment.sh b/ios/Flutter/flutter_export_environment.sh new file mode 100755 index 0000000..571e03c --- /dev/null +++ b/ios/Flutter/flutter_export_environment.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# This is a generated file; do not edit or check into version control. +export "FLUTTER_ROOT=/Users/aksel/development/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/aksel/Documents/GitHub/real_time_chart" +export "COCOAPODS_PARALLEL_CODE_SIGN=true" +export "FLUTTER_TARGET=lib/main.dart" +export "FLUTTER_BUILD_DIR=build" +export "FLUTTER_BUILD_NAME=0.0.1" +export "FLUTTER_BUILD_NUMBER=0.0.1" +export "DART_OBFUSCATION=false" +export "TRACK_WIDGET_CREATION=true" +export "TREE_SHAKE_ICONS=false" +export "PACKAGE_CONFIG=.dart_tool/package_config.json" diff --git a/lib/real_time_chart.dart b/lib/real_time_chart.dart index f40da2a..0b0eee3 100644 --- a/lib/real_time_chart.dart +++ b/lib/real_time_chart.dart @@ -1,7 +1,4 @@ library real_time_chart; -/// A Calculator. -class Calculator { - /// Returns [value] plus 1. - int addOne(int value) => value + 1; -} +export 'src/point.dart'; +export 'src/live_chart.dart'; \ No newline at end of file diff --git a/lib/src/live_chart.dart b/lib/src/live_chart.dart new file mode 100644 index 0000000..4c7d9cd --- /dev/null +++ b/lib/src/live_chart.dart @@ -0,0 +1,123 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import 'point.dart'; + +class RealTimeGraph extends StatefulWidget { + final Stream stream; + + const RealTimeGraph({Key? key, required this.stream}) : super(key: key); + + @override + RealTimeGraphState createState() => RealTimeGraphState(); +} + +class RealTimeGraphState extends State + with TickerProviderStateMixin { + StreamSubscription? streamSubscription; + List> _data = []; + Timer? timer; + + @override + void initState() { + super.initState(); + + // Subscribe to the stream provided in the constructor + streamSubscription = widget.stream.listen(_listener); + + // Start a periodic timer to update the data for visualization + timer = Timer.periodic(const Duration(milliseconds: 50), (_) { + //_data.removeWhere((element) => element.y > 100); + + // Clone the data to avoid modifying the original list while iterating + List> data = _data.map((e) => e).toList(); + + // Increment the x value of each data point + for (var element in data) { + element.x = element.x - 1; + } + + // Trigger a rebuild with the updated data + setState(() { + _data = data; + }); + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + if (!constraints.maxWidth.isFinite || !constraints.maxHeight.isFinite) { + return const SizedBox.shrink(); + } + + return SizedBox( + key: Key('${constraints.maxWidth}${constraints.maxHeight}'), + height: constraints.maxWidth, + width: constraints.maxHeight, + child: CustomPaint( + painter: _GraphPainter(data: _data), + ), + ); + }, + ); + } + + void _listener(double data) { + // Insert the new data point in the beginning of the list + _data.insert(0, Point(0, data)); + } + + @override + void dispose() { + // Clean up resources when the widget is removed from the tree + streamSubscription?.cancel(); + timer?.cancel(); + super.dispose(); + } +} + +class _GraphPainter extends CustomPainter { + final List> data; + + _GraphPainter({required this.data}); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1.5 + ..color = Colors.white; + List points = []; + + // Iterate over the data points and add intermediate points if necessary + for (int i = 0; i < data.length - 1; i++) { + double y1 = size.height - data[i].y; + double x1 = data[i].x + size.width; + double y2 = size.height - data[i + 1].y; + double x2 = data[i + 1].x + size.width; + double yDiff = y1 - y2; + double xDiff = x1 - x2; + + // If the difference in y values is small, add intermediate points + if (xDiff.abs() <= 10) { + int numOfIntermediatePoints = (xDiff / 2).round(); + double yInterval = yDiff / numOfIntermediatePoints; + double xInterval = xDiff / numOfIntermediatePoints; + for (int j = 1; j <= numOfIntermediatePoints; j++) { + double intermediateY = y1 + yInterval * j; + double intermediateX = x1 + xInterval * j; + points.add(Offset(intermediateX, intermediateY)); + } + } + } + + canvas.drawPoints(PointMode.points, points, paint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/lib/src/point.dart b/lib/src/point.dart new file mode 100644 index 0000000..4dd4205 --- /dev/null +++ b/lib/src/point.dart @@ -0,0 +1,16 @@ +class Point { + T x; + T y; + + Point(this.x, this.y) { + this.x = x; + this.y = y; + } + + @override + String toString() { + return 'Point{x: $x, y: $y}'; + } + + +} \ No newline at end of file