├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example └── example.dart ├── flutter_sparkline.iml ├── lib ├── flutter_sparkline.dart └── src │ └── sparkline.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshots ├── example_base.png ├── example_fill_above.png ├── example_fill_below.png ├── example_fill_gradient.png ├── example_line_gradient.png ├── example_lineopts.png ├── example_points_all.png ├── example_points_last.png ├── sparkline.png └── sparkline_.png └── test └── flutter_sparkline_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | .atom/ 4 | .idea 5 | .vscode/ 6 | 7 | .packages 8 | .pub/ 9 | packages 10 | 11 | ios/Flutter/Generated.xcconfig 12 | ios/Runner/GeneratedPluginRegistrant.* 13 | 14 | .history/ 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.1.0] - 2018-09-04 2 | 3 | - Jump to version 2 of the dart sdk 4 | 5 | ## [0.0.4] - 2018-04-27 6 | 7 | - Add lineGradient and fillGradient options. 8 | 9 | ## [0.0.3] - 2018-04-17 10 | 11 | - Add basic example 12 | 13 | ## [0.0.2] - 2018-04-16 14 | 15 | - Fix readme screenshots for pub. 16 | 17 | ## [0.0.1] - 2018-04-16 18 | 19 | - Customizable fillMode and fillColor. 20 | - Cuztomizable pointsMode, pointSize, and pointColor. 21 | - Customizable lineWidth and lineColor. 22 | - Basic Sparkline. 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Victor Choueiri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![pub package](https://img.shields.io/pub/v/flutter_sparkline.svg)](https://pub.dartlang.org/packages/flutter_sparkline) 2 | 3 | # flutter_sparkline 4 | 5 | Beautiful sparkline charts for Flutter. 6 | 7 | ![screenshot](screenshots/sparkline.png) 8 | 9 | ## Installation 10 | 11 | Install the latest version [from pub](https://pub.dartlang.org/packages/flutter_sparkline#-installing-tab-). 12 | 13 | ## Quick Start 14 | 15 | Import the package, create a `Sparkline`, and pass it your data. 16 | 17 | ```dart 18 | import 'package:flutter/material.dart'; 19 | import 'package:flutter_sparkline/flutter_sparkline.dart'; 20 | 21 | void main() { 22 | var data = [0.0, 1.0, 1.5, 2.0, 0.0, 0.0, -0.5, -1.0, -0.5, 0.0, 0.0]; 23 | runApp( 24 | new MaterialApp( 25 | home: new Scaffold( 26 | body: new Center( 27 | child: new Container( 28 | width: 300.0, 29 | height: 100.0, 30 | child: new Sparkline( 31 | data: data, 32 | ), 33 | ), 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | ``` 40 | 41 | ![base example screenshot](screenshots/example_base.png) 42 | 43 | 44 | ## Customization 45 | 46 | ### Sparkline 47 | 48 | | Property | Default | 49 | |--------------|:----------------:| 50 | | lineWidth | 2.0 | 51 | | lineColor | Colors.lightBlue | 52 | | lineGradient | null | 53 | 54 | Example: 55 | 56 | ```dart 57 | new Sparkline( 58 | data: data, 59 | lineWidth: 5.0, 60 | lineColor: Colors.purple, 61 | ); 62 | ``` 63 | 64 | ![lineopts example screenshot](screenshots/example_lineopts.png) 65 | 66 | ```dart 67 | new Sparkline( 68 | data: data, 69 | lineWidth: 10.0, 70 | lineGradient: new LinearGradient( 71 | begin: Alignment.topCenter, 72 | end: Alignment.bottomCenter, 73 | colors: [Colors.purple[800], Colors.purple[200]], 74 | ), 75 | ); 76 | ``` 77 | 78 | ![lineopts example screenshot](screenshots/example_line_gradient.png) 79 | 80 | --- 81 | 82 | ### Points 83 | 84 | | Property | Default | 85 | |------------|:---------------------:| 86 | | pointsMode | PointsMode.none | 87 | | pointSize | 4.0 | 88 | | pointColor | Colors.lightBlue[800] | 89 | 90 | | PointsMode | Description | 91 | |:--------------:|-------------------------------------------| 92 | | none (default) | Do not draw individual points. | 93 | | all | Draw all the points in the data set. | 94 | | last | Draw only the last point in the data set. | 95 | 96 | Example: 97 | 98 | ```dart 99 | new Sparkline( 100 | data: data, 101 | pointsMode: PointsMode.all, 102 | pointSize: 8.0, 103 | pointColor: Colors.amber, 104 | ); 105 | ``` 106 | 107 | ![all points example screenshot](screenshots/example_points_all.png) 108 | 109 | ```dart 110 | new Sparkline( 111 | data: data, 112 | pointsMode: PointsMode.last, 113 | pointSize: 8.0, 114 | pointColor: Colors.amber, 115 | ); 116 | ``` 117 | 118 | ![last point example screenshot](screenshots/example_points_last.png) 119 | 120 | --- 121 | 122 | ### Fill 123 | 124 | | Property | Default | 125 | |--------------|:---------------------:| 126 | | fillMode | FillMode.none | 127 | | fillColor | Colors.lightBlue[200] | 128 | | fillGradient | null | 129 | 130 | | FillMode | Description | 131 | |:--------------:|---------------------------------------| 132 | | none (default) | Do not fill, draw only the sparkline. | 133 | | above | Fill the area above the sparkline. | 134 | | below | Fill the area below the sparkline. | 135 | 136 | Example: 137 | 138 | ```dart 139 | new Sparkline( 140 | data: data, 141 | fillMode: FillMode.below, 142 | fillColor: Colors.red[200], 143 | ); 144 | ``` 145 | 146 | ![fill below example screenshot](screenshots/example_fill_below.png) 147 | 148 | ```dart 149 | new Sparkline( 150 | data: data, 151 | fillMode: FillMode.above, 152 | fillColor: Colors.red[200], 153 | ); 154 | ``` 155 | 156 | ![fill above example screenshot](screenshots/example_fill_above.png) 157 | 158 | ```dart 159 | new Sparkline( 160 | data: data, 161 | fillMode: FillMode.below, 162 | fillGradient: new LinearGradient( 163 | begin: Alignment.topCenter, 164 | end: Alignment.bottomCenter, 165 | colors: [Colors.red[800], Colors.red[200]], 166 | ), 167 | ); 168 | ``` 169 | 170 | ![fill above example screenshot](screenshots/example_fill_gradient.png) 171 | 172 | --- 173 | 174 | ### Todo: 175 | 176 | - [x] simple sparkline 177 | - [x] custom line width 178 | - [x] custom line color 179 | - [x] optional rounded corners 180 | - [x] fill 181 | - [x] embiggen individual points/change color 182 | - [x] different points modes [all/last/none] 183 | - [ ] animate between two sparklines 184 | - [ ] animate drawing a single sparkline 185 | - [ ] gesture detector to select closest point to tap 186 | - [ ] baseline 187 | - [x] different fill modes [above/below/none] 188 | - [x] fix edge points overflowing by offsetting by lineWidth 189 | - [ ] better corner rounding 190 | - [ ] axis labels 191 | - [x] gradient shader on line paint 192 | - [x] gradient shader on fill paint 193 | - [ ] multiple overlapping sparklines on a shared axis 194 | - [ ] tests 195 | -------------------------------------------------------------------------------- /example/example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_sparkline/flutter_sparkline.dart'; 5 | 6 | math.Random random = new math.Random(); 7 | 8 | List _generateRandomData(int count) { 9 | List result = []; 10 | for (int i = 0; i < count; i++) { 11 | result.add(random.nextDouble() * 100); 12 | } 13 | return result; 14 | } 15 | 16 | void main() { 17 | var data = _generateRandomData(100); 18 | runApp( 19 | new MaterialApp( 20 | home: new Scaffold( 21 | body: new Center( 22 | child: new Container( 23 | width: 300.0, 24 | height: 100.0, 25 | child: new Sparkline( 26 | data: data, 27 | lineColor: Colors.lightGreen[500], 28 | fillMode: FillMode.below, 29 | fillColor: Colors.lightGreen[200], 30 | pointsMode: PointsMode.all, 31 | pointSize: 5.0, 32 | pointColor: Colors.amber, 33 | ), 34 | ), 35 | ), 36 | ), 37 | ), 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /flutter_sparkline.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/flutter_sparkline.dart: -------------------------------------------------------------------------------- 1 | library flutter_sparkline; 2 | 3 | export 'src/sparkline.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/sparkline.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui' as ui show PointMode; 3 | 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | /// Strategy used when filling the area of a sparkline. 8 | enum FillMode { 9 | /// Do not fill, draw only the sparkline. 10 | none, 11 | 12 | /// Fill the area above the sparkline: creating a closed path from the line 13 | /// to the upper edge of the widget. 14 | above, 15 | 16 | /// Fill the area below the sparkline: creating a closed path from the line 17 | /// to the lower edge of the widget. 18 | below, 19 | } 20 | 21 | /// Strategy used when drawing individual data points over the sparkline. 22 | enum PointsMode { 23 | /// Do not draw individual points. 24 | none, 25 | 26 | /// Draw all the points in the data set. 27 | all, 28 | 29 | /// Draw only the last point in the data set. 30 | last, 31 | } 32 | 33 | /// A widget that draws a sparkline chart. 34 | /// 35 | /// Represents the given [data] in a sparkline chart that spans the available 36 | /// space. 37 | /// 38 | /// By default only the sparkline is drawn, with its looks defined by 39 | /// the [lineWidth], [lineColor], and [lineGradient] properties. 40 | /// 41 | /// The corners between two segments of the sparkline can be made sharper by 42 | /// setting [sharpCorners] to true. 43 | /// 44 | /// The area above or below the sparkline can be filled with the provided 45 | /// [fillColor] or [fillGradient] by setting the desired [fillMode]. 46 | /// 47 | /// [pointsMode] controls how individual points are drawn over the sparkline 48 | /// at the provided data point. Their appearance is determined by the 49 | /// [pointSize] and [pointColor] properties. 50 | /// 51 | /// By default, the sparkline is sized to fit its container. If the 52 | /// sparkline is in an unbounded space, it will size itself according to the 53 | /// given [fallbackWidth] and [fallbackHeight]. 54 | class Sparkline extends StatelessWidget { 55 | /// Creates a widget that represents provided [data] in a Sparkline chart. 56 | Sparkline({ 57 | Key key, 58 | @required this.data, 59 | this.lineWidth = 2.0, 60 | this.lineColor = Colors.lightBlue, 61 | this.lineGradient, 62 | this.pointsMode = PointsMode.none, 63 | this.pointSize = 4.0, 64 | this.pointColor = const Color(0xFF0277BD), //Colors.lightBlue[800] 65 | this.sharpCorners = false, 66 | this.fillMode = FillMode.none, 67 | this.fillColor = const Color(0xFF81D4FA), //Colors.lightBlue[200] 68 | this.fillGradient, 69 | this.fallbackHeight = 100.0, 70 | this.fallbackWidth = 300.0, 71 | this.enableGridLines = false, 72 | this.gridLineColor = Colors.grey, 73 | this.gridLineAmount = 5, 74 | this.gridLineWidth = 0.5, 75 | this.gridLineLabelColor = Colors.grey, 76 | this.labelPrefix = "\$", 77 | }) : assert(data != null), 78 | assert(lineWidth != null), 79 | assert(lineColor != null), 80 | assert(pointsMode != null), 81 | assert(pointSize != null), 82 | assert(pointColor != null), 83 | assert(sharpCorners != null), 84 | assert(fillMode != null), 85 | assert(fillColor != null), 86 | assert(fallbackHeight != null), 87 | assert(fallbackWidth != null), 88 | super(key: key); 89 | 90 | /// List of values to be represented by the sparkline. 91 | /// 92 | /// Each data entry represents an individual point on the chart, with a path 93 | /// drawn connecting the consecutive points to form the sparkline. 94 | /// 95 | /// The values are normalized to fit within the bounds of the chart. 96 | final List data; 97 | 98 | /// The width of the sparkline. 99 | /// 100 | /// Defaults to 2.0. 101 | final double lineWidth; 102 | 103 | /// The color of the sparkline. 104 | /// 105 | /// Defaults to Colors.lightBlue. 106 | /// 107 | /// This is ignored if [lineGradient] is non-null. 108 | final Color lineColor; 109 | 110 | /// A gradient to use when coloring the sparkline. 111 | /// 112 | /// If this is specified, [lineColor] has no effect. 113 | final Gradient lineGradient; 114 | 115 | /// Determines how individual data points should be drawn over the sparkline. 116 | /// 117 | /// Defaults to [PointsMode.none]. 118 | final PointsMode pointsMode; 119 | 120 | /// The size to use when drawing individual data points over the sparkline. 121 | /// 122 | /// Defaults to 4.0. 123 | final double pointSize; 124 | 125 | /// The color used when drawing individual data points over the sparkline. 126 | /// 127 | /// Defaults to Colors.lightBlue[800]. 128 | final Color pointColor; 129 | 130 | /// Determines if the sparkline path should have sharp corners where two 131 | /// segments intersect. 132 | /// 133 | /// Defaults to false. 134 | final bool sharpCorners; 135 | 136 | /// Determines the area that should be filled with [fillColor]. 137 | /// 138 | /// Defaults to [FillMode.none]. 139 | final FillMode fillMode; 140 | 141 | /// The fill color used in the chart, as determined by [fillMode]. 142 | /// 143 | /// Defaults to Colors.lightBlue[200]. 144 | /// 145 | /// This is ignored if [fillGradient] is non-null. 146 | final Color fillColor; 147 | 148 | /// A gradient to use when filling the chart, as determined by [fillMode]. 149 | /// 150 | /// If this is specified, [fillColor] has no effect. 151 | final Gradient fillGradient; 152 | 153 | /// The width to use when the sparkline is in a situation with an unbounded 154 | /// width. 155 | /// 156 | /// See also: 157 | /// 158 | /// * [fallbackHeight], the same but vertically. 159 | final double fallbackWidth; 160 | 161 | /// The height to use when the sparkline is in a situation with an unbounded 162 | /// height. 163 | /// 164 | /// See also: 165 | /// 166 | /// * [fallbackWidth], the same but horizontally. 167 | final double fallbackHeight; 168 | 169 | /// Enable or disable grid lines 170 | final bool enableGridLines; 171 | 172 | /// Color of grid lines and label text 173 | final Color gridLineColor; 174 | final Color gridLineLabelColor; 175 | 176 | /// Number of grid lines 177 | final int gridLineAmount; 178 | 179 | /// Width of grid lines 180 | final double gridLineWidth; 181 | 182 | /// Symbol prefix for grid line labels 183 | final String labelPrefix; 184 | 185 | @override 186 | Widget build(BuildContext context) { 187 | return new LimitedBox( 188 | maxWidth: fallbackWidth, 189 | maxHeight: fallbackHeight, 190 | child: new CustomPaint( 191 | size: Size.infinite, 192 | painter: new _SparklinePainter( 193 | data, 194 | lineWidth: lineWidth, 195 | lineColor: lineColor, 196 | lineGradient: lineGradient, 197 | sharpCorners: sharpCorners, 198 | fillMode: fillMode, 199 | fillColor: fillColor, 200 | fillGradient: fillGradient, 201 | pointsMode: pointsMode, 202 | pointSize: pointSize, 203 | pointColor: pointColor, 204 | enableGridLines: enableGridLines, 205 | gridLineColor: gridLineColor, 206 | gridLineAmount: gridLineAmount, 207 | gridLineLabelColor: gridLineLabelColor, 208 | gridLineWidth: gridLineWidth, 209 | labelPrefix: labelPrefix 210 | ), 211 | ), 212 | ); 213 | } 214 | } 215 | 216 | class _SparklinePainter extends CustomPainter { 217 | _SparklinePainter( 218 | this.dataPoints, { 219 | @required this.lineWidth, 220 | @required this.lineColor, 221 | this.lineGradient, 222 | @required this.sharpCorners, 223 | @required this.fillMode, 224 | @required this.fillColor, 225 | this.fillGradient, 226 | @required this.pointsMode, 227 | @required this.pointSize, 228 | @required this.pointColor, 229 | @required this.enableGridLines, 230 | this.gridLineColor, 231 | this.gridLineAmount, 232 | this.gridLineWidth, 233 | this.gridLineLabelColor, 234 | this.labelPrefix 235 | }) : _max = dataPoints.reduce(math.max), 236 | _min = dataPoints.reduce(math.min); 237 | 238 | final List dataPoints; 239 | 240 | final double lineWidth; 241 | final Color lineColor; 242 | final Gradient lineGradient; 243 | 244 | final bool sharpCorners; 245 | 246 | final FillMode fillMode; 247 | final Color fillColor; 248 | final Gradient fillGradient; 249 | 250 | final PointsMode pointsMode; 251 | final double pointSize; 252 | final Color pointColor; 253 | 254 | final double _max; 255 | final double _min; 256 | 257 | final bool enableGridLines; 258 | final Color gridLineColor; 259 | final int gridLineAmount; 260 | final double gridLineWidth; 261 | final Color gridLineLabelColor; 262 | final String labelPrefix; 263 | 264 | List gridLineTextPainters = []; 265 | 266 | update() { 267 | if (enableGridLines) { 268 | double gridLineValue; 269 | for (int i = 0; i < gridLineAmount; i++) { 270 | // Label grid lines 271 | gridLineValue = _max - (((_max - _min) / (gridLineAmount - 1)) * i); 272 | 273 | String gridLineText; 274 | if (gridLineValue < 1) { 275 | gridLineText = gridLineValue.toStringAsPrecision(4); 276 | } else if (gridLineValue < 999) { 277 | gridLineText = gridLineValue.toStringAsFixed(2); 278 | } else { 279 | gridLineText = gridLineValue.round().toString(); 280 | } 281 | 282 | gridLineTextPainters.add(new TextPainter( 283 | text: new TextSpan( 284 | text: labelPrefix + gridLineText, 285 | style: new TextStyle( 286 | color: gridLineLabelColor, 287 | fontSize: 10.0, 288 | fontWeight: FontWeight.bold)), 289 | textDirection: TextDirection.ltr)); 290 | gridLineTextPainters[i].layout(); 291 | } 292 | } 293 | } 294 | 295 | @override 296 | void paint(Canvas canvas, Size size) { 297 | double width = size.width - lineWidth; 298 | final double height = size.height - lineWidth; 299 | final double heightNormalizer = height / (_max - _min); 300 | 301 | final Path path = new Path(); 302 | final List points = []; 303 | 304 | Offset startPoint; 305 | 306 | if (gridLineTextPainters.isEmpty) { 307 | update(); 308 | } 309 | 310 | if (enableGridLines) { 311 | width = size.width - gridLineTextPainters[0].text.text.length * 6; 312 | Paint gridPaint = new Paint() 313 | ..color = gridLineColor 314 | ..strokeWidth = gridLineWidth; 315 | 316 | double gridLineDist = height / (gridLineAmount - 1); 317 | double gridLineY; 318 | 319 | // Draw grid lines 320 | for (int i = 0; i < gridLineAmount; i++) { 321 | gridLineY = (gridLineDist * i).round().toDouble(); 322 | canvas.drawLine(new Offset(0.0, gridLineY), 323 | new Offset(width, gridLineY), gridPaint); 324 | 325 | // Label grid lines 326 | gridLineTextPainters[i] 327 | .paint(canvas, new Offset(width + 2.0, gridLineY - 6.0)); 328 | } 329 | } 330 | 331 | final double widthNormalizer = width / dataPoints.length; 332 | 333 | for (int i = 0; i < dataPoints.length; i++) { 334 | double x = i * widthNormalizer + lineWidth / 2; 335 | double y = 336 | height - (dataPoints[i] - _min) * heightNormalizer + lineWidth / 2; 337 | 338 | if (pointsMode == PointsMode.all) { 339 | points.add(new Offset(x, y)); 340 | } 341 | 342 | if (pointsMode == PointsMode.last && i == dataPoints.length - 1) { 343 | points.add(new Offset(x, y)); 344 | } 345 | 346 | if (i == 0) { 347 | startPoint = new Offset(x, y); 348 | path.moveTo(x, y); 349 | } else { 350 | path.lineTo(x, y); 351 | } 352 | } 353 | 354 | Paint paint = new Paint() 355 | ..strokeWidth = lineWidth 356 | ..color = lineColor 357 | ..strokeCap = StrokeCap.round 358 | ..strokeJoin = sharpCorners ? StrokeJoin.miter : StrokeJoin.round 359 | ..style = PaintingStyle.stroke; 360 | 361 | if (lineGradient != null) { 362 | final Rect lineRect = new Rect.fromLTWH(0.0, 0.0, width, height); 363 | paint.shader = lineGradient.createShader(lineRect); 364 | } 365 | 366 | if (fillMode != FillMode.none) { 367 | Path fillPath = new Path()..addPath(path, Offset.zero); 368 | if (fillMode == FillMode.below) { 369 | fillPath.relativeLineTo(lineWidth / 2, 0.0); 370 | fillPath.lineTo(size.width, size.height); 371 | fillPath.lineTo(0.0, size.height); 372 | fillPath.lineTo(startPoint.dx - lineWidth / 2, startPoint.dy); 373 | } else if (fillMode == FillMode.above) { 374 | fillPath.relativeLineTo(lineWidth / 2, 0.0); 375 | fillPath.lineTo(size.width, 0.0); 376 | fillPath.lineTo(0.0, 0.0); 377 | fillPath.lineTo(startPoint.dx - lineWidth / 2, startPoint.dy); 378 | } 379 | fillPath.close(); 380 | 381 | Paint fillPaint = new Paint() 382 | ..strokeWidth = 0.0 383 | ..color = fillColor 384 | ..style = PaintingStyle.fill; 385 | 386 | if (fillGradient != null) { 387 | final Rect fillRect = new Rect.fromLTWH(0.0, 0.0, width, height); 388 | fillPaint.shader = fillGradient.createShader(fillRect); 389 | } 390 | canvas.drawPath(fillPath, fillPaint); 391 | } 392 | 393 | canvas.drawPath(path, paint); 394 | 395 | if (points.isNotEmpty) { 396 | Paint pointsPaint = new Paint() 397 | ..strokeCap = StrokeCap.round 398 | ..strokeWidth = pointSize 399 | ..color = pointColor; 400 | canvas.drawPoints(ui.PointMode.points, points, pointsPaint); 401 | } 402 | } 403 | 404 | @override 405 | bool shouldRepaint(_SparklinePainter old) { 406 | return dataPoints != old.dataPoints || 407 | lineWidth != old.lineWidth || 408 | lineColor != old.lineColor || 409 | lineGradient != old.lineGradient || 410 | sharpCorners != old.sharpCorners || 411 | fillMode != old.fillMode || 412 | fillColor != old.fillColor || 413 | fillGradient != old.fillGradient || 414 | pointsMode != old.pointsMode || 415 | pointSize != old.pointSize || 416 | pointColor != old.pointColor || 417 | enableGridLines != old.enableGridLines || 418 | gridLineColor != old.gridLineColor || 419 | gridLineAmount != old.gridLineAmount || 420 | gridLineWidth != old.gridLineWidth || 421 | gridLineLabelColor != old.gridLineLabelColor; 422 | } 423 | } 424 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | analyzer: 5 | dependency: transitive 6 | description: 7 | name: analyzer 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "0.32.4" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.5.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.0.8" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.0.4" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.2" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.14.11" 46 | convert: 47 | dependency: transitive 48 | description: 49 | name: convert 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.0.2" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "2.0.6" 60 | csslib: 61 | dependency: transitive 62 | description: 63 | name: csslib 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.14.5" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_test: 73 | dependency: "direct dev" 74 | description: flutter 75 | source: sdk 76 | version: "0.0.0" 77 | front_end: 78 | dependency: transitive 79 | description: 80 | name: front_end 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "0.1.4" 84 | glob: 85 | dependency: transitive 86 | description: 87 | name: glob 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.1.7" 91 | html: 92 | dependency: transitive 93 | description: 94 | name: html 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.13.3+3" 98 | http: 99 | dependency: transitive 100 | description: 101 | name: http 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "0.11.3+17" 105 | http_multi_server: 106 | dependency: transitive 107 | description: 108 | name: http_multi_server 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "2.0.5" 112 | http_parser: 113 | dependency: transitive 114 | description: 115 | name: http_parser 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "3.1.3" 119 | io: 120 | dependency: transitive 121 | description: 122 | name: io 123 | url: "https://pub.dartlang.org" 124 | source: hosted 125 | version: "0.3.3" 126 | js: 127 | dependency: transitive 128 | description: 129 | name: js 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "0.6.1+1" 133 | json_rpc_2: 134 | dependency: transitive 135 | description: 136 | name: json_rpc_2 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "2.0.9" 140 | kernel: 141 | dependency: transitive 142 | description: 143 | name: kernel 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "0.3.4" 147 | logging: 148 | dependency: transitive 149 | description: 150 | name: logging 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "0.11.3+2" 154 | matcher: 155 | dependency: transitive 156 | description: 157 | name: matcher 158 | url: "https://pub.dartlang.org" 159 | source: hosted 160 | version: "0.12.3+1" 161 | meta: 162 | dependency: transitive 163 | description: 164 | name: meta 165 | url: "https://pub.dartlang.org" 166 | source: hosted 167 | version: "1.1.6" 168 | mime: 169 | dependency: transitive 170 | description: 171 | name: mime 172 | url: "https://pub.dartlang.org" 173 | source: hosted 174 | version: "0.9.6+2" 175 | multi_server_socket: 176 | dependency: transitive 177 | description: 178 | name: multi_server_socket 179 | url: "https://pub.dartlang.org" 180 | source: hosted 181 | version: "1.0.2" 182 | node_preamble: 183 | dependency: transitive 184 | description: 185 | name: node_preamble 186 | url: "https://pub.dartlang.org" 187 | source: hosted 188 | version: "1.4.4" 189 | package_config: 190 | dependency: transitive 191 | description: 192 | name: package_config 193 | url: "https://pub.dartlang.org" 194 | source: hosted 195 | version: "1.0.5" 196 | package_resolver: 197 | dependency: transitive 198 | description: 199 | name: package_resolver 200 | url: "https://pub.dartlang.org" 201 | source: hosted 202 | version: "1.0.4" 203 | path: 204 | dependency: transitive 205 | description: 206 | name: path 207 | url: "https://pub.dartlang.org" 208 | source: hosted 209 | version: "1.6.2" 210 | plugin: 211 | dependency: transitive 212 | description: 213 | name: plugin 214 | url: "https://pub.dartlang.org" 215 | source: hosted 216 | version: "0.2.0+3" 217 | pool: 218 | dependency: transitive 219 | description: 220 | name: pool 221 | url: "https://pub.dartlang.org" 222 | source: hosted 223 | version: "1.3.6" 224 | pub_semver: 225 | dependency: transitive 226 | description: 227 | name: pub_semver 228 | url: "https://pub.dartlang.org" 229 | source: hosted 230 | version: "1.4.2" 231 | quiver: 232 | dependency: transitive 233 | description: 234 | name: quiver 235 | url: "https://pub.dartlang.org" 236 | source: hosted 237 | version: "2.0.0+1" 238 | shelf: 239 | dependency: transitive 240 | description: 241 | name: shelf 242 | url: "https://pub.dartlang.org" 243 | source: hosted 244 | version: "0.7.3+3" 245 | shelf_packages_handler: 246 | dependency: transitive 247 | description: 248 | name: shelf_packages_handler 249 | url: "https://pub.dartlang.org" 250 | source: hosted 251 | version: "1.0.4" 252 | shelf_static: 253 | dependency: transitive 254 | description: 255 | name: shelf_static 256 | url: "https://pub.dartlang.org" 257 | source: hosted 258 | version: "0.2.8" 259 | shelf_web_socket: 260 | dependency: transitive 261 | description: 262 | name: shelf_web_socket 263 | url: "https://pub.dartlang.org" 264 | source: hosted 265 | version: "0.2.2+4" 266 | sky_engine: 267 | dependency: transitive 268 | description: flutter 269 | source: sdk 270 | version: "0.0.99" 271 | source_map_stack_trace: 272 | dependency: transitive 273 | description: 274 | name: source_map_stack_trace 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "1.1.5" 278 | source_maps: 279 | dependency: transitive 280 | description: 281 | name: source_maps 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "0.10.7" 285 | source_span: 286 | dependency: transitive 287 | description: 288 | name: source_span 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "1.4.1" 292 | stack_trace: 293 | dependency: transitive 294 | description: 295 | name: stack_trace 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "1.9.3" 299 | stream_channel: 300 | dependency: transitive 301 | description: 302 | name: stream_channel 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "1.6.8" 306 | string_scanner: 307 | dependency: transitive 308 | description: 309 | name: string_scanner 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "1.0.4" 313 | term_glyph: 314 | dependency: transitive 315 | description: 316 | name: term_glyph 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "1.0.1" 320 | test: 321 | dependency: transitive 322 | description: 323 | name: test 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "1.3.0" 327 | typed_data: 328 | dependency: transitive 329 | description: 330 | name: typed_data 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "1.1.6" 334 | utf: 335 | dependency: transitive 336 | description: 337 | name: utf 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "0.9.0+5" 341 | vector_math: 342 | dependency: transitive 343 | description: 344 | name: vector_math 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "2.0.8" 348 | vm_service_client: 349 | dependency: transitive 350 | description: 351 | name: vm_service_client 352 | url: "https://pub.dartlang.org" 353 | source: hosted 354 | version: "0.2.6" 355 | watcher: 356 | dependency: transitive 357 | description: 358 | name: watcher 359 | url: "https://pub.dartlang.org" 360 | source: hosted 361 | version: "0.9.7+10" 362 | web_socket_channel: 363 | dependency: transitive 364 | description: 365 | name: web_socket_channel 366 | url: "https://pub.dartlang.org" 367 | source: hosted 368 | version: "1.0.9" 369 | yaml: 370 | dependency: transitive 371 | description: 372 | name: yaml 373 | url: "https://pub.dartlang.org" 374 | source: hosted 375 | version: "2.1.15" 376 | sdks: 377 | dart: ">=2.0.0-dev.68.0 <3.0.0" 378 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_sparkline 2 | description: Beautiful sparkline charts for Flutter. 3 | version: 0.1.0 4 | author: Victor Choueiri 5 | homepage: https://github.com/xqwzts/flutter_sparkline 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | 11 | dev_dependencies: 12 | flutter_test: 13 | sdk: flutter 14 | 15 | environment: 16 | sdk: ">=2.0.0-dev.58.0 <3.0.0" 17 | -------------------------------------------------------------------------------- /screenshots/example_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_base.png -------------------------------------------------------------------------------- /screenshots/example_fill_above.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_fill_above.png -------------------------------------------------------------------------------- /screenshots/example_fill_below.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_fill_below.png -------------------------------------------------------------------------------- /screenshots/example_fill_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_fill_gradient.png -------------------------------------------------------------------------------- /screenshots/example_line_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_line_gradient.png -------------------------------------------------------------------------------- /screenshots/example_lineopts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_lineopts.png -------------------------------------------------------------------------------- /screenshots/example_points_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_points_all.png -------------------------------------------------------------------------------- /screenshots/example_points_last.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/example_points_last.png -------------------------------------------------------------------------------- /screenshots/sparkline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/sparkline.png -------------------------------------------------------------------------------- /screenshots/sparkline_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/screenshots/sparkline_.png -------------------------------------------------------------------------------- /test/flutter_sparkline_test.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xqwzts/flutter_sparkline/208e3750154aef4a5fb21aa44a6c8aafc1f9f7d7/test/flutter_sparkline_test.dart --------------------------------------------------------------------------------