├── .vscode └── settings.json ├── lib ├── src │ ├── chart │ │ ├── chart_behavior │ │ │ ├── chart_behavior.dart │ │ │ ├── zoom_behavior.dart │ │ │ └── selection_behavior.dart │ │ ├── chart_segment │ │ │ ├── chart_segment.dart │ │ │ ├── scatter_segment.dart │ │ │ ├── spline_segment.dart │ │ │ ├── fastline_segment.dart │ │ │ ├── line_segment.dart │ │ │ ├── stacked_line_segment.dart │ │ │ ├── bubble_segment.dart │ │ │ ├── stepline_segment.dart │ │ │ ├── range_column_segment.dart │ │ │ ├── stacked_bar_segment.dart │ │ │ ├── stacked_column_segment.dart │ │ │ ├── area_segment.dart │ │ │ └── stacked_area_segment.dart │ │ ├── series_painter │ │ │ ├── scatter_painter.dart │ │ │ ├── stepline_painter.dart │ │ │ ├── bar_painter.dart │ │ │ ├── bubble_painter.dart │ │ │ ├── column_painter.dart │ │ │ ├── range_column_painter.dart │ │ │ ├── stacked_column_painter.dart │ │ │ ├── stacked_bar_painter.dart │ │ │ ├── stacked_line_painter.dart │ │ │ ├── fastline_painter.dart │ │ │ ├── line_painter.dart │ │ │ ├── stacked_area_painter.dart │ │ │ ├── area_painter.dart │ │ │ └── spline_painter.dart │ │ ├── chart_series │ │ │ ├── fastline_series.dart │ │ │ ├── stacked_bar_series.dart │ │ │ ├── stacked_column_series.dart │ │ │ ├── area_series.dart │ │ │ ├── stacked_area_series.dart │ │ │ ├── scatter_series.dart │ │ │ ├── stepline_series.dart │ │ │ ├── line_series.dart │ │ │ ├── xy_data_series.dart │ │ │ └── stacked_line_series.dart │ │ ├── user_interaction │ │ │ └── tooltip_template.dart │ │ ├── common │ │ │ └── marker.dart │ │ └── themes │ │ │ └── chart_theme.dart │ ├── pyramid_chart │ │ └── utils │ │ │ ├── common.dart │ │ │ └── helper.dart │ ├── circular_chart │ │ ├── utils │ │ │ └── enum.dart │ │ └── renderer │ │ │ ├── pie_series.dart │ │ │ └── doughnut_series.dart │ └── common │ │ ├── utils │ │ └── enum.dart │ │ └── event_args.dart └── charts.dart ├── pubspec.yaml ├── syncfusion_flutter_charts.iml ├── LICENSE ├── .packages ├── example └── main.dart ├── pubspec.lock └── CHANGELOG.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /lib/src/chart/chart_behavior/chart_behavior.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Holds the gestures for chart. 4 | abstract class ChartBehavior { 5 | void onTouchDown(double xPos, double yPos); 6 | 7 | void onTouchUp(double xPos, double yPos); 8 | 9 | void onDoubleTap(double xPos, double yPos); 10 | 11 | void onTouchMove(double xPos, double yPos); 12 | 13 | void onLongPress(double xPos, double yPos); 14 | 15 | void onPaint(Canvas canvas); 16 | } 17 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: syncfusion_flutter_charts 2 | description: Syncfusion Flutter Charts is a data visualization library written natively in Dart for creating beautiful and high-performance cartesian and circular charts. 3 | version: 17.3.14+1 4 | author: Syncfusion 5 | homepage: https://github.com/syncfusion/flutter-examples 6 | 7 | environment: 8 | sdk: ">=2.1.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | syncfusion_flutter_core: ^17.3.14 14 | intl: ^0.16.0 15 | vector_math: ^2.0.8 16 | -------------------------------------------------------------------------------- /lib/src/pyramid_chart/utils/common.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _PointInfo { 4 | _PointInfo(this.x, this.y); 5 | dynamic x; 6 | num y; 7 | String text; 8 | Color fill; 9 | Color color; 10 | Color borderColor; 11 | D sortValue; 12 | num borderWidth; 13 | bool isExplode; 14 | bool isShadow; 15 | bool isEmpty = false; 16 | bool isVisible = true; 17 | bool isSelected = false; 18 | Position dataLabelPosition; 19 | ChartDataLabelPosition renderPosition; 20 | Rect labelRect; 21 | Size dataLabelSize = const Size(0, 0); 22 | bool saturationRegionOutside = false; 23 | num yRatio; 24 | num heightRatio; 25 | List pathRegion; 26 | Rect region; 27 | Offset symbolLocation; 28 | num explodeDistance; 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/chart/chart_behavior/zoom_behavior.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Holds the zooming gestures. 4 | abstract class ZoomBehavior { 5 | void onPan(double xPos, double yPos); 6 | 7 | void onDoubleTap(double xPos, double yPos, double zoomFactor); 8 | 9 | void onPaint(Canvas canvas); 10 | 11 | void onDrawSelectionZoomRect( 12 | double currentX, double currentY, double startX, double startY); 13 | 14 | void onPinchStart(ChartAxis axis, double firstX, double firstY, 15 | double secondX, double secondY, double scaleFactor); 16 | 17 | void onPinchEnd(ChartAxis axis, double firstX, double firstY, double secondX, 18 | double secondY, double scaleFactor); 19 | 20 | void onPinch(ChartAxis axis, double position, double scaleFactor); 21 | } 22 | -------------------------------------------------------------------------------- /syncfusion_flutter_charts.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Syncfusion License 2 | 3 | Syncfusion Flutter Chart package is available under the Syncfusion Essential Studio program, and can be licensed either under the Syncfusion Community License Program or the Syncfusion commercial license. 4 | 5 | To be qualified for the Syncfusion Community License Program you must have a gross revenue of less than one (1) million U.S. dollars ($1,000,000.00 USD) per year and have less than five (5) developers in your organization, and agree to be bound by Syncfusion’s terms and conditions. 6 | 7 | Customers who do not qualify for the community license can contact sales@syncfusion.com for commercial licensing options. 8 | 9 | Under no circumstances can you use this product without (1) either a Community License or a commercial license and (2) without agreeing and abiding by Syncfusion’s license containing all terms and conditions. 10 | 11 | The Syncfusion license that contains the terms and conditions can be found at 12 | https://www.syncfusion.com/content/downloads/syncfusion_license.pdf -------------------------------------------------------------------------------- /.packages: -------------------------------------------------------------------------------- 1 | # Generated by pub on 2019-10-30 14:25:44.805394. 2 | collection:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib/ 3 | flutter:file:///Users/a/fvm/dev/packages/flutter/lib/ 4 | intl:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/intl-0.16.0/lib/ 5 | meta:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/meta-1.1.7/lib/ 6 | path:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/path-1.6.4/lib/ 7 | sky_engine:file:///Users/a/fvm/dev/bin/cache/pkg/sky_engine/lib/ 8 | syncfusion_flutter_core:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/syncfusion_flutter_core-17.3.14/lib/ 9 | typed_data:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib/ 10 | url_launcher:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.2.5/lib/ 11 | url_launcher_platform_interface:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/url_launcher_platform_interface-1.0.1/lib/ 12 | utf:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib/ 13 | vector_math:file:///Users/a/.pub-cache/hosted/pub.dartlang.org/vector_math-2.0.8/lib/ 14 | syncfusion_flutter_charts:lib/ 15 | -------------------------------------------------------------------------------- /lib/src/circular_chart/utils/enum.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Data points grouping mode. 4 | /// - CircularChartGroupMode.point, groups the points based on length. 5 | /// - CircularChartGroupMode.value, groups the points based on the y value. 6 | enum CircularChartGroupMode { point, value } 7 | 8 | /// Data label position of range bar series. 9 | /// - Position.left, places the data label to the left side. 10 | /// - Position.right, places the data label to the right side. 11 | enum Position { left, right } 12 | 13 | /// Data labels intersect action. 14 | /// - LabelIntersectAction.hide, hides the intersecting labels. 15 | /// - LabelIntersectAction.none, will not perform any action on intersection. 16 | enum LabelIntersectAction { hide, none } 17 | 18 | /// Type of connector line. 19 | /// - ConnectorType.curve, will render the data label connector line curvely. 20 | /// - ConnectorType.line, will render the data label connector lien straightly. 21 | enum ConnectorType { curve, line } 22 | 23 | /// Corner style of range bar series. 24 | /// - CornerStyle.bothFlat, will render both the corners flatly. 25 | /// - CornerStyle.bothCurve, will render both the corners curvely. 26 | /// - CornerStyle.startCurve, will render starting corner curvely. 27 | /// - CornerStyle.endCurve, will render ending corner curvely. 28 | enum CornerStyle { bothFlat, bothCurve, startCurve, endCurve } 29 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/chart_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for chart series. 4 | abstract class ChartSegment { 5 | ///Gets the color of the series 6 | Paint getFillPaint(); 7 | 8 | ///Gets the border color of the series 9 | Paint getStrokePaint(); 10 | 11 | ///Calculates the rendering bounds of a segment 12 | void calculateSegmentPoints(); 13 | 14 | ///Draws segment in series bounds. 15 | void onPaint(Canvas canvas); 16 | 17 | ///Color of the segment 18 | Color color; 19 | 20 | ///Border color of the segment 21 | Color strokeColor; 22 | 23 | ///Border width of the segment 24 | double strokeWidth; 25 | 26 | ///Fill paint of the segment 27 | Paint fillPaint; 28 | 29 | ///Stroke paint of the segment 30 | Paint strokePaint; 31 | 32 | ///Chart series 33 | XyDataSeries series; 34 | 35 | ///Chart series 36 | XyDataSeries oldSeries; 37 | 38 | ///Animation factor value 39 | double animationFactor; 40 | 41 | ///Rectangle of the segment 42 | RRect segmentRect; 43 | 44 | Paint _defaultFillColor; 45 | 46 | Paint _defaultStrokeColor; 47 | 48 | int currentSegmentIndex, _seriesIndex; 49 | 50 | _CartesianChartPoint _currentPoint; 51 | 52 | _CartesianChartPoint _point; 53 | 54 | _CartesianChartPoint _oldPoint; 55 | 56 | bool _oldSeriesVisible; 57 | 58 | Rect _oldRegion; 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/chart/chart_behavior/selection_behavior.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Customizes the selection in chart. 4 | abstract class ChartSelectionBehavior { 5 | void onTouchDown(double xPos, double yPos); 6 | 7 | void onDoubleTap(double xPos, double yPos); 8 | 9 | void onLongPress(double xPos, double yPos); 10 | 11 | void onCircularTouchDown(int pointIndex, int seriesIndex); 12 | 13 | void onCircularDoubleTap(int pointIndex, int seriesIndex); 14 | 15 | void onCircularLongPress(int pointIndex, int seriesIndex); 16 | 17 | Paint getSelectedItemFill(Paint paint, int seriesIndex, int pointIndex, 18 | List selectedSegments); 19 | 20 | Paint getUnselectedItemFill(Paint paint, int seriesIndex, int pointIndex, 21 | List unselectedSegments); 22 | 23 | Paint getSelectedItemBorder(Paint paint, int seriesIndex, int pointIndex, 24 | List selectedSegments); 25 | 26 | Paint getUnselectedItemBorder(Paint paint, int seriesIndex, int pointIndex, 27 | List unselectedSegments); 28 | 29 | Color getCircularSelectedItemFill(Color color, int seriesIndex, 30 | int pointIndex, List<_Region> selectedRegions); 31 | 32 | Color getCircularUnSelectedItemFill(Color color, int seriesIndex, 33 | int pointIndex, List<_Region> unselectedRegions); 34 | 35 | Color getCircularSelectedItemBorder(Color color, int seriesIndex, 36 | int pointIndex, List<_Region> selectedRegions); 37 | 38 | Color getCircularUnSelectedItemBorder(Color color, int seriesIndex, 39 | int pointIndex, List<_Region> unselectedRegions); 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/scatter_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _ScatterChartPainter extends CustomPainter { 4 | _ScatterChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final Animation animationController; 16 | final Animation seriesAnimation; 17 | final Animation chartElementAnimation; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | if (series._visible) { 26 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 27 | 28 | /// Clip rect will be added for series. 29 | final Rect axisClipRect = _calculatePlotOffset( 30 | chart._chartAxis._axisClipRect, 31 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 32 | canvas.clipRect(axisClipRect); 33 | series._draw(canvas, animationFactor); 34 | clipRect = _calculatePlotOffset( 35 | Rect.fromLTRB( 36 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 37 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 38 | chart._chartAxis._axisClipRect.right + 39 | series.markerSettings.width, 40 | chart._chartAxis._axisClipRect.bottom + 41 | series.markerSettings.height), 42 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 43 | } 44 | canvas.restore(); 45 | 46 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 47 | canvas.clipRect(clipRect); 48 | 49 | ///Draw marker and other elements for scatter series 50 | _renderSeriesElements(canvas, series, chartElementAnimation); 51 | } 52 | } 53 | 54 | @override 55 | bool shouldRepaint(_ScatterChartPainter oldDelegate) => isRepaint; 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/stepline_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _StepLineChartPainter extends CustomPainter { 4 | _StepLineChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final Animation animationController; 16 | final Animation seriesAnimation; 17 | final Animation chartElementAnimation; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | if (series._visible) { 26 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 27 | 28 | /// Clip rect will be added for series. 29 | final Rect axisClipRect = _calculatePlotOffset( 30 | chart._chartAxis._axisClipRect, 31 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 32 | canvas.clipRect(axisClipRect); 33 | series._draw(canvas, animationFactor); 34 | clipRect = _calculatePlotOffset( 35 | Rect.fromLTRB( 36 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 37 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 38 | chart._chartAxis._axisClipRect.right + 39 | series.markerSettings.width, 40 | chart._chartAxis._axisClipRect.bottom + 41 | series.markerSettings.height), 42 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 43 | } 44 | canvas.restore(); 45 | 46 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 47 | canvas.clipRect(clipRect); 48 | 49 | ///Draw marker and other elements for stepline series 50 | _renderSeriesElements(canvas, series, chartElementAnimation); 51 | } 52 | } 53 | 54 | @override 55 | bool shouldRepaint(_StepLineChartPainter oldDelegate) => isRepaint; 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/bar_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _BarChartPainter extends CustomPainter { 4 | _BarChartPainter( 5 | {this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier}) 12 | : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final Animation animationController; 16 | final Animation seriesAnimation; 17 | final Animation chartElementAnimation; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | 26 | /// Clip rect added 27 | if (series._visible) { 28 | final Rect axisClipRect = _calculatePlotOffset( 29 | chart._chartAxis._axisClipRect, 30 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 31 | canvas.clipRect(axisClipRect); 32 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 33 | 34 | ///Draw bar series 35 | series._draw(canvas, animationFactor); 36 | clipRect = _calculatePlotOffset( 37 | Rect.fromLTRB( 38 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 39 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 40 | chart._chartAxis._axisClipRect.right + 41 | series.markerSettings.width, 42 | chart._chartAxis._axisClipRect.bottom + 43 | series.markerSettings.height), 44 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 45 | } 46 | canvas.restore(); 47 | 48 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 49 | canvas.clipRect(clipRect); 50 | 51 | ///Draw marker and other elements for bar series 52 | _renderSeriesElements(canvas, series, chartElementAnimation); 53 | } 54 | } 55 | 56 | @override 57 | bool shouldRepaint(_BarChartPainter oldDelegate) => isRepaint; 58 | } 59 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/bubble_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _BubbleChartPainter extends CustomPainter { 4 | _BubbleChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | 14 | final SfCartesianChart chart; 15 | final bool isRepaint; 16 | final Animation animationController; 17 | final Animation seriesAnimation; 18 | final Animation chartElementAnimation; 19 | final XyDataSeries series; 20 | 21 | @override 22 | void paint(Canvas canvas, Size size) { 23 | canvas.save(); 24 | Rect clipRect; 25 | double animationFactor; 26 | if (series._visible) { 27 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 28 | 29 | /// Clip rect will be added for series. 30 | final Rect axisClipRect = _calculatePlotOffset( 31 | chart._chartAxis._axisClipRect, 32 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 33 | canvas.clipRect(axisClipRect); 34 | 35 | ///Draw bubble series 36 | series._draw(canvas, animationFactor); 37 | clipRect = _calculatePlotOffset( 38 | Rect.fromLTRB( 39 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 40 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 41 | chart._chartAxis._axisClipRect.right + 42 | series.markerSettings.width, 43 | chart._chartAxis._axisClipRect.bottom + 44 | series.markerSettings.height), 45 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 46 | } 47 | canvas.restore(); 48 | 49 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 50 | canvas.clipRect(clipRect); 51 | 52 | ///Draw marker and other elements for bubble series 53 | _renderSeriesElements(canvas, series, chartElementAnimation); 54 | } 55 | } 56 | 57 | @override 58 | bool shouldRepaint(_BubbleChartPainter oldDelegate) => isRepaint; 59 | } 60 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/column_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _ColumnChartPainter extends CustomPainter { 4 | _ColumnChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | 14 | final SfCartesianChart chart; 15 | final bool isRepaint; 16 | final AnimationController animationController; 17 | final Animation seriesAnimation; 18 | final Animation chartElementAnimation; 19 | List<_ChartLocation> currentChartLocations = <_ChartLocation>[]; 20 | XyDataSeries series; 21 | 22 | @override 23 | void paint(Canvas canvas, Size size) { 24 | canvas.save(); 25 | Rect clipRect; 26 | double animationFactor; 27 | 28 | /// Clip rect added 29 | if (series._visible) { 30 | final Rect axisClipRect = _calculatePlotOffset( 31 | chart._chartAxis._axisClipRect, 32 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 33 | canvas.clipRect(axisClipRect); 34 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 35 | 36 | ///Draw Column series 37 | series._draw(canvas, animationFactor); 38 | clipRect = _calculatePlotOffset( 39 | Rect.fromLTRB( 40 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 41 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 42 | chart._chartAxis._axisClipRect.right + 43 | series.markerSettings.width, 44 | chart._chartAxis._axisClipRect.bottom + 45 | series.markerSettings.height), 46 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 47 | } 48 | canvas.restore(); 49 | 50 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 51 | canvas.clipRect(clipRect); 52 | 53 | ///Draw marker and other elements for column series 54 | _renderSeriesElements(canvas, series, chartElementAnimation); 55 | } 56 | } 57 | 58 | @override 59 | bool shouldRepaint(_ColumnChartPainter oldDelegate) => isRepaint; 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/range_column_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _RangeColumnChartPainter extends CustomPainter { 4 | _RangeColumnChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | 14 | final SfCartesianChart chart; 15 | final bool isRepaint; 16 | final AnimationController animationController; 17 | final Animation seriesAnimation; 18 | final Animation chartElementAnimation; 19 | List<_ChartLocation> currentChartLocations = <_ChartLocation>[]; 20 | XyDataSeries series; 21 | 22 | @override 23 | void paint(Canvas canvas, Size size) { 24 | canvas.save(); 25 | Rect clipRect; 26 | double animationFactor; 27 | 28 | /// Clip rect added 29 | if (series._visible) { 30 | final Rect axisClipRect = _calculatePlotOffset( 31 | chart._chartAxis._axisClipRect, 32 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 33 | canvas.clipRect(axisClipRect); 34 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 35 | 36 | ///Draw Range column series 37 | series._draw(canvas, animationFactor); 38 | clipRect = _calculatePlotOffset( 39 | Rect.fromLTWH( 40 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 41 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 42 | chart._chartAxis._axisClipRect.right + 43 | series.markerSettings.width, 44 | chart._chartAxis._axisClipRect.bottom + 45 | series.markerSettings.height), 46 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 47 | } 48 | canvas.restore(); 49 | 50 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 51 | canvas.clipRect(clipRect); 52 | 53 | ///Draw marker and other elements for range column series 54 | _renderSeriesElements(canvas, series, chartElementAnimation); 55 | } 56 | } 57 | 58 | @override 59 | bool shouldRepaint(_RangeColumnChartPainter oldDelegate) => isRepaint; 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/stacked_column_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _StackedColummnChartPainter extends CustomPainter { 4 | _StackedColummnChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | 14 | final SfCartesianChart chart; 15 | _CartesianChartPoint point; 16 | final bool isRepaint; 17 | final AnimationController animationController; 18 | final Animation seriesAnimation; 19 | final Animation chartElementAnimation; 20 | List<_ChartLocation> currentChartLocations = <_ChartLocation>[]; 21 | StackedColumnSeries series; 22 | 23 | @override 24 | void paint(Canvas canvas, Size size) { 25 | canvas.save(); 26 | Rect clipRect; 27 | double animationFactor; 28 | if (series._visible) { 29 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 30 | 31 | /// Clip rect will be added for series. 32 | final Rect axisClipRect = _calculatePlotOffset( 33 | chart._chartAxis._axisClipRect, 34 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 35 | canvas.clipRect(axisClipRect); 36 | series._draw(canvas, animationFactor); 37 | clipRect = _calculatePlotOffset( 38 | Rect.fromLTRB( 39 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 40 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 41 | chart._chartAxis._axisClipRect.right + 42 | series.markerSettings.width, 43 | chart._chartAxis._axisClipRect.bottom + 44 | series.markerSettings.height), 45 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 46 | } 47 | canvas.restore(); 48 | 49 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 50 | canvas.clipRect(clipRect); 51 | 52 | ///Draw marker and other elements for stacked column series 53 | _renderSeriesElements(canvas, series, chartElementAnimation); 54 | } 55 | } 56 | 57 | @override 58 | bool shouldRepaint(_StackedColummnChartPainter oldDelegate) => isRepaint; 59 | } 60 | -------------------------------------------------------------------------------- /example/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:syncfusion_flutter_charts/charts.dart'; 3 | import 'package:syncfusion_flutter_core/core.dart'; 4 | 5 | void main() { 6 | // Register your license here 7 | SyncfusionLicense.registerLicense(null); 8 | return runApp(ChartApp()); 9 | } 10 | 11 | class ChartApp extends StatelessWidget { 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | title: 'Chart Demo', 16 | theme: ThemeData( 17 | primarySwatch: Colors.blue, 18 | ), 19 | home: MyHomePage(), 20 | ); 21 | } 22 | } 23 | 24 | class MyHomePage extends StatefulWidget { 25 | // ignore: prefer_const_constructors_in_immutables 26 | MyHomePage({Key key}) : super(key: key); 27 | 28 | @override 29 | _MyHomePageState createState() => _MyHomePageState(); 30 | } 31 | 32 | class _MyHomePageState extends State { 33 | @override 34 | Widget build(BuildContext context) { 35 | return Scaffold( 36 | appBar: AppBar( 37 | title: const Text('Syncfusion Flutter chart'), 38 | ), 39 | body: SfCartesianChart( 40 | primaryXAxis: CategoryAxis(), 41 | // Chart title 42 | title: ChartTitle(text: 'Half yearly sales analysis'), 43 | // Enable legend 44 | legend: Legend(isVisible: true), 45 | // Enable tooltip 46 | tooltipBehavior: TooltipBehavior(enable: true), 47 | series: >[ 48 | LineSeries( 49 | dataSource: [ 50 | SalesData('Jan', 35), 51 | SalesData('Feb', 28), 52 | SalesData('Mar', 34), 53 | SalesData('Apr', 32), 54 | SalesData('May', 40) 55 | ], 56 | xValueMapper: (SalesData sales, _) => sales.year, 57 | yValueMapper: (SalesData sales, _) => sales.sales, 58 | // Enable data label 59 | dataLabelSettings: DataLabelSettings(isVisible: true) 60 | ) 61 | ] 62 | ) 63 | ); 64 | } 65 | } 66 | 67 | class SalesData { 68 | SalesData(this.year, this.sales); 69 | 70 | final String year; 71 | final double sales; 72 | } -------------------------------------------------------------------------------- /lib/src/chart/series_painter/stacked_bar_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _StackedBarChartPainter extends CustomPainter { 4 | _StackedBarChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | 14 | final SfCartesianChart chart; 15 | _CartesianChartPoint point; 16 | final bool isRepaint; 17 | final AnimationController animationController; 18 | final Animation seriesAnimation; 19 | final Animation chartElementAnimation; 20 | List<_ChartLocation> currentChartLocations = <_ChartLocation>[]; 21 | StackedBarSeries series; 22 | static double animation; 23 | 24 | @override 25 | void paint(Canvas canvas, Size size) { 26 | canvas.save(); 27 | Rect clipRect; 28 | double animationFactor; 29 | if (series._visible) { 30 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 31 | 32 | /// Clip rect will be added for series. 33 | final Rect axisClipRect = _calculatePlotOffset( 34 | chart._chartAxis._axisClipRect, 35 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 36 | canvas.clipRect(axisClipRect); 37 | series._draw(canvas, animationFactor); 38 | clipRect = _calculatePlotOffset( 39 | Rect.fromLTRB( 40 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 41 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 42 | chart._chartAxis._axisClipRect.right + 43 | series.markerSettings.width, 44 | chart._chartAxis._axisClipRect.bottom + 45 | series.markerSettings.height), 46 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 47 | } 48 | canvas.restore(); 49 | 50 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 51 | canvas.clipRect(clipRect); 52 | 53 | ///Draw marker and other elements for stacked bar series 54 | _renderSeriesElements(canvas, series, chartElementAnimation); 55 | } 56 | } 57 | 58 | @override 59 | bool shouldRepaint(_StackedBarChartPainter oldDelegate) => isRepaint; 60 | } 61 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | collection: 5 | dependency: transitive 6 | description: 7 | name: collection 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "1.14.11" 11 | flutter: 12 | dependency: "direct main" 13 | description: flutter 14 | source: sdk 15 | version: "0.0.0" 16 | intl: 17 | dependency: "direct main" 18 | description: 19 | name: intl 20 | url: "https://pub.dartlang.org" 21 | source: hosted 22 | version: "0.16.0" 23 | meta: 24 | dependency: transitive 25 | description: 26 | name: meta 27 | url: "https://pub.dartlang.org" 28 | source: hosted 29 | version: "1.1.7" 30 | path: 31 | dependency: transitive 32 | description: 33 | name: path 34 | url: "https://pub.dartlang.org" 35 | source: hosted 36 | version: "1.6.4" 37 | sky_engine: 38 | dependency: transitive 39 | description: flutter 40 | source: sdk 41 | version: "0.0.99" 42 | syncfusion_flutter_core: 43 | dependency: "direct main" 44 | description: 45 | name: syncfusion_flutter_core 46 | url: "https://pub.dartlang.org" 47 | source: hosted 48 | version: "17.3.14" 49 | typed_data: 50 | dependency: transitive 51 | description: 52 | name: typed_data 53 | url: "https://pub.dartlang.org" 54 | source: hosted 55 | version: "1.1.6" 56 | url_launcher: 57 | dependency: transitive 58 | description: 59 | name: url_launcher 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "5.2.5" 63 | url_launcher_platform_interface: 64 | dependency: transitive 65 | description: 66 | name: url_launcher_platform_interface 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "1.0.1" 70 | utf: 71 | dependency: transitive 72 | description: 73 | name: utf 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "0.9.0+5" 77 | vector_math: 78 | dependency: "direct main" 79 | description: 80 | name: vector_math 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "2.0.8" 84 | sdks: 85 | dart: ">=2.2.2 <3.0.0" 86 | flutter: ">=1.9.1+hotfix.4 <2.0.0" 87 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/stacked_line_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _StackedLineChartPainter extends CustomPainter { 4 | _StackedLineChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final AnimationController animationController; 16 | final Animation seriesAnimation; 17 | final Animation chartElementAnimation; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | if (series._visible) { 26 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 27 | 28 | /// Clip rect will be added for series. 29 | final Rect axisClipRect = _calculatePlotOffset( 30 | chart._chartAxis._axisClipRect, 31 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 32 | canvas.clipRect(axisClipRect); 33 | if (series.animationDuration > 0) { 34 | _performLinearAnimation(series, canvas, animationFactor); 35 | } 36 | 37 | ///Draw line for line series 38 | series._draw(canvas, animationFactor); 39 | clipRect = _calculatePlotOffset( 40 | Rect.fromLTRB( 41 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 42 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 43 | chart._chartAxis._axisClipRect.right + 44 | series.markerSettings.width, 45 | chart._chartAxis._axisClipRect.bottom + 46 | series.markerSettings.height), 47 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 48 | } 49 | canvas.restore(); 50 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 51 | canvas.clipRect(clipRect); 52 | 53 | ///Draw marker and other elements for stacked line series 54 | _renderSeriesElements(canvas, series, chartElementAnimation); 55 | } 56 | } 57 | 58 | @override 59 | bool shouldRepaint(_StackedLineChartPainter oldDelegate) => isRepaint; 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/fastline_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _FastLineChartPainter extends CustomPainter { 4 | _FastLineChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | 14 | final SfCartesianChart chart; 15 | final bool isRepaint; 16 | final AnimationController animationController; 17 | final Animation seriesAnimation; 18 | final Animation chartElementAnimation; 19 | final XyDataSeries series; 20 | Path path; 21 | Paint pathPaint; 22 | 23 | @override 24 | void paint(Canvas canvas, Size size) { 25 | canvas.save(); 26 | Rect clipRect; 27 | double animationFactor; 28 | if (series._visible) { 29 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 30 | 31 | /// Clip rect will be added for series. 32 | final Rect axisClipRect = _calculatePlotOffset( 33 | chart._chartAxis._axisClipRect, 34 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 35 | canvas.clipRect(axisClipRect); 36 | if (series.animationDuration > 0) { 37 | _performLinearAnimation(series, canvas, animationFactor); 38 | } 39 | 40 | /// Draw fast line series 41 | series._draw(canvas); 42 | clipRect = _calculatePlotOffset( 43 | Rect.fromLTRB( 44 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 45 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 46 | chart._chartAxis._axisClipRect.right + 47 | series.markerSettings.width, 48 | chart._chartAxis._axisClipRect.bottom + 49 | series.markerSettings.height), 50 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 51 | } 52 | canvas.restore(); 53 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 54 | canvas.clipRect(clipRect); 55 | 56 | /// Draw marker and other elements for fastline series 57 | _renderSeriesElements(canvas, series, chartElementAnimation); 58 | } 59 | } 60 | 61 | @override 62 | bool shouldRepaint(_FastLineChartPainter oldDelegate) => isRepaint; 63 | } 64 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/line_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _LineChartPainter extends CustomPainter { 4 | _LineChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final AnimationController animationController; 16 | final Animation seriesAnimation; 17 | final Animation chartElementAnimation; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | if (series._visible) { 26 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 27 | 28 | /// Clip rect will be added for series. 29 | final Rect axisClipRect = _calculatePlotOffset( 30 | chart._chartAxis._axisClipRect, 31 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 32 | canvas.clipRect(axisClipRect); 33 | if (!series._chart._chartState.widgetNeedUpdate && 34 | series.animationDuration > 0) { 35 | _performLinearAnimation(series, canvas, animationFactor); 36 | } 37 | 38 | ///Draw line for line series 39 | series._draw(canvas, animationFactor); 40 | clipRect = _calculatePlotOffset( 41 | Rect.fromLTRB( 42 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 43 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 44 | chart._chartAxis._axisClipRect.right + 45 | series.markerSettings.width, 46 | chart._chartAxis._axisClipRect.bottom + 47 | series.markerSettings.height), 48 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 49 | } 50 | canvas.restore(); 51 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 52 | ///Draw marker and other elements for line series 53 | canvas.clipRect(clipRect); 54 | _renderSeriesElements(canvas, series, chartElementAnimation); 55 | } 56 | } 57 | 58 | @override 59 | bool shouldRepaint(_LineChartPainter oldDelegate) => isRepaint; 60 | } 61 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/stacked_area_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _StackedAreaChartPainter extends CustomPainter { 4 | _StackedAreaChartPainter( 5 | {this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier}) 12 | : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final Animation seriesAnimation; 16 | final Animation chartElementAnimation; 17 | final Animation animationController; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | 26 | /// Clip rect will be added for series. 27 | if (series._visible) { 28 | final Rect axisClipRect = _calculatePlotOffset( 29 | chart._chartAxis._axisClipRect, 30 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 31 | canvas.clipRect(axisClipRect); 32 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 33 | if (series.animationDuration > 0) { 34 | _performLinearAnimation(series, canvas, animationFactor); 35 | } 36 | 37 | /// Draw the area series. 38 | series._draw(canvas, animationFactor); 39 | clipRect = _calculatePlotOffset( 40 | Rect.fromLTRB( 41 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 42 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 43 | chart._chartAxis._axisClipRect.right + 44 | series.markerSettings.width, 45 | chart._chartAxis._axisClipRect.bottom + 46 | series.markerSettings.height), 47 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 48 | } 49 | canvas.restore(); 50 | 51 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 52 | canvas.clipRect(clipRect); 53 | 54 | ///Draw marker and other elements for stacked area series 55 | _renderSeriesElements(canvas, series, chartElementAnimation); 56 | } 57 | } 58 | 59 | @override 60 | bool shouldRepaint(_StackedAreaChartPainter oldDelegate) => isRepaint; 61 | } 62 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/area_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _AreaChartPainter extends CustomPainter { 4 | _AreaChartPainter( 5 | {this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier}) 12 | : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final Animation seriesAnimation; 16 | final Animation chartElementAnimation; 17 | final Animation animationController; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | 26 | /// Clip rect will be added for series. 27 | if (series._visible) { 28 | final Rect axisClipRect = _calculatePlotOffset( 29 | chart._chartAxis._axisClipRect, 30 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 31 | canvas.clipRect(axisClipRect); 32 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 33 | if (!series._chart._chartState.widgetNeedUpdate && 34 | series.animationDuration > 0) { 35 | _performLinearAnimation(series, canvas, animationFactor); 36 | } 37 | 38 | /// Draw the area series. 39 | series._draw(canvas, animationFactor); 40 | clipRect = _calculatePlotOffset( 41 | Rect.fromLTRB( 42 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 43 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 44 | chart._chartAxis._axisClipRect.right + 45 | series.markerSettings.width, 46 | chart._chartAxis._axisClipRect.bottom + 47 | series.markerSettings.height), 48 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 49 | } 50 | canvas.restore(); 51 | 52 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 53 | canvas.clipRect(clipRect); 54 | 55 | ///Draw marker and other elements for area series 56 | _renderSeriesElements(canvas, series, chartElementAnimation); 57 | } 58 | } 59 | 60 | @override 61 | bool shouldRepaint(_AreaChartPainter oldDelegate) => isRepaint; 62 | } 63 | -------------------------------------------------------------------------------- /lib/src/chart/series_painter/spline_painter.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _SplineChartPainter extends CustomPainter { 4 | _SplineChartPainter({ 5 | this.chart, 6 | this.series, 7 | this.isRepaint, 8 | this.animationController, 9 | this.seriesAnimation, 10 | this.chartElementAnimation, 11 | ValueNotifier notifier, 12 | }) : super(repaint: notifier); 13 | final SfCartesianChart chart; 14 | final bool isRepaint; 15 | final AnimationController animationController; 16 | final Animation seriesAnimation; 17 | final Animation chartElementAnimation; 18 | final XyDataSeries series; 19 | 20 | @override 21 | void paint(Canvas canvas, Size size) { 22 | canvas.save(); 23 | Rect clipRect; 24 | double animationFactor; 25 | 26 | /// Clip rect will be added for series. 27 | if (series._visible) { 28 | final Rect axisClipRect = _calculatePlotOffset( 29 | chart._chartAxis._axisClipRect, 30 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 31 | canvas.clipRect(axisClipRect); 32 | animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; 33 | if (!series._chart._chartState.widgetNeedUpdate && 34 | series.animationDuration > 0) { 35 | _performLinearAnimation(series, canvas, animationFactor); 36 | } 37 | 38 | ///Draw spline for spline series 39 | series._draw(canvas, animationFactor); 40 | clipRect = _calculatePlotOffset( 41 | Rect.fromLTRB( 42 | chart._chartAxis._axisClipRect.left - series.markerSettings.width, 43 | chart._chartAxis._axisClipRect.top - series.markerSettings.height, 44 | chart._chartAxis._axisClipRect.right + 45 | series.markerSettings.width, 46 | chart._chartAxis._axisClipRect.bottom + 47 | series.markerSettings.height), 48 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 49 | } 50 | canvas.restore(); 51 | 52 | if (series._visible && (animationFactor > chart._seriesDurationFactor)) { 53 | canvas.clipRect(clipRect); 54 | 55 | ///Draw marker and other elements for spline series 56 | _renderSeriesElements(canvas, series, chartElementAnimation); 57 | } 58 | } 59 | 60 | @override 61 | bool shouldRepaint(_SplineChartPainter oldDelegate) => isRepaint; 62 | } 63 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [17.3.14] - 10/03/2019 2 | 3 | **Breaking changes** 4 | * `roundingPlace` property has been changed to `decimalPlaces` in the axis and tooltip. 5 | * `child` property has been changed to `widget` in chart annotation. 6 | * `position` property has been changed to `labelAlignment` in dataLabelSettings. 7 | * `imageUrl` property has been changed to `image` in markerSettings. 8 | * `backgroundImageUrl` property has been changed to `backgroundImage` in SfCartesianChart. 9 | * `initialSelectedDatIndexes` property has been moved to series from SfCartesianChart. 10 | 11 | **Bug fixes** 12 | * Tooltip format with point.y value is working properly now. 13 | * Bar chart with negative values is rendering properly now. 14 | 15 | ## [1.0.0-beta.5] - 09/17/2019 16 | 17 | **Features** 18 | * Stacked line, stacked area, stacked column, stacked bar, range column, pyramid and funnel chart types. 19 | * Logarithmic axis. 20 | * Axis crossing support. 21 | * Plot bands and recursive plot bands support. 22 | * Dynamic data source update animation. 23 | 24 | **Bug fixes** 25 | * Tooltip template will not be displayed for hidden series. 26 | * Now the axis interval will be calculated properly for small decimal values. 27 | * Normal range padding is working fine for category axis. 28 | * Few more improvements and bug fixes. 29 | 30 | ## [1.0.0-beta.4] - 08/29/2019 31 | 32 | **Bug fixes** 33 | * Now, the category axis will work properly with additional range padding. 34 | * Now, the column series of category axis with a point can be placed on the ticks. 35 | * Trackball interactive tooltip will be shown only for the visible series. 36 | * On panning with grid lines, the grid lines will be moved within the chart area. 37 | * Panning will work properly on adding or removing the chart series dynamically. 38 | * Now, the data labels will not be hidden on scrolling. 39 | * The circular chart will render at the center position along with the legend. 40 | * Now, the panning is working properly for the inversed axis. 41 | * Now, the data labels appearance can be customized using onDataLabelRender event. 42 | * The tooltip and explode in the circular charts will work together. properly. 43 | * The scatter series is rendering properly with image markers. 44 | * Few more improvements and bug fixes. 45 | 46 | ## [1.0.0-beta] - 07/16/2019 47 | 48 | Initial release. 49 | 50 | **Features** 51 | * Line, spline, area, column, bar, bubble, scatter, step line, fast line, pie, doughnut and radial bar chart types. 52 | * Numeric, category and date time axis types. 53 | * User interactive features like zooming and panning, trackball, crosshair, selection and tooltip. 54 | * Additional features like animation, marker, data label, empty points, legend, annotation and much more. -------------------------------------------------------------------------------- /lib/src/common/utils/enum.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Position of the legend in chart. 4 | /// - LegendPosition.auto places the legend either at the bottom when the height is 5 | /// greater than the width, or at right when the width is greater than height. 6 | /// - LegendPosition.bottom places the legend below the plot area. 7 | /// - LegendPosition.left places the legend at the left to the plot area. 8 | /// - LegendPosition.right places the legend at the right to the plot area. 9 | /// - LegendPosition.top places the legend at the top of the plot area. 10 | enum LegendPosition { auto, bottom, left, right, top } 11 | 12 | /// Alignment of various elements in chart. 13 | /// - ChartAlignment.near aligns to the near position. 14 | /// - ChartAlignment.center aligns to the center position. 15 | /// - ChartAlignment.far aligns to the far position. 16 | enum ChartAlignment { near, center, far } 17 | 18 | /// Mode to handle the legend items overflow. 19 | /// - LegendItemOverflowMode.wrap will wrap and place the remaining legend item to next line. 20 | /// - LegendItemOverflowMode.scroll will place all the legend items in single line and enables scrolling. 21 | /// - LegendItemOverflowMode.none will simply hides the remaining legend items 22 | enum LegendItemOverflowMode { wrap, scroll, none } 23 | 24 | /// Orientation of legend items. 25 | /// - LegendItemOrientation.auto will align the legend items based on the position. 26 | /// - LegendItemOrientation.horizontal will align the legend items horizontally. 27 | /// - LegendItemOrientation.vertical will align the legend item vertically. 28 | enum LegendItemOrientation { auto, horizontal, vertical } 29 | 30 | /// Supported shapes for legend item. 31 | enum LegendIconType { 32 | seriesType, 33 | circle, 34 | rectangle, 35 | image, 36 | pentagon, 37 | verticalLine, 38 | horizontalLine, 39 | diamond, 40 | triangle, 41 | invertedTriangle 42 | } 43 | 44 | /// Position of data labels in Cartesian chart. 45 | /// - ChartDataLabelAlignment.auto places the data label either top or bottom position 46 | /// of a point based on the position. 47 | /// - ChartDataLabelAlignment.outer places the data label at outside of a point. 48 | /// - ChartDataLabelAlignment.top places the data label at the top position of a point. 49 | /// - ChartDataLabelAlignment.bottom places the data label at the bottom position of a point. 50 | /// - ChartDataLabelAlignment.middle places the data label at the center position of a point. 51 | enum ChartDataLabelAlignment { auto, outer, top, bottom, middle } 52 | 53 | /// Position of data labels in Circular chart. 54 | /// - CircularLabelPosition.curve places the data label inside the point. 55 | /// - CircularLabelPosition.line places the data label outside the point. 56 | enum CircularLabelPosition { inside, outside } 57 | 58 | enum PyramidMode { linear, surface } 59 | 60 | enum SmartLabelMode { shift, hide, none } 61 | -------------------------------------------------------------------------------- /lib/src/common/event_args.dart: -------------------------------------------------------------------------------- 1 | //Event Arguments 2 | part of charts; 3 | 4 | /// Holds the onTooltipRender event arguments. 5 | class TooltipArgs { 6 | String text; 7 | String header; 8 | double locationX; 9 | double locationY; 10 | dynamic seriesIndex; 11 | List dataPoints; 12 | num pointIndex; 13 | } 14 | 15 | /// Holds the onActualRangeChanged event arguments. 16 | class ActualRangeChangedArgs { 17 | String axisName; 18 | ChartAxis axis; 19 | dynamic actualMin; 20 | dynamic actualMax; 21 | dynamic actualInterval; 22 | dynamic visibleMin; 23 | dynamic visibleMax; 24 | dynamic visibleInterval; 25 | AxisOrientation orientation; 26 | } 27 | 28 | /// Holds the onAxisLabelRender event arguments. 29 | class AxisLabelRenderArgs { 30 | String text; 31 | num value; 32 | String axisName; 33 | AxisOrientation orientation; 34 | ChartAxis axis; 35 | ChartTextStyle textStyle = ChartTextStyle(); 36 | } 37 | 38 | /// Holds the onDataLabelRender event arguments. 39 | class DataLabelRenderArgs { 40 | String text; 41 | ChartTextStyle textStyle = ChartTextStyle(); 42 | dynamic series; 43 | dynamic dataPoints; 44 | num pointIndex; 45 | } 46 | 47 | /// Holds the onLegendItemRender event arguments. 48 | class LegendRenderArgs { 49 | String text; 50 | LegendIconType legendIconType; 51 | int seriesIndex; 52 | //Applicable for circular chart alone 53 | int pointIndex; 54 | Color color; 55 | } 56 | 57 | /// Holds the onTrackballPositionChanging event arguments. 58 | class TrackballArgs { 59 | _ChartPointInfo chartPointInfo = _ChartPointInfo(); 60 | } 61 | 62 | /// Holds the onCrosshairPositionChanging event arguments. 63 | class CrosshairRenderArgs { 64 | ChartAxis axis; 65 | String text; 66 | Color lineColor; 67 | dynamic value; 68 | String axisName; 69 | AxisOrientation orientation; 70 | } 71 | 72 | /// Holds the zooming event arguments. 73 | class ZoomPanArgs { 74 | ChartAxis axis; 75 | double currentZoomPosition; 76 | double currentZoomFactor; 77 | double previousZoomPosition; 78 | double previousZoomFactor; 79 | } 80 | 81 | /// Holds the onPointTapped event arguments. 82 | class PointTapArgs { 83 | int seriesIndex; 84 | int pointIndex; 85 | List dataPoints; 86 | } 87 | 88 | /// Holds the onAxisLabelTapped event arguments. 89 | class AxisLabelTapArgs { 90 | ChartAxis axis; 91 | String text; 92 | num value; 93 | String axisName; 94 | } 95 | 96 | /// Holds the onLegendTapped event arguments. 97 | class LegendTapArgs { 98 | dynamic series; 99 | int seriesIndex; 100 | //Applicable for circular chart alone 101 | int pointIndex; 102 | } 103 | 104 | /// Holds the onSelectionChanged event arguments. 105 | class SelectionArgs { 106 | dynamic series; 107 | Color selectedColor; 108 | Color unselectedColor; 109 | Color selectedBorderColor; 110 | double selectedBorderWidth; 111 | Color unselectedBorderColor; 112 | double unselectedBorderWidth; 113 | int seriesIndex; 114 | int pointIndex; 115 | } 116 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/scatter_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for scatter series. 4 | class ScatterSegment extends ChartSegment { 5 | ///X position of the scatter 6 | num centerX; 7 | 8 | ///Y position of the scatter 9 | num centerY; 10 | 11 | ///Radius of the scatter 12 | num radius; 13 | 14 | ///X value of the scatter 15 | num xData; 16 | 17 | ///Y value of the scatter 18 | num yData; 19 | 20 | ///Size of the scatter 21 | num size; 22 | _CartesianChartPoint _currentPoint; 23 | 24 | /// Gets the color of the series. 25 | @override 26 | Paint getFillPaint() { 27 | final bool hasPointColor = series.pointColorMapper != null ? true : false; 28 | if (series.gradient == null) { 29 | if (color != null) { 30 | fillPaint = Paint() 31 | ..color = _currentPoint.isEmpty == true 32 | ? series.emptyPointSettings.color 33 | : (((hasPointColor && _currentPoint.pointColorMapper != null) && 34 | _currentPoint.pointColorMapper != null) 35 | ? _currentPoint.pointColorMapper 36 | : color) 37 | ..style = PaintingStyle.fill; 38 | } 39 | } else { 40 | fillPaint = _getLinearGradientPaint(series.gradient, _currentPoint.region, 41 | series._chart._requireInvertedAxis); 42 | } 43 | _defaultFillColor = fillPaint; 44 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 45 | return fillPaint; 46 | } 47 | 48 | /// Gets the border color of the series. 49 | @override 50 | Paint getStrokePaint() { 51 | final Paint strokePaint = Paint() 52 | ..color = _currentPoint.isEmpty == true 53 | ? series.emptyPointSettings.borderColor 54 | : series.markerSettings.isVisible 55 | ? series.markerSettings.borderColor ?? series._seriesColor 56 | : strokeColor 57 | ..style = PaintingStyle.stroke 58 | ..strokeWidth = _currentPoint.isEmpty == true 59 | ? series.emptyPointSettings.borderWidth 60 | : strokeWidth; 61 | series.borderWidth == 0 62 | ? strokePaint.color = Colors.transparent 63 | : strokePaint.color; 64 | strokePaint.color = strokePaint.color == Colors.transparent 65 | ? strokePaint.color 66 | : strokePaint.color.withOpacity(series.opacity); 67 | _defaultStrokeColor = strokePaint; 68 | return strokePaint; 69 | } 70 | 71 | /// Calculates the rendering bounds of a segment. 72 | @override 73 | void calculateSegmentPoints() { 74 | centerX = centerY = double.nan; 75 | final Rect rect = _calculatePlotOffset( 76 | series._chart._chartAxis._axisClipRect, 77 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 78 | final _ChartLocation currentPoint = _calculatePoint( 79 | xData, 80 | yData, 81 | series._xAxis, 82 | series._yAxis, 83 | series._chart._requireInvertedAxis, 84 | series, 85 | rect); 86 | centerX = currentPoint.x; 87 | centerY = currentPoint.y; 88 | radius = size; 89 | } 90 | 91 | /// Draws segment in series bounds. 92 | @override 93 | void onPaint(Canvas canvas) { 94 | series.selectionSettings._selectionRenderer._checkWithSelectionState( 95 | series.segments[currentSegmentIndex], series._chart); 96 | if (fillPaint != null) { 97 | series.animationDuration > 0 98 | ? _animateScatterSeries(series, _point, _oldPoint, animationFactor, 99 | canvas, fillPaint, strokePaint) 100 | : series.drawDataMarker(currentSegmentIndex, canvas, fillPaint, 101 | strokePaint, _point.markerPoint.x, _point.markerPoint.y); 102 | } 103 | } 104 | 105 | /// Method to set data. 106 | void _setData(List values) { 107 | xData = values[0]; 108 | yData = values[1]; 109 | size = values[2]; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/fastline_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the fast line series. 4 | class FastLineSeries extends XyDataSeries { 5 | FastLineSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper dataLabelMapper, 11 | String xAxisName, 12 | String yAxisName, 13 | Color color, 14 | double width, 15 | List dashArray, 16 | LinearGradient gradient, 17 | MarkerSettings markerSettings, 18 | EmptyPointSettings emptyPointSettings, 19 | DataLabelSettings dataLabelSettings, 20 | SortingOrder sortingOrder, 21 | bool isVisible, 22 | String name, 23 | bool enableTooltip, 24 | double animationDuration, 25 | SelectionSettings selectionSettings, 26 | bool isVisibleInLegend, 27 | LegendIconType legendIconType, 28 | String legendItemText, 29 | double opacity}) 30 | : super( 31 | name: name, 32 | xValueMapper: xValueMapper, 33 | yValueMapper: yValueMapper, 34 | sortFieldValueMapper: sortFieldValueMapper, 35 | dataLabelMapper: dataLabelMapper, 36 | dataSource: dataSource, 37 | xAxisName: xAxisName, 38 | yAxisName: yAxisName, 39 | color: color, 40 | width: width ?? 2, 41 | gradient: gradient, 42 | dashArray: dashArray, 43 | markerSettings: markerSettings, 44 | emptyPointSettings: emptyPointSettings, 45 | dataLabelSettings: dataLabelSettings, 46 | isVisible: isVisible, 47 | enableTooltip: enableTooltip, 48 | animationDuration: animationDuration, 49 | selectionSettings: selectionSettings, 50 | legendItemText: legendItemText, 51 | isVisibleInLegend: isVisibleInLegend, 52 | legendIconType: legendIconType, 53 | sortingOrder: sortingOrder, 54 | opacity: opacity); 55 | 56 | /// Creates a segment for a data point in the series. 57 | @override 58 | ChartSegment createSegment() => FastLineSegment(); 59 | 60 | /// Creates a collection of segments for the points in the series. 61 | @override 62 | void createSegments() => _createSegment(_dataPoints); 63 | 64 | /// FastLine segment is created here 65 | void _createSegment(List<_CartesianChartPoint> values) { 66 | final FastLineSegment segment = createSegment(); 67 | _isRectSeries = false; 68 | if (segment != null) { 69 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 70 | segment.series = this; 71 | segment.calculateSegmentPoints(); 72 | customizeSegment(segment); 73 | segment.strokePaint = segment.getStrokePaint(); 74 | segment.fillPaint = segment.getFillPaint(); 75 | segments.add(segment); 76 | } 77 | } 78 | 79 | /// Changes the series color, border color, and border width. 80 | @override 81 | void customizeSegment(ChartSegment segment) { 82 | segment.color = segment.series._seriesColor; 83 | segment.strokeColor = segment.series._seriesColor; 84 | segment.strokeWidth = segment.series.width; 85 | } 86 | 87 | ///Draws marker with different shape and color of the appropriate data point in the series. 88 | @override 89 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 90 | Paint strokePaint, double pointX, double pointY) { 91 | canvas.drawPath(_markerShapes[index], strokePaint); 92 | canvas.drawPath(_markerShapes[index], fillPaint); 93 | } 94 | 95 | /// Draws data label text of the appropriate data point in a series. 96 | @override 97 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 98 | double pointY, int angle, ChartTextStyle style) => 99 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 100 | } 101 | -------------------------------------------------------------------------------- /lib/src/chart/user_interaction/tooltip_template.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | // ignore: must_be_immutable 4 | class _TooltipTemplate extends StatefulWidget { 5 | // ignore: prefer_const_constructors_in_immutables 6 | _TooltipTemplate({this.rect, this.template, this.show, this.clipRect}); 7 | 8 | Rect rect; 9 | 10 | Widget template; 11 | 12 | bool show; 13 | 14 | Rect clipRect; 15 | 16 | _TooltipTemplateState state; 17 | 18 | @override 19 | State createState() { 20 | return _TooltipTemplateState(); 21 | } 22 | } 23 | 24 | class _TooltipTemplateState extends State<_TooltipTemplate> 25 | with SingleTickerProviderStateMixin { 26 | BuildContext tooltipContext; 27 | 28 | bool needMeasure; 29 | 30 | Size tooltipSize = const Size(0, 0); 31 | 32 | AnimationController _controller; 33 | Animation _animation; 34 | 35 | Timer tooltipTimer; 36 | 37 | @override 38 | void initState() { 39 | widget.state = this; 40 | _controller = AnimationController( 41 | vsync: this, duration: const Duration(milliseconds: 500)); 42 | _animation = Tween(begin: 0.0, end: 1.0) 43 | .animate(CurvedAnimation(parent: _controller, curve: Curves.bounceOut)); 44 | super.initState(); 45 | } 46 | 47 | void _tooltipClose() { 48 | setState(() { 49 | widget.show = false; 50 | }); 51 | } 52 | 53 | @override 54 | void dispose() { 55 | _controller.dispose(); 56 | super.dispose(); 57 | } 58 | 59 | @override 60 | void didUpdateWidget(_TooltipTemplate oldWidget) { 61 | widget.state = this; 62 | super.didUpdateWidget(oldWidget); 63 | } 64 | 65 | void _performTooltip() { 66 | needMeasure = true; 67 | tooltipSize = const Size(0, 0); 68 | setState(() {}); 69 | } 70 | 71 | void _loaded() { 72 | final RenderBox renderBox = tooltipContext.findRenderObject(); 73 | tooltipSize = renderBox.size; 74 | needMeasure = false; 75 | setState(() {}); 76 | } 77 | 78 | @override 79 | Widget build(BuildContext context) { 80 | Widget tooltipWidget; 81 | if (widget.show) { 82 | if (needMeasure) { 83 | tooltipWidget = 84 | Opacity(opacity: 0.0, child: Container(child: widget.template)); 85 | tooltipContext = context; 86 | SchedulerBinding.instance.addPostFrameCallback((_) => _loaded()); 87 | } else { 88 | _controller.forward(from: 0.0); 89 | final Rect tooltipRect = Rect.fromLTWH( 90 | widget.rect.left - tooltipSize.width / 2, 91 | widget.rect.top - tooltipSize.height / 2, 92 | tooltipSize.width, 93 | tooltipSize.height); 94 | final Offset location = 95 | _getTooltipLocation(tooltipRect, widget.clipRect); 96 | tooltipWidget = AnimatedBuilder( 97 | animation: _animation, 98 | child: Container( 99 | margin: EdgeInsets.fromLTRB(location.dx, location.dy, 0, 0), 100 | child: widget.template), 101 | builder: (BuildContext context, Widget _widget) { 102 | return Opacity(opacity: _controller.value * 1.0, child: _widget); 103 | }); 104 | } 105 | } else { 106 | tooltipWidget = Container(); 107 | } 108 | if (tooltipTimer != null) { 109 | tooltipTimer.cancel(); 110 | } 111 | if (widget.show) { 112 | tooltipTimer = Timer(const Duration(milliseconds: 2000), _tooltipClose); 113 | } 114 | return tooltipWidget; 115 | } 116 | 117 | Offset _getTooltipLocation(Rect tooltipRect, Rect bounds) { 118 | double left = tooltipRect.left, top = tooltipRect.top; 119 | if (tooltipRect.left < bounds.left) { 120 | left = bounds.left; 121 | } 122 | if (tooltipRect.top < bounds.top) { 123 | top = bounds.top; 124 | } 125 | if (tooltipRect.left + tooltipRect.width > bounds.left + bounds.width) { 126 | left = (bounds.left + bounds.width) - tooltipRect.width; 127 | } 128 | if (tooltipRect.top + tooltipRect.height > bounds.top + bounds.height) { 129 | top = (bounds.top + bounds.height) - tooltipRect.height; 130 | } 131 | return Offset(left, top); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/spline_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for spline series. 4 | class SplineSegment extends ChartSegment { 5 | num x1, y1, x2, y2, _x1Pos, _y1Pos, _x2Pos, _y2Pos; 6 | double startControlX, startControlY, endControlX, endControlY; 7 | _CartesianChartPoint _currentPoint; 8 | _CartesianChartPoint _nextPoint; 9 | Color _pointColorMapper; 10 | 11 | /// Gets the color of the series. 12 | @override 13 | Paint getFillPaint() { 14 | final Paint fillPaint = Paint(); 15 | if (strokeColor != null) { 16 | fillPaint.color = strokeColor.withOpacity(series.opacity); 17 | } 18 | fillPaint.strokeWidth = strokeWidth; 19 | fillPaint.style = PaintingStyle.stroke; 20 | _defaultFillColor = fillPaint; 21 | return fillPaint; 22 | } 23 | 24 | /// Gets the stroke color of the series. 25 | @override 26 | Paint getStrokePaint() { 27 | final Paint strokePaint = Paint(); 28 | if (series.gradient == null) { 29 | if (strokeColor != null) { 30 | strokePaint.color = 31 | _pointColorMapper ?? strokeColor.withOpacity(series.opacity); 32 | } 33 | } else { 34 | strokePaint.color = series.gradient.colors[0]; 35 | } 36 | strokePaint.strokeWidth = strokeWidth; 37 | strokePaint.style = PaintingStyle.stroke; 38 | strokePaint.strokeCap = StrokeCap.round; 39 | _defaultStrokeColor = strokePaint; 40 | return strokePaint; 41 | } 42 | 43 | /// Calculates the rendering bounds of a segment. 44 | @override 45 | void calculateSegmentPoints() { 46 | x1 = _x1Pos; 47 | y1 = _y1Pos; 48 | x2 = _x2Pos; 49 | y2 = _y2Pos; 50 | } 51 | 52 | /// Draws segment in series bounds. 53 | @override 54 | void onPaint(Canvas canvas) { 55 | series.selectionSettings._selectionRenderer._checkWithSelectionState( 56 | series.segments[currentSegmentIndex], series._chart); 57 | 58 | /// Draw spline series 59 | if (series.animationDuration > 0 && 60 | series._chart._chartState.widgetNeedUpdate && 61 | !series._chart._chartState._isLegendToggled && 62 | series._chart._chartState.prevWidgetSeries != null && 63 | series._chart._chartState.prevWidgetSeries.isNotEmpty && 64 | oldSeries != null && 65 | oldSeries.segments.isNotEmpty && 66 | oldSeries.segments[0] is SplineSegment && 67 | series._chart._chartState.prevWidgetSeries.length - 1 >= 68 | series.segments[currentSegmentIndex]._seriesIndex && 69 | series.segments[currentSegmentIndex].oldSeries.segments.isNotEmpty && 70 | _currentPoint.isGap != true && 71 | _nextPoint.isGap != true) { 72 | final SplineSegment currentSegment = series.segments[currentSegmentIndex]; 73 | final SplineSegment oldSegment = 74 | (currentSegment.oldSeries.segments.length - 1 >= currentSegmentIndex) 75 | ? currentSegment.oldSeries.segments[currentSegmentIndex] 76 | : null; 77 | _animateLineTypeSeries( 78 | canvas, 79 | series, 80 | strokePaint, 81 | animationFactor, 82 | currentSegment.x1, 83 | currentSegment.y1, 84 | currentSegment.x2, 85 | currentSegment.y2, 86 | oldSegment?.x1, 87 | oldSegment?.y1, 88 | oldSegment?.x2, 89 | oldSegment?.y2, 90 | currentSegment?.startControlX, 91 | currentSegment?.startControlY, 92 | oldSegment?.startControlX, 93 | oldSegment?.startControlY, 94 | currentSegment.endControlX, 95 | currentSegment.endControlY, 96 | oldSegment?.endControlX, 97 | oldSegment?.endControlY, 98 | ); 99 | } else { 100 | final Path path = Path(); 101 | path.moveTo(x1, y1); 102 | if (_currentPoint.isGap != true && _nextPoint.isGap != true) { 103 | path.cubicTo( 104 | startControlX, startControlY, endControlX, endControlY, x2, y2); 105 | _drawDashedLine(canvas, series, strokePaint, path); 106 | } 107 | } 108 | } 109 | 110 | /// Method to set data. 111 | void _setData(List values) { 112 | _x1Pos = values[0]; 113 | _y1Pos = values[1]; 114 | _x2Pos = values[2]; 115 | _y2Pos = values[3]; 116 | startControlX = values[4]; 117 | startControlY = values[5]; 118 | endControlX = values[6]; 119 | endControlY = values[7]; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/fastline_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for fast line series. 4 | class FastLineSegment extends ChartSegment { 5 | Path _path; 6 | 7 | /// Gets the color of the series. 8 | @override 9 | Paint getFillPaint() { 10 | final Paint fillPaint = Paint(); 11 | if (color != null) { 12 | fillPaint.color = color.withOpacity(series.opacity); 13 | } 14 | fillPaint.style = PaintingStyle.fill; 15 | _defaultFillColor = fillPaint; 16 | return fillPaint; 17 | } 18 | 19 | /// Gets the stroke color of the series. 20 | @override 21 | Paint getStrokePaint() { 22 | final Paint strokePaint = Paint(); 23 | if (series.gradient == null) { 24 | if (strokeColor != null) { 25 | strokePaint.color = strokeColor.withOpacity(series.opacity); 26 | } 27 | } else { 28 | strokePaint.color = series.gradient.colors[0]; 29 | } 30 | strokePaint.strokeWidth = strokeWidth; 31 | strokePaint.style = PaintingStyle.stroke; 32 | strokePaint.strokeCap = StrokeCap.round; 33 | _defaultStrokeColor = strokePaint; 34 | return strokePaint; 35 | } 36 | 37 | /// Draws segment in series bounds. 38 | @override 39 | void onPaint(Canvas canvas) { 40 | series.selectionSettings._selectionRenderer 41 | ._checkWithSelectionState(series.segments[0], series._chart); 42 | series.dashArray != null 43 | ? _drawDashedLine(canvas, series, strokePaint, _path) 44 | : canvas.drawPath(_path, strokePaint); 45 | } 46 | 47 | /// Calculates the rendering bounds of a segment. 48 | @override 49 | void calculateSegmentPoints() { 50 | final ChartAxis xAxis = series._xAxis; 51 | final ChartAxis yAxis = series._yAxis; 52 | final Rect rect = _calculatePlotOffset( 53 | series._chart._chartAxis._axisClipRect, 54 | Offset(xAxis.plotOffset, yAxis.plotOffset)); 55 | _CartesianChartPoint prevPoint, point; 56 | _ChartLocation currentLocation; 57 | _path = Path(); 58 | final _VisibleRange xVisibleRange = series._xAxis._visibleRange; 59 | final _VisibleRange yVisibleRange = series._yAxis._visibleRange; 60 | final List<_CartesianChartPoint> seriesPoints = series._dataPoints; 61 | final Rect areaBounds = series._chart._chartAxis._axisClipRect; 62 | final num xTolerance = (xVisibleRange.delta / areaBounds.width).abs(); 63 | final num yTolerance = (yVisibleRange.delta / areaBounds.height).abs(); 64 | num prevXValue = (seriesPoints.isNotEmpty && 65 | seriesPoints[0] != null && 66 | seriesPoints[0].xValue > xTolerance) 67 | ? 0 68 | : xTolerance; 69 | num prevYValue = (seriesPoints.isNotEmpty && 70 | seriesPoints[0] != null && 71 | seriesPoints[0].yValue > yTolerance) 72 | ? 0 73 | : yTolerance; 74 | num xVal = 0; 75 | num yVal = 0; 76 | 77 | ///Eliminating nearest points 78 | dynamic currentPoint; 79 | for (int pointIndex = 0; 80 | pointIndex < series._dataPoints.length; 81 | pointIndex++) { 82 | currentPoint = series._dataPoints[pointIndex]; 83 | xVal = currentPoint.xValue != null 84 | ? currentPoint.xValue 85 | : xVisibleRange.minimum; 86 | yVal = currentPoint.yValue != null 87 | ? currentPoint.yValue 88 | : yVisibleRange.minimum; 89 | if ((prevXValue - xVal).abs() >= xTolerance || 90 | (prevYValue - yVal).abs() >= yTolerance) { 91 | point = currentPoint; 92 | if (point.isVisible) { 93 | currentLocation = _calculatePoint(point.xValue, point.yValue, xAxis, 94 | yAxis, series._chart._requireInvertedAxis, series, rect); 95 | point.markerPoint = currentLocation; 96 | if (prevPoint == null) 97 | _path.moveTo(currentLocation.x, currentLocation.y); 98 | else if (series._dataPoints[pointIndex - 1].isVisible == false && 99 | series.emptyPointSettings.mode == EmptyPointMode.gap) 100 | _path.moveTo(currentLocation.x, currentLocation.y); 101 | else if (point.isGap != true && 102 | series._dataPoints[pointIndex - 1].isGap != true && 103 | series._dataPoints[pointIndex].isVisible == true) 104 | _path.lineTo(currentLocation.x, currentLocation.y); 105 | else 106 | _path.moveTo(currentLocation.x, currentLocation.y); 107 | } 108 | prevPoint = point; 109 | prevXValue = xVal; 110 | prevYValue = yVal; 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/line_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for line series. 4 | class LineSegment extends ChartSegment { 5 | num x1, y1, x2, y2, _x1Pos, _y1Pos, _x2Pos, _y2Pos; 6 | Color _pointColorMapper; 7 | 8 | /// Gets the color of the series. 9 | @override 10 | Paint getFillPaint() { 11 | final Paint fillPaint = Paint(); 12 | if (color != null) { 13 | fillPaint.color = _pointColorMapper ?? color.withOpacity(series.opacity); 14 | } 15 | fillPaint.strokeWidth = strokeWidth; 16 | fillPaint.style = PaintingStyle.fill; 17 | _defaultFillColor = fillPaint; 18 | return fillPaint; 19 | } 20 | 21 | /// Gets the stroke color of the series. 22 | @override 23 | Paint getStrokePaint() { 24 | final Paint strokePaint = Paint(); 25 | if (series.gradient == null) { 26 | if (strokeColor != null) { 27 | strokePaint.color = 28 | _pointColorMapper ?? strokeColor.withOpacity(series.opacity); 29 | } 30 | } else { 31 | strokePaint.color = series.gradient.colors[0]; 32 | } 33 | strokePaint.strokeWidth = strokeWidth; 34 | strokePaint.style = PaintingStyle.stroke; 35 | strokePaint.strokeCap = StrokeCap.round; 36 | _defaultStrokeColor = strokePaint; 37 | return strokePaint; 38 | } 39 | 40 | /// Calculates the rendering bounds of a segment. 41 | @override 42 | void calculateSegmentPoints() { 43 | final dynamic start = series._xAxis._visibleRange.minimum; 44 | final dynamic end = series._xAxis._visibleRange.maximum; 45 | x1 = y1 = x2 = y2 = double.nan; 46 | final Rect rect = _calculatePlotOffset( 47 | series._chart._chartAxis._axisClipRect, 48 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 49 | if ((_x1Pos != null && 50 | _x2Pos != null && 51 | _y1Pos != null && 52 | _y2Pos != null) && 53 | ((_x1Pos >= start && _x1Pos <= end) || 54 | (_x2Pos >= start && _x2Pos <= end) || 55 | (start >= _x1Pos && start <= _x2Pos))) { 56 | final _ChartLocation currentChartPoint = _calculatePoint( 57 | _x1Pos, 58 | _y1Pos, 59 | series._xAxis, 60 | series._yAxis, 61 | series._chart._requireInvertedAxis, 62 | series, 63 | rect); 64 | final _ChartLocation nextPoint = _calculatePoint( 65 | _x2Pos, 66 | _y2Pos, 67 | series._xAxis, 68 | series._yAxis, 69 | series._chart._requireInvertedAxis, 70 | series, 71 | rect); 72 | x1 = currentChartPoint.x; 73 | y1 = currentChartPoint.y; 74 | x2 = nextPoint.x; 75 | y2 = nextPoint.y; 76 | } 77 | } 78 | 79 | /// Draws segment in series bounds. 80 | @override 81 | void onPaint(Canvas canvas) { 82 | series.selectionSettings._selectionRenderer._checkWithSelectionState( 83 | series.segments[currentSegmentIndex], series._chart); 84 | if (series.animationDuration > 0 && 85 | series._chart._chartState.widgetNeedUpdate && 86 | !series._chart._chartState._isLegendToggled && 87 | series._chart._chartState.prevWidgetSeries != null && 88 | series._chart._chartState.prevWidgetSeries.isNotEmpty && 89 | oldSeries != null && 90 | oldSeries.segments.isNotEmpty && 91 | oldSeries.segments[0] is LineSegment && 92 | series._chart._chartState.prevWidgetSeries.length - 1 >= 93 | series.segments[currentSegmentIndex]._seriesIndex && 94 | series.segments[currentSegmentIndex].oldSeries.segments.isNotEmpty) { 95 | final LineSegment currentSegment = series.segments[currentSegmentIndex]; 96 | final LineSegment oldSegment = 97 | (currentSegment.oldSeries.segments.length - 1 >= currentSegmentIndex) 98 | ? currentSegment.oldSeries.segments[currentSegmentIndex] 99 | : null; 100 | _animateLineTypeSeries( 101 | canvas, 102 | series, 103 | strokePaint, 104 | animationFactor, 105 | currentSegment.x1, 106 | currentSegment.y1, 107 | currentSegment.x2, 108 | currentSegment.y2, 109 | oldSegment?.x1, 110 | oldSegment?.y1, 111 | oldSegment?.x2, 112 | oldSegment?.y2, 113 | ); 114 | } else { 115 | final Path path = Path(); 116 | path.moveTo(x1, y1); 117 | path.lineTo(x2, y2); 118 | _drawDashedLine(canvas, series, strokePaint, path); 119 | } 120 | } 121 | 122 | /// Method to set data. 123 | void _setData(List values) { 124 | _x1Pos = values[0]; 125 | _y1Pos = values[1]; 126 | _x2Pos = values[2]; 127 | _y2Pos = values[3]; 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/stacked_line_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for staceked line series. 4 | class StackedLineSegment extends ChartSegment { 5 | num x1, y1, x2, y2, _x1Pos, _y1Pos, _x2Pos, _y2Pos; 6 | Color _pointColorMapper; 7 | 8 | /// Gets the color of the series. 9 | @override 10 | Paint getFillPaint() { 11 | final Paint fillPaint = Paint(); 12 | if (color != null) { 13 | fillPaint.color = _pointColorMapper ?? color.withOpacity(series.opacity); 14 | } 15 | fillPaint.strokeWidth = strokeWidth; 16 | fillPaint.style = PaintingStyle.fill; 17 | _defaultFillColor = fillPaint; 18 | return fillPaint; 19 | } 20 | 21 | /// Gets the stroke color of the series. 22 | @override 23 | Paint getStrokePaint() { 24 | final Paint strokePaint = Paint(); 25 | if (series.gradient == null) { 26 | if (strokeColor != null) { 27 | strokePaint.color = 28 | _pointColorMapper ?? strokeColor.withOpacity(series.opacity); 29 | } 30 | } else { 31 | strokePaint.color = series.gradient.colors[0]; 32 | } 33 | strokePaint.strokeWidth = strokeWidth; 34 | strokePaint.style = PaintingStyle.stroke; 35 | strokePaint.strokeCap = StrokeCap.round; 36 | _defaultStrokeColor = strokePaint; 37 | return strokePaint; 38 | } 39 | 40 | /// Calculates the rendering bounds of a segment. 41 | @override 42 | void calculateSegmentPoints() { 43 | final dynamic start = series._xAxis._visibleRange.minimum; 44 | final dynamic end = series._xAxis._visibleRange.maximum; 45 | x1 = y1 = x2 = y2 = double.nan; 46 | final Rect rect = _calculatePlotOffset( 47 | series._chart._chartAxis._axisClipRect, 48 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 49 | if ((_x1Pos != null && 50 | _x2Pos != null && 51 | _y1Pos != null && 52 | _y2Pos != null) && 53 | ((_x1Pos >= start && _x1Pos <= end) || 54 | (_x2Pos >= start && _x2Pos <= end) || 55 | (start >= _x1Pos && start <= _x2Pos))) { 56 | final _ChartLocation currentChartPoint = _calculatePoint( 57 | _x1Pos, 58 | _y1Pos, 59 | series._xAxis, 60 | series._yAxis, 61 | series._chart._requireInvertedAxis, 62 | series, 63 | rect); 64 | final _ChartLocation nextPoint = _calculatePoint( 65 | _x2Pos, 66 | _y2Pos, 67 | series._xAxis, 68 | series._yAxis, 69 | series._chart._requireInvertedAxis, 70 | series, 71 | rect); 72 | x1 = currentChartPoint.x; 73 | y1 = currentChartPoint.y; 74 | x2 = nextPoint.x; 75 | y2 = nextPoint.y; 76 | } 77 | } 78 | 79 | /// Draws segment in series bounds. 80 | @override 81 | void onPaint(Canvas canvas) { 82 | final Rect rect = series._chart._chartAxis._axisClipRect; 83 | _ChartLocation point1; 84 | final ChartAxis xAxis = series._xAxis; 85 | final ChartAxis yAxis = series._yAxis; 86 | _CartesianChartPoint point; 87 | final Path path = Path(); 88 | final _StackedValues stackedValues = series._stackingValues[0]; 89 | for (int i = 0; i < series._dataPoints.length; i++) { 90 | point = series._dataPoints[i]; 91 | point.symbolLocations = <_ChartLocation>[]; 92 | point.regions = []; 93 | if (point.isVisible) { 94 | point1 = _calculatePoint( 95 | series._dataPoints[i].xValue, 96 | stackedValues.endValues[i], 97 | xAxis, 98 | yAxis, 99 | series._chart._requireInvertedAxis, 100 | series, 101 | rect); 102 | i == 0 103 | ? path.moveTo(point1.x, point1.y) 104 | : path.lineTo(point1.x, point1.y); 105 | } else { 106 | if (series.emptyPointSettings.mode != EmptyPointMode.drop) { 107 | if (series._dataPoints.length > i + 1 && 108 | series._dataPoints[i + 1] != null && 109 | series._dataPoints[i + 1].isVisible) { 110 | point1 = _calculatePoint( 111 | series._dataPoints[i + 1].xValue, 112 | stackedValues.endValues[i + 1], 113 | xAxis, 114 | yAxis, 115 | series._chart._requireInvertedAxis, 116 | series, 117 | rect); 118 | path.moveTo(point1.x, point1.y); 119 | } 120 | } 121 | } 122 | } 123 | _drawDashedLine(canvas, series, strokePaint, path); 124 | } 125 | 126 | /// Method to set data. 127 | void _setData(List values) { 128 | _x1Pos = values[0]; 129 | _y1Pos = values[1]; 130 | _x2Pos = values[2]; 131 | _y2Pos = values[3]; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/bubble_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for bubble series. 4 | class BubbleSegment extends ChartSegment { 5 | ///X position of the bubble 6 | num centerX; 7 | 8 | ///Y position of the bubble 9 | num centerY; 10 | 11 | ///Radius of the bubble 12 | num radius; 13 | 14 | ///X value of the bubble 15 | num xData; 16 | 17 | ///Y value of the bubble 18 | num yData; 19 | 20 | ///Size of the bubble 21 | num size; 22 | 23 | ///Bubble series. 24 | XyDataSeries series; 25 | 26 | _CartesianChartPoint _currentPoint; 27 | 28 | /// Gets the color of the series. 29 | @override 30 | Paint getFillPaint() { 31 | final bool hasPointColor = series.pointColorMapper != null ? true : false; 32 | if (series.gradient == null) { 33 | if (color != null) { 34 | fillPaint = Paint() 35 | ..color = _currentPoint.isEmpty == true 36 | ? series.emptyPointSettings.color 37 | : ((hasPointColor && _currentPoint.pointColorMapper != null) 38 | ? _currentPoint.pointColorMapper 39 | : color) 40 | ..style = PaintingStyle.fill; 41 | } 42 | } else { 43 | fillPaint = _getLinearGradientPaint(series.gradient, _currentPoint.region, 44 | series._chart._requireInvertedAxis); 45 | } 46 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 47 | _defaultFillColor = fillPaint; 48 | return fillPaint; 49 | } 50 | 51 | /// Gets the border color of the series. 52 | @override 53 | Paint getStrokePaint() { 54 | final Paint strokePaint = Paint() 55 | ..color = _currentPoint.isEmpty == true 56 | ? series.emptyPointSettings.borderColor 57 | : strokeColor 58 | ..style = PaintingStyle.stroke 59 | ..strokeWidth = _currentPoint.isEmpty == true 60 | ? series.emptyPointSettings.borderWidth 61 | : strokeWidth; 62 | series.borderWidth == 0 63 | ? strokePaint.color = Colors.transparent 64 | : strokePaint.color; 65 | strokePaint.color = strokePaint.color == Colors.transparent 66 | ? strokePaint.color 67 | : strokePaint.color.withOpacity(series.opacity); 68 | _defaultStrokeColor = strokePaint; 69 | return strokePaint; 70 | } 71 | 72 | /// Calculates the rendering bounds of a segment. 73 | @override 74 | void calculateSegmentPoints() { 75 | centerX = centerY = double.nan; 76 | final Rect rect = _calculatePlotOffset( 77 | series._chart._chartAxis._axisClipRect, 78 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 79 | final _ChartLocation currentPoint = _calculatePoint( 80 | xData, 81 | yData, 82 | series._xAxis, 83 | series._yAxis, 84 | series._chart._requireInvertedAxis, 85 | series, 86 | rect); 87 | centerX = currentPoint.x; 88 | centerY = currentPoint.y; 89 | radius = size; 90 | } 91 | 92 | /// Draws segment in series bounds. 93 | @override 94 | void onPaint(Canvas canvas) { 95 | series.selectionSettings._selectionRenderer._checkWithSelectionState( 96 | series.segments[currentSegmentIndex], series._chart); 97 | segmentRect = RRect.fromRectAndRadius(_currentPoint.region, Radius.zero); 98 | if (series._chart._chartState.widgetNeedUpdate && 99 | !series._chart._chartState._isLegendToggled && 100 | series._chart._chartState.prevWidgetSeries != null && 101 | series._chart._chartState.prevWidgetSeries.isNotEmpty && 102 | oldSeries.segments.isNotEmpty && 103 | oldSeries.segments[0] is BubbleSegment && 104 | series.animationDuration > 0 && 105 | _oldPoint != null) { 106 | final BubbleSegment currentSegment = series.segments[currentSegmentIndex]; 107 | final BubbleSegment oldSegment = 108 | (currentSegment.oldSeries.segments.length - 1 >= currentSegmentIndex) 109 | ? currentSegment.oldSeries.segments[currentSegmentIndex] 110 | : null; 111 | _animateBubbleSeries( 112 | canvas, 113 | centerX, 114 | centerY, 115 | oldSegment.centerX, 116 | oldSegment.centerY, 117 | oldSegment?.size, 118 | animationFactor, 119 | radius, 120 | strokePaint, 121 | fillPaint, 122 | series._chart.isTransposed); 123 | } else { 124 | canvas.drawCircle( 125 | Offset(centerX, centerY), radius * animationFactor, fillPaint); 126 | canvas.drawCircle( 127 | Offset(centerX, centerY), radius * animationFactor, strokePaint); 128 | } 129 | } 130 | 131 | /// Method to set data. 132 | void _setData(List values) { 133 | xData = values[0]; 134 | yData = values[1]; 135 | size = values[2]; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/stepline_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for step line series. 4 | class StepLineSegment extends ChartSegment { 5 | num x1, y1, x2, y2, x3, y3, _x1Pos, _y1Pos, _x2Pos, _y2Pos, _midX, _midY; 6 | _CartesianChartPoint _presentPoint; 7 | Color _pointColorMapper; 8 | 9 | /// Gets the color of the series. 10 | @override 11 | Paint getFillPaint() { 12 | final Paint fillPaint = Paint(); 13 | if (color != null) { 14 | fillPaint.color = color.withOpacity(series.opacity); 15 | } 16 | fillPaint.strokeWidth = strokeWidth; 17 | fillPaint.style = PaintingStyle.stroke; 18 | _defaultFillColor = fillPaint; 19 | return fillPaint; 20 | } 21 | 22 | /// Gets the stroke color of the series. 23 | @override 24 | Paint getStrokePaint() { 25 | final Paint strokePaint = Paint(); 26 | if (series.gradient == null) { 27 | if (strokeColor != null) { 28 | strokePaint.color = 29 | _pointColorMapper ?? strokeColor.withOpacity(series.opacity); 30 | } 31 | } else { 32 | strokePaint.color = series.gradient.colors[0]; 33 | } 34 | strokePaint.strokeWidth = strokeWidth; 35 | strokePaint.style = PaintingStyle.stroke; 36 | strokePaint.strokeCap = StrokeCap.square; 37 | _defaultStrokeColor = strokePaint; 38 | return strokePaint; 39 | } 40 | 41 | /// Calculates the rendering bounds of a segment. 42 | @override 43 | void calculateSegmentPoints() { 44 | final dynamic start = series._xAxis._visibleRange.minimum; 45 | final dynamic end = series._xAxis._visibleRange.maximum; 46 | x1 = y1 = x2 = y2 = double.nan; 47 | final Rect rect = _calculatePlotOffset( 48 | series._chart._chartAxis._axisClipRect, 49 | Offset(series._xAxis.plotOffset, series._yAxis.plotOffset)); 50 | if ((_x1Pos != null && 51 | _x2Pos != null && 52 | _y1Pos != null && 53 | _y2Pos != null) && 54 | ((_x1Pos >= start && _x1Pos <= end) || 55 | (_x2Pos >= start && _x2Pos <= end) || 56 | (start >= _x1Pos && start <= _x2Pos))) { 57 | final _ChartLocation currentPoint = _calculatePoint( 58 | _x1Pos, 59 | _y1Pos, 60 | series._xAxis, 61 | series._yAxis, 62 | series._chart._requireInvertedAxis, 63 | series, 64 | rect); 65 | final _ChartLocation nextPoint = _calculatePoint( 66 | _x2Pos, 67 | _y2Pos, 68 | series._xAxis, 69 | series._yAxis, 70 | series._chart._requireInvertedAxis, 71 | series, 72 | rect); 73 | final _ChartLocation midPoint = _calculatePoint( 74 | _midX, 75 | _midY, 76 | series._xAxis, 77 | series._yAxis, 78 | series._chart._requireInvertedAxis, 79 | series, 80 | rect); 81 | x1 = currentPoint.x; 82 | y1 = currentPoint.y; 83 | x2 = nextPoint.x; 84 | y2 = nextPoint.y; 85 | x3 = midPoint.x; 86 | y3 = midPoint.y; 87 | segmentRect = RRect.fromRectAndRadius(_presentPoint.region, Radius.zero); 88 | } 89 | } 90 | 91 | /// Draws segment in series bounds. 92 | @override 93 | void onPaint(Canvas canvas) { 94 | final Path path = Path(); 95 | series.selectionSettings._selectionRenderer._checkWithSelectionState( 96 | series.segments[currentSegmentIndex], series._chart); 97 | if (series.animationDuration > 0 && 98 | series._chart._chartState.widgetNeedUpdate && 99 | !series._chart._chartState._isLegendToggled && 100 | series._chart._chartState.prevWidgetSeries != null && 101 | series._chart._chartState.prevWidgetSeries.isNotEmpty && 102 | oldSeries != null && 103 | oldSeries.segments.isNotEmpty && 104 | oldSeries.segments[0] is StepLineSegment && 105 | series._chart._chartState.prevWidgetSeries.length - 1 >= 106 | series.segments[currentSegmentIndex]._seriesIndex && 107 | series.segments[currentSegmentIndex].oldSeries.segments.isNotEmpty) { 108 | final StepLineSegment currentSegment = 109 | series.segments[currentSegmentIndex]; 110 | final StepLineSegment oldSegment = 111 | (currentSegment.oldSeries.segments.length - 1 >= currentSegmentIndex) 112 | ? currentSegment.oldSeries.segments[currentSegmentIndex] 113 | : null; 114 | _animateLineTypeSeries( 115 | canvas, 116 | series, 117 | strokePaint, 118 | animationFactor, 119 | currentSegment.x1, 120 | currentSegment.y1, 121 | currentSegment.x2, 122 | currentSegment.y2, 123 | oldSegment?.x1, 124 | oldSegment?.y1, 125 | oldSegment?.x2, 126 | oldSegment?.y2, 127 | currentSegment.x3, 128 | currentSegment.y3, 129 | oldSegment?.x3, 130 | oldSegment?.y3, 131 | ); 132 | } else { 133 | path.moveTo(x1, y1); 134 | path.lineTo(x3, y3); 135 | path.lineTo(x2, y2); 136 | _drawDashedLine(canvas, series, strokePaint, path); 137 | } 138 | } 139 | 140 | /// Method to set data. 141 | void setData(List values) { 142 | _x1Pos = values[0]; 143 | _y1Pos = values[1]; 144 | _x2Pos = values[2]; 145 | _y2Pos = values[3]; 146 | _midX = _x2Pos; 147 | _midY = _y1Pos; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/stacked_bar_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the stacked bar series. 4 | class StackedBarSeries extends _StackedSeriesBase { 5 | StackedBarSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | SortingOrder sortingOrder, 13 | bool isTrackVisible, 14 | String groupName, 15 | String xAxisName, 16 | String yAxisName, 17 | String name, 18 | Color color, 19 | double width, 20 | double spacing, 21 | MarkerSettings markerSettings, 22 | EmptyPointSettings emptyPointSettings, 23 | DataLabelSettings dataLabelSettings, 24 | bool isVisible, 25 | LinearGradient gradient, 26 | BorderRadius borderRadius, 27 | bool enableTooltip, 28 | double animationDuration, 29 | Color trackColor, 30 | Color trackBorderColor, 31 | double trackBorderWidth, 32 | double trackPadding, 33 | Color borderColor, 34 | double borderWidth, 35 | SelectionSettings selectionSettings, 36 | bool isVisibleInLegend, 37 | LegendIconType legendIconType, 38 | String legendItemText, 39 | List dashArray, 40 | double opacity, 41 | List initialSelectedDataIndexes}) 42 | : super( 43 | name: name, 44 | dashArray: dashArray, 45 | groupName: groupName, 46 | spacing: spacing, 47 | xValueMapper: xValueMapper, 48 | yValueMapper: yValueMapper, 49 | sortFieldValueMapper: sortFieldValueMapper, 50 | pointColorMapper: pointColorMapper, 51 | dataLabelMapper: dataLabelMapper, 52 | dataSource: dataSource, 53 | xAxisName: xAxisName, 54 | isTrackVisible: isTrackVisible, 55 | trackColor: trackColor, 56 | trackBorderColor: trackBorderColor, 57 | trackBorderWidth: trackBorderWidth, 58 | trackPadding: trackPadding, 59 | yAxisName: yAxisName, 60 | color: color, 61 | width: width ?? 0.7, 62 | markerSettings: markerSettings, 63 | dataLabelSettings: dataLabelSettings, 64 | isVisible: isVisible, 65 | gradient: gradient, 66 | emptyPointSettings: emptyPointSettings, 67 | enableTooltip: enableTooltip, 68 | animationDuration: animationDuration, 69 | borderColor: borderColor, 70 | borderWidth: borderWidth, 71 | borderRadius: borderRadius, 72 | selectionSettings: selectionSettings, 73 | legendItemText: legendItemText, 74 | isVisibleInLegend: isVisibleInLegend, 75 | legendIconType: legendIconType, 76 | sortingOrder: sortingOrder, 77 | opacity: opacity, 78 | initialSelectedDataIndexes: initialSelectedDataIndexes); 79 | num _rectPosition; 80 | num _rectCount; 81 | 82 | @override 83 | ChartSegment createSegment() => StackedBarSegment(); 84 | 85 | @override 86 | void createSegments() { 87 | int segmentIndex = 0; 88 | for (int i = 0; i < _dataPoints.length; i++) { 89 | if (_dataPoints[i].isVisible && _dataPoints[i].isGap != true) { 90 | final List values = []; 91 | values.add(_dataPoints[i].xValue); 92 | values.add(_dataPoints[i].yValue); 93 | _createSegment(values, _dataPoints[i], segmentIndex); 94 | segmentIndex++; 95 | } 96 | } 97 | } 98 | 99 | /// Stacked Bar segment is created here 100 | void _createSegment(List values, 101 | _CartesianChartPoint currentPoint, int segmentIndex) { 102 | final StackedBarSegment segment = createSegment(); 103 | _isRectSeries = true; 104 | if (segment != null) { 105 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 106 | segment.currentSegmentIndex = segmentIndex; 107 | segment.series = this; 108 | segment._currentPoint = currentPoint; 109 | segment._setData(values); 110 | segment.calculateSegmentPoints(); 111 | customizeSegment(segment); 112 | segment.strokePaint = segment.getStrokePaint(); 113 | segment.fillPaint = segment.getFillPaint(); 114 | segment._trackerFillPaint = segment._getTrackerFillPaint(); 115 | segment._trackerStrokePaint = segment._getTrackerStrokePaint(); 116 | segments.add(segment); 117 | } 118 | } 119 | 120 | @override 121 | void customizeSegment(ChartSegment segment) { 122 | segment.color = segment.series._seriesColor; 123 | segment.strokeColor = segment.series.borderColor; 124 | segment.strokeWidth = segment.series.borderWidth; 125 | } 126 | 127 | @override 128 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 129 | double pointY, int angle, ChartTextStyle style) => 130 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 131 | 132 | @override 133 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 134 | Paint strokePaint, double pointX, double pointY) { 135 | canvas.drawPath(_markerShapes[index], strokePaint); 136 | canvas.drawPath(_markerShapes[index], fillPaint); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/stacked_column_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the stacked column series. 4 | class StackedColumnSeries extends _StackedSeriesBase { 5 | StackedColumnSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | SortingOrder sortingOrder, 13 | bool isTrackVisible, 14 | String groupName, 15 | String xAxisName, 16 | String yAxisName, 17 | String name, 18 | Color color, 19 | double width, 20 | double spacing, 21 | MarkerSettings markerSettings, 22 | EmptyPointSettings emptyPointSettings, 23 | DataLabelSettings dataLabelSettings, 24 | bool isVisible, 25 | LinearGradient gradient, 26 | BorderRadius borderRadius, 27 | bool enableTooltip, 28 | double animationDuration, 29 | Color trackColor, 30 | Color trackBorderColor, 31 | double trackBorderWidth, 32 | double trackPadding, 33 | Color borderColor, 34 | double borderWidth, 35 | SelectionSettings selectionSettings, 36 | bool isVisibleInLegend, 37 | LegendIconType legendIconType, 38 | String legendItemText, 39 | List dashArray, 40 | double opacity, 41 | List initialSelectedDataIndexes}) 42 | : super( 43 | name: name, 44 | dashArray: dashArray, 45 | groupName: groupName, 46 | spacing: spacing, 47 | xValueMapper: xValueMapper, 48 | yValueMapper: yValueMapper, 49 | sortFieldValueMapper: sortFieldValueMapper, 50 | pointColorMapper: pointColorMapper, 51 | dataLabelMapper: dataLabelMapper, 52 | dataSource: dataSource, 53 | xAxisName: xAxisName, 54 | isTrackVisible: isTrackVisible, 55 | trackColor: trackColor, 56 | trackBorderColor: trackBorderColor, 57 | trackBorderWidth: trackBorderWidth, 58 | trackPadding: trackPadding, 59 | yAxisName: yAxisName, 60 | color: color, 61 | width: width ?? 0.7, 62 | markerSettings: markerSettings, 63 | dataLabelSettings: dataLabelSettings, 64 | isVisible: isVisible, 65 | gradient: gradient, 66 | emptyPointSettings: emptyPointSettings, 67 | enableTooltip: enableTooltip, 68 | animationDuration: animationDuration, 69 | borderColor: borderColor, 70 | borderWidth: borderWidth, 71 | borderRadius: borderRadius, 72 | selectionSettings: selectionSettings, 73 | legendItemText: legendItemText, 74 | isVisibleInLegend: isVisibleInLegend, 75 | legendIconType: legendIconType, 76 | sortingOrder: sortingOrder, 77 | opacity: opacity, 78 | initialSelectedDataIndexes: initialSelectedDataIndexes); 79 | num _rectPosition; 80 | num _rectCount; 81 | 82 | @override 83 | ChartSegment createSegment() => StackedColumnSegment(); 84 | 85 | @override 86 | void createSegments() { 87 | int segmentIndex = 0; 88 | for (int i = 0; i < _dataPoints.length; i++) { 89 | if (_dataPoints[i].isVisible && _dataPoints[i].isGap != true) { 90 | final List values = []; 91 | values.add(_dataPoints[i].xValue); 92 | values.add(_dataPoints[i].yValue); 93 | _createSegment(values, _dataPoints[i], segmentIndex); 94 | segmentIndex++; 95 | } 96 | } 97 | } 98 | 99 | /// Stacked Column segment is created here 100 | void _createSegment(List values, 101 | _CartesianChartPoint currentPoint, int segmentIndex) { 102 | final StackedColumnSegment segment = createSegment(); 103 | _isRectSeries = true; 104 | if (segment != null) { 105 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 106 | segment.currentSegmentIndex = segmentIndex; 107 | segment.series = this; 108 | segment._currentPoint = currentPoint; 109 | segment._setData(values); 110 | segment.calculateSegmentPoints(); 111 | customizeSegment(segment); 112 | segment.strokePaint = segment.getStrokePaint(); 113 | segment.fillPaint = segment.getFillPaint(); 114 | segment._trackerFillPaint = segment._getTrackerFillPaint(); 115 | segment._trackerStrokePaint = segment._getTrackerStrokePaint(); 116 | segments.add(segment); 117 | } 118 | } 119 | 120 | @override 121 | void customizeSegment(ChartSegment segment) { 122 | segment.color = segment.series._seriesColor; 123 | segment.strokeColor = segment.series.borderColor; 124 | segment.strokeWidth = segment.series.borderWidth; 125 | } 126 | 127 | @override 128 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 129 | double pointY, int angle, ChartTextStyle style) => 130 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 131 | 132 | @override 133 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 134 | Paint strokePaint, double pointX, double pointY) { 135 | canvas.drawPath(_markerShapes[index], strokePaint); 136 | canvas.drawPath(_markerShapes[index], fillPaint); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/area_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the area series. 4 | class AreaSeries extends XyDataSeries { 5 | AreaSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | SortingOrder sortingOrder, 13 | String xAxisName, 14 | String yAxisName, 15 | String name, 16 | Color color, 17 | MarkerSettings markerSettings, 18 | EmptyPointSettings emptyPointSettings, 19 | DataLabelSettings dataLabelSettings, 20 | bool isVisible, 21 | bool enableTooltip, 22 | List dashArray, 23 | double animationDuration, 24 | Color borderColor, 25 | double borderWidth, 26 | LinearGradient gradient, 27 | SelectionSettings selectionSettings, 28 | bool isVisibleInLegend, 29 | LegendIconType legendIconType, 30 | String legendItemText, 31 | double opacity, 32 | AreaBorderMode borderMode}) 33 | : borderMode = borderMode ?? AreaBorderMode.top, 34 | super( 35 | xValueMapper: xValueMapper, 36 | yValueMapper: yValueMapper, 37 | sortFieldValueMapper: sortFieldValueMapper, 38 | pointColorMapper: pointColorMapper, 39 | dataLabelMapper: dataLabelMapper, 40 | dataSource: dataSource, 41 | xAxisName: xAxisName, 42 | yAxisName: yAxisName, 43 | name: name, 44 | color: color, 45 | markerSettings: markerSettings, 46 | dataLabelSettings: dataLabelSettings, 47 | isVisible: isVisible, 48 | emptyPointSettings: emptyPointSettings, 49 | enableTooltip: enableTooltip, 50 | dashArray: dashArray, 51 | animationDuration: animationDuration, 52 | borderColor: borderColor, 53 | borderWidth: borderWidth, 54 | gradient: gradient, 55 | selectionSettings: selectionSettings, 56 | legendItemText: legendItemText, 57 | isVisibleInLegend: isVisibleInLegend, 58 | legendIconType: legendIconType, 59 | sortingOrder: sortingOrder, 60 | opacity: opacity); 61 | 62 | ///Border type of area series. 63 | /// 64 | ///Defaults to AreaBorderMode.top 65 | /// 66 | ///Also refer [AreaBorderMode] 67 | /// 68 | ///```dart 69 | ///Widget build(BuildContext context) { 70 | /// return Container( 71 | /// child: SfCartesianChart( 72 | /// series: >[ 73 | /// AreaSeries( 74 | /// borderMode: AreaBorderMode.all, 75 | /// ), 76 | /// ], 77 | /// )); 78 | ///} 79 | ///``` 80 | final AreaBorderMode borderMode; 81 | 82 | /// Creates a segment for a data point in the series. 83 | @override 84 | ChartSegment createSegment() => AreaSegment(); 85 | 86 | /// Creates a collection of segments for the points in the series. 87 | @override 88 | void createSegments() => _createSegment(_dataPoints); 89 | 90 | /// Area segment is created here 91 | void _createSegment(List<_CartesianChartPoint> dataPoints) { 92 | final AreaSegment segment = createSegment(); 93 | _isRectSeries = false; 94 | if (segment != null) { 95 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 96 | segment.series = this; 97 | if (_chart._chartState.widgetNeedUpdate && 98 | _xAxis._zoomFactor == 1 && 99 | _yAxis._zoomFactor == 1 && 100 | _chart._chartState.prevWidgetSeries != null && 101 | _chart._chartState.prevWidgetSeries.isNotEmpty && 102 | _chart._chartState.prevWidgetSeries.length - 1 >= 103 | segment._seriesIndex && 104 | _chart._chartState.prevWidgetSeries[segment._seriesIndex] 105 | ._seriesName == 106 | segment.series._seriesName) { 107 | segment.oldSeries = 108 | _chart._chartState.prevWidgetSeries[segment._seriesIndex]; 109 | } 110 | customizeSegment(segment); 111 | segment.strokePaint = segment.getStrokePaint(); 112 | segment.fillPaint = segment.getFillPaint(); 113 | segments.add(segment); 114 | } 115 | } 116 | 117 | /// Changes the series color, border color, and border width. 118 | @override 119 | void customizeSegment(ChartSegment segment) { 120 | segment.color = segment.series._seriesColor; 121 | segment.strokeColor = segment.series.borderColor; 122 | segment.strokeWidth = segment.series.borderWidth; 123 | } 124 | 125 | /// Draws marker with different shape and color of the appropriate data point in the series. 126 | @override 127 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 128 | Paint strokePaint, double pointX, double pointY) { 129 | canvas.drawPath(_markerShapes[index], strokePaint); 130 | canvas.drawPath(_markerShapes[index], fillPaint); 131 | } 132 | 133 | /// Draws data label text of the appropriate data point in a series. 134 | @override 135 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 136 | double pointY, int angle, ChartTextStyle style) => 137 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 138 | } 139 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/stacked_area_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the stacked area series. 4 | class StackedAreaSeries extends _StackedSeriesBase { 5 | StackedAreaSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | SortingOrder sortingOrder, 13 | String xAxisName, 14 | String yAxisName, 15 | String name, 16 | String groupName, 17 | Color color, 18 | MarkerSettings markerSettings, 19 | EmptyPointSettings emptyPointSettings, 20 | DataLabelSettings dataLabelSettings, 21 | bool isVisible, 22 | bool enableTooltip, 23 | List dashArray, 24 | double animationDuration, 25 | Color borderColor, 26 | double borderWidth, 27 | LinearGradient gradient, 28 | SelectionSettings selectionSettings, 29 | bool isVisibleInLegend, 30 | LegendIconType legendIconType, 31 | String legendItemText, 32 | double opacity, 33 | AreaBorderMode borderMode}) 34 | : borderMode = borderMode ?? AreaBorderMode.top, 35 | super( 36 | xValueMapper: xValueMapper, 37 | yValueMapper: yValueMapper, 38 | sortFieldValueMapper: sortFieldValueMapper, 39 | pointColorMapper: pointColorMapper, 40 | dataLabelMapper: dataLabelMapper, 41 | dataSource: dataSource, 42 | xAxisName: xAxisName, 43 | yAxisName: yAxisName, 44 | name: name, 45 | color: color, 46 | markerSettings: markerSettings, 47 | dataLabelSettings: dataLabelSettings, 48 | isVisible: isVisible, 49 | emptyPointSettings: emptyPointSettings, 50 | enableTooltip: enableTooltip, 51 | dashArray: dashArray, 52 | animationDuration: animationDuration, 53 | borderColor: borderColor, 54 | borderWidth: borderWidth, 55 | gradient: gradient, 56 | selectionSettings: selectionSettings, 57 | legendItemText: legendItemText, 58 | isVisibleInLegend: isVisibleInLegend, 59 | legendIconType: legendIconType, 60 | sortingOrder: sortingOrder, 61 | groupName: groupName, 62 | opacity: opacity); 63 | 64 | ///Border type of stacked area series. 65 | /// 66 | ///Defaults to AreaBorderMode.top 67 | /// 68 | ///Also refer [AreaBorderMode] 69 | /// 70 | ///```dart 71 | ///Widget build(BuildContext context) { 72 | /// return Container( 73 | /// child: SfCartesianChart( 74 | /// series: >[ 75 | /// AreaSeries( 76 | /// borderMode: AreaBorderMode.all, 77 | /// ), 78 | /// ], 79 | /// )); 80 | ///} 81 | ///``` 82 | final AreaBorderMode borderMode; 83 | 84 | /// Creates a segment for a data point in the series. 85 | @override 86 | ChartSegment createSegment() => StackedAreaSegment(); 87 | 88 | /// Creates a collection of segments for the points in the series. 89 | @override 90 | void createSegments() { 91 | _createSegment(_dataPoints); 92 | } 93 | 94 | /// Area segment is created here 95 | void _createSegment(List<_CartesianChartPoint> dataPoints) { 96 | final StackedAreaSegment segment = createSegment(); 97 | _isRectSeries = false; 98 | if (segment != null) { 99 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 100 | segment.series = this; 101 | if (_chart._chartState.widgetNeedUpdate && 102 | _xAxis._zoomFactor == 1 && 103 | _yAxis._zoomFactor == 1 && 104 | _chart._chartState.prevWidgetSeries != null && 105 | _chart._chartState.prevWidgetSeries.isNotEmpty && 106 | _chart._chartState.prevWidgetSeries.length - 1 >= 107 | segment._seriesIndex && 108 | _chart._chartState.prevWidgetSeries[segment._seriesIndex] 109 | ._seriesName == 110 | segment.series._seriesName) { 111 | segment.oldSeries = 112 | _chart._chartState.prevWidgetSeries[segment._seriesIndex]; 113 | } 114 | customizeSegment(segment); 115 | segment.strokePaint = segment.getStrokePaint(); 116 | segment.fillPaint = segment.getFillPaint(); 117 | segments.add(segment); 118 | } 119 | } 120 | 121 | /// Changes the series color, border color, and border width. 122 | @override 123 | void customizeSegment(ChartSegment segment) { 124 | segment.color = segment.series._seriesColor; 125 | segment.strokeColor = segment.series.borderColor; 126 | segment.strokeWidth = segment.series.borderWidth; 127 | } 128 | 129 | /// Draws marker with different shape and color of the appropriate data point in the series. 130 | @override 131 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 132 | Paint strokePaint, double pointX, double pointY) { 133 | canvas.drawPath(_markerShapes[index], strokePaint); 134 | canvas.drawPath(_markerShapes[index], fillPaint); 135 | } 136 | 137 | /// Draws data label text of the appropriate data point in a series. 138 | @override 139 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 140 | double pointY, int angle, ChartTextStyle style) => 141 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 142 | } 143 | -------------------------------------------------------------------------------- /lib/src/circular_chart/renderer/pie_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the pie series. 4 | class PieSeries extends CircularSeries { 5 | PieSeries( 6 | {List dataSource, 7 | ChartValueMapper xValueMapper, 8 | ChartValueMapper yValueMapper, 9 | ChartValueMapper pointColorMapper, 10 | ChartValueMapper pointRadiusMapper, 11 | ChartValueMapper dataLabelMapper, 12 | ChartValueMapper sortFieldValueMapper, 13 | int startAngle, 14 | int endAngle, 15 | String radius, 16 | bool explode, 17 | bool explodeAll, 18 | int explodeIndex, 19 | ActivationMode explodeGesture, 20 | String explodeOffset, 21 | double groupTo, 22 | CircularChartGroupMode groupMode, 23 | EmptyPointSettings emptyPointSettings, 24 | Color strokeColor, 25 | double strokeWidth, 26 | double opacity, 27 | DataLabelSettings dataLabelSettings, 28 | bool enableTooltip, 29 | bool enableSmartLabels, 30 | String name, 31 | double animationDuration, 32 | SelectionSettings selectionSettings, 33 | SortingOrder sortingOrder, 34 | LegendIconType legendIconType, 35 | List initialSelectedDataIndexes}) 36 | : super( 37 | animationDuration: animationDuration, 38 | dataSource: dataSource, 39 | xValueMapper: (int index) => xValueMapper(dataSource[index], index), 40 | yValueMapper: (int index) => yValueMapper(dataSource[index], index), 41 | pointColorMapper: (int index) => pointColorMapper != null 42 | ? pointColorMapper(dataSource[index], index) 43 | : null, 44 | pointRadiusMapper: pointRadiusMapper == null 45 | ? null 46 | : (int index) => pointRadiusMapper(dataSource[index], index), 47 | dataLabelMapper: (int index) => dataLabelMapper != null 48 | ? dataLabelMapper(dataSource[index], index) 49 | : null, 50 | sortFieldValueMapper: sortFieldValueMapper != null 51 | ? (int index) => sortFieldValueMapper(dataSource[index], index) 52 | : null, 53 | startAngle: startAngle, 54 | endAngle: endAngle, 55 | radius: radius, 56 | explode: explode, 57 | explodeAll: explodeAll, 58 | explodeIndex: explodeIndex, 59 | explodeOffset: explodeOffset, 60 | explodeGesture: explodeGesture, 61 | groupTo: groupTo, 62 | groupMode: groupMode, 63 | emptyPointSettings: emptyPointSettings, 64 | initialSelectedDataIndexes: initialSelectedDataIndexes, 65 | borderColor: strokeColor, 66 | borderWidth: strokeWidth, 67 | opacity: opacity, 68 | dataLabelSettings: dataLabelSettings, 69 | enableTooltip: enableTooltip, 70 | name: name, 71 | selectionSettings: selectionSettings, 72 | legendIconType: legendIconType, 73 | sortingOrder: sortingOrder, 74 | enableSmartLabels: enableSmartLabels); 75 | } 76 | 77 | class _PieChartPainter extends CustomPainter { 78 | _PieChartPainter({ 79 | this.chart, 80 | this.index, 81 | this.isRepaint, 82 | this.animationController, 83 | this.seriesAnimation, 84 | ValueNotifier notifier, 85 | }) : super(repaint: notifier); 86 | final SfCircularChart chart; 87 | final int index; 88 | final bool isRepaint; 89 | final AnimationController animationController; 90 | final Animation seriesAnimation; 91 | PieSeries series; 92 | static _ChartPoint point; 93 | @override 94 | void paint(Canvas canvas, Size size) { 95 | num pointStartAngle; 96 | series = chart._chartSeries.visibleSeries[index]; 97 | pointStartAngle = series._start; 98 | series._pointRegions = <_Region>[]; 99 | bool isAnyPointNeedSelect = false; 100 | if (chart._chartState.initialRender) { 101 | isAnyPointNeedSelect = _checkIsAnyPointSelect(series, point, chart); 102 | } 103 | _ChartPoint oldPoint; 104 | final PieSeries oldSeries = 105 | (chart._chartState.widgetNeedUpdate && 106 | !chart._chartState._isLegendToggled && 107 | chart._chartState.prevSeries._seriesType == 'pie') 108 | ? chart._chartState.prevSeries 109 | : null; 110 | for (int i = 0; i < series._renderPoints.length; i++) { 111 | point = series._renderPoints[i]; 112 | oldPoint = (oldSeries != null && 113 | oldSeries._renderPoints != null && 114 | (oldSeries._renderPoints.length - 1 >= i)) 115 | ? oldSeries._renderPoints[i] 116 | : ((chart._chartState._isLegendToggled && 117 | chart._chartState.prevSeries._seriesType == 'pie') 118 | ? chart._chartState._oldPoints[i] 119 | : null); 120 | point.innerRadius = 0.0; 121 | pointStartAngle = series._renderPoint( 122 | chart, 123 | series, 124 | point, 125 | pointStartAngle, 126 | point.innerRadius, 127 | point.outerRadius, 128 | canvas, 129 | index, 130 | i, 131 | seriesAnimation != null ? seriesAnimation?.value : 1, 132 | seriesAnimation != null ? seriesAnimation?.value : 1, 133 | isAnyPointNeedSelect, 134 | oldPoint, 135 | chart._chartState._oldPoints); 136 | } 137 | } 138 | 139 | @override 140 | bool shouldRepaint(_PieChartPainter oldDelegate) => isRepaint; 141 | } 142 | -------------------------------------------------------------------------------- /lib/src/circular_chart/renderer/doughnut_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | //// Renders the doughnut series. 4 | class DoughnutSeries extends CircularSeries { 5 | DoughnutSeries( 6 | {List dataSource, 7 | ChartValueMapper xValueMapper, 8 | ChartValueMapper yValueMapper, 9 | ChartValueMapper pointColorMapper, 10 | ChartValueMapper pointRadiusMapper, 11 | ChartValueMapper dataLabelMapper, 12 | ChartValueMapper sortFieldValueMapper, 13 | int startAngle, 14 | int endAngle, 15 | String radius, 16 | String innerRadius, 17 | bool explode, 18 | bool explodeAll, 19 | int explodeIndex, 20 | String explodeOffset, 21 | ActivationMode explodeGesture, 22 | double groupTo, 23 | CircularChartGroupMode groupMode, 24 | EmptyPointSettings emptyPointSettings, 25 | Color strokeColor, 26 | double strokeWidth, 27 | DataLabelSettings dataLabelSettings, 28 | bool enableTooltip, 29 | bool enableSmartLabels, 30 | String name, 31 | double opacity, 32 | double animationDuration, 33 | SelectionSettings selectionSettings, 34 | SortingOrder sortingOrder, 35 | LegendIconType legendIconType, 36 | CornerStyle cornerStyle, 37 | List initialSelectedDataIndexes}) 38 | : super( 39 | dataSource: dataSource, 40 | xValueMapper: (int index) => xValueMapper(dataSource[index], index), 41 | yValueMapper: (int index) => yValueMapper(dataSource[index], index), 42 | pointColorMapper: (int index) => pointColorMapper != null 43 | ? pointColorMapper(dataSource[index], index) 44 | : null, 45 | pointRadiusMapper: pointRadiusMapper == null 46 | ? null 47 | : (int index) => pointRadiusMapper(dataSource[index], index), 48 | dataLabelMapper: (int index) => dataLabelMapper != null 49 | ? dataLabelMapper(dataSource[index], index) 50 | : null, 51 | sortFieldValueMapper: sortFieldValueMapper != null 52 | ? (int index) => sortFieldValueMapper(dataSource[index], index) 53 | : null, 54 | animationDuration: animationDuration, 55 | startAngle: startAngle, 56 | endAngle: endAngle, 57 | radius: radius, 58 | innerRadius: innerRadius, 59 | explode: explode, 60 | opacity: opacity, 61 | explodeAll: explodeAll, 62 | explodeIndex: explodeIndex, 63 | explodeOffset: explodeOffset, 64 | explodeGesture: explodeGesture, 65 | groupMode: groupMode, 66 | groupTo: groupTo, 67 | emptyPointSettings: emptyPointSettings, 68 | borderColor: strokeColor, 69 | borderWidth: strokeWidth, 70 | dataLabelSettings: dataLabelSettings, 71 | enableTooltip: enableTooltip, 72 | name: name, 73 | selectionSettings: selectionSettings, 74 | legendIconType: legendIconType, 75 | sortingOrder: sortingOrder, 76 | enableSmartLabels: enableSmartLabels, 77 | cornerStyle: cornerStyle, 78 | initialSelectedDataIndexes: initialSelectedDataIndexes, 79 | ); 80 | } 81 | 82 | class _DoughnutChartPainter extends CustomPainter { 83 | _DoughnutChartPainter({ 84 | this.chart, 85 | this.index, 86 | this.isRepaint, 87 | this.animationController, 88 | this.seriesAnimation, 89 | ValueNotifier notifier, 90 | }) : super(repaint: notifier); 91 | final SfCircularChart chart; 92 | final int index; 93 | final bool isRepaint; 94 | final AnimationController animationController; 95 | final Animation seriesAnimation; 96 | 97 | DoughnutSeries series; 98 | 99 | num innerRadius; 100 | 101 | num radius; 102 | 103 | @override 104 | void paint(Canvas canvas, Size size) { 105 | num pointStartAngle; 106 | series = chart._chartSeries.visibleSeries[index]; 107 | pointStartAngle = series._start; 108 | innerRadius = series._currentInnerRadius; 109 | radius = series._currentRadius; 110 | _ChartPoint point; 111 | series._pointRegions = <_Region>[]; 112 | _ChartPoint oldPoint; 113 | final DoughnutSeries oldSeries = 114 | (chart._chartState.widgetNeedUpdate && 115 | !chart._chartState._isLegendToggled && 116 | chart._chartState.prevSeries._seriesType == 'doughnut') 117 | ? chart._chartState.prevSeries 118 | : null; 119 | for (int i = 0; i < series._renderPoints.length; i++) { 120 | point = series._renderPoints[i]; 121 | oldPoint = (oldSeries != null && 122 | oldSeries._renderPoints != null && 123 | (oldSeries._renderPoints.length - 1 >= i)) 124 | ? oldSeries._renderPoints[i] 125 | : ((chart._chartState._isLegendToggled && 126 | chart._chartState.prevSeries._seriesType == 'doughnut') 127 | ? chart._chartState._oldPoints[i] 128 | : null); 129 | pointStartAngle = series._renderPoint( 130 | chart, 131 | series, 132 | point, 133 | pointStartAngle, 134 | point.innerRadius, 135 | point.outerRadius, 136 | canvas, 137 | index, 138 | i, 139 | seriesAnimation != null ? seriesAnimation?.value : 1, 140 | 1, 141 | _checkIsAnyPointSelect(series, point, chart), 142 | oldPoint, 143 | chart._chartState._oldPoints); 144 | } 145 | } 146 | 147 | @override 148 | bool shouldRepaint(_DoughnutChartPainter oldDelegate) => isRepaint; 149 | } 150 | -------------------------------------------------------------------------------- /lib/src/chart/common/marker.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Customizes the markers. 4 | class MarkerSettings { 5 | MarkerSettings( 6 | {this.isVisible = false, 7 | this.height = 8, 8 | this.width = 8, 9 | this.color = Colors.white, 10 | this.shape = DataMarkerType.circle, 11 | this.borderWidth = 2, 12 | this.borderColor, 13 | this.image}); 14 | 15 | ///Toggles the visibility of the marker. 16 | /// 17 | ///Defaults to false 18 | ///```dart 19 | ///Widget build(BuildContext context) { 20 | /// return Container( 21 | /// child: SfCartesianChart( 22 | /// selectionGesture: ActivationMode.doubleTap, 23 | /// series: >[ 24 | /// SplineSeries( 25 | /// markerSettings: MarkerSettings(isVisible: true), 26 | /// ), 27 | /// ], 28 | /// )); 29 | ///} 30 | ///``` 31 | final bool isVisible; 32 | 33 | ///Height of the marker shape. 34 | /// 35 | ///Defaults to 4 36 | ///```dart 37 | ///Widget build(BuildContext context) { 38 | /// return Container( 39 | /// child: SfCartesianChart( 40 | /// selectionGesture: ActivationMode.doubleTap, 41 | /// series: >[ 42 | /// SplineSeries( 43 | /// markerSettings: MarkerSettings( 44 | /// isVisible: true, height: 10), 45 | /// ), 46 | /// ], 47 | /// )); 48 | ///} 49 | ///``` 50 | final double height; 51 | 52 | ///Width of the marker shape. 53 | /// 54 | ///Defaults to 4 55 | ///```dart 56 | ///Widget build(BuildContext context) { 57 | /// return Container( 58 | /// child: SfCartesianChart( 59 | /// selectionGesture: ActivationMode.doubleTap, 60 | /// series: >[ 61 | /// SplineSeries( 62 | /// markerSettings: MarkerSettings( 63 | /// isVisible: true, width: 10), 64 | /// ), 65 | /// ], 66 | /// )); 67 | ///} 68 | ///``` 69 | final double width; 70 | 71 | ///Color of the marker shape. 72 | /// 73 | ///Defaults to null 74 | ///```dart 75 | ///Widget build(BuildContext context) { 76 | /// return Container( 77 | /// child: SfCartesianChart( 78 | /// selectionGesture: ActivationMode.doubleTap, 79 | /// series: >[ 80 | /// SplineSeries( 81 | /// markerSettings: MarkerSettings( 82 | /// isVisible: true, color: Colors.red), 83 | /// ), 84 | /// ], 85 | /// )); 86 | ///} 87 | ///``` 88 | final Color color; 89 | 90 | ///Shape of the marker. 91 | /// 92 | ///Defaults to DataMarkerType.circle. 93 | /// 94 | ///Also refer [DataMarkerType] 95 | /// 96 | ///```dart 97 | ///Widget build(BuildContext context) { 98 | /// return Container( 99 | /// child: SfCartesianChart( 100 | /// selectionGesture: ActivationMode.doubleTap, 101 | /// series: >[ 102 | /// SplineSeries( 103 | /// markerSettings: MarkerSettings( 104 | /// isVisible: true, shape: DataMarkerType.diamond), 105 | /// ), 106 | /// ], 107 | /// )); 108 | ///} 109 | ///``` 110 | final DataMarkerType shape; 111 | 112 | ///Border color of the marker. 113 | /// 114 | ///Defaults to null 115 | ///```dart 116 | ///Widget build(BuildContext context) { 117 | /// return Container( 118 | /// child: SfCartesianChart( 119 | /// selectionGesture: ActivationMode.doubleTap, 120 | /// series: >[ 121 | /// SplineSeries( 122 | /// markerSettings: MarkerSettings( 123 | /// isVisible: true, 124 | /// borderColor: Colors.red, borderWidth: 3), 125 | /// ), 126 | /// ], 127 | /// )); 128 | ///} 129 | ///``` 130 | final Color borderColor; 131 | 132 | ///Border width of the marker. 133 | /// 134 | ///Defaults to 2 135 | ///```dart 136 | ///Widget build(BuildContext context) { 137 | /// return Container( 138 | /// child: SfCartesianChart( 139 | /// selectionGesture: ActivationMode.doubleTap, 140 | /// series: >[ 141 | /// SplineSeries( 142 | /// markerSettings: MarkerSettings( 143 | /// isVisible: true, 144 | /// borderWidth: 2, borderColor: Colors.pink), 145 | /// ), 146 | /// ], 147 | /// )); 148 | ///} 149 | ///``` 150 | final double borderWidth; 151 | 152 | ///Image to be used as marker. 153 | /// 154 | ///Defaults to null 155 | ///```dart 156 | ///Widget build(BuildContext context) { 157 | /// return Container( 158 | /// child: SfCartesianChart( 159 | /// selectionGesture: ActivationMode.doubleTap, 160 | /// series: >[ 161 | /// SplineSeries( 162 | /// markerSettings: MarkerSettings( 163 | /// isVisible: true, image: const AssetImage('images/bike.png'), 164 | /// shape: DataMarkerType.image), 165 | /// ), 166 | /// ], 167 | /// )); 168 | ///} 169 | ///``` 170 | final ImageProvider image; 171 | 172 | String _imageUrl; 173 | 174 | dart_ui.Image _image; 175 | } 176 | -------------------------------------------------------------------------------- /lib/src/chart/themes/chart_theme.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | class _ChartTheme { 4 | _ChartTheme(); 5 | Color axisLabelColor; 6 | Color axisTitleColor; 7 | Color axisLineColor; 8 | Color majorGridLineColor; 9 | Color minorGridLineColor; 10 | Color majorTickLineColor; 11 | Color minorTickLineColor; 12 | Color chartTitleColor; 13 | Color titleBackgroundColor; 14 | Color legendLabelColor; 15 | Color legendTitleColor; 16 | Color legendBackGroundColor; 17 | Color plotAreaBackgroundColor; 18 | Color crosshairLineColor; 19 | Color crosshairFillColor; 20 | Color crosshairLabelColor; 21 | Color tooltipFillColor; 22 | Color tooltipLabelColor; 23 | Color tooltipHeaderLineColor; 24 | Color selectionRectFillColor; 25 | Color selectionRectStrokeColor; 26 | Color selectionTooltipConnectorLineColor; 27 | Brightness brightness; 28 | 29 | void initializeChartTheme(ThemeData theme) { 30 | switch (theme.brightness) { 31 | case Brightness.light: 32 | { 33 | axisLabelColor = const Color.fromRGBO(104, 104, 104, 1); 34 | axisTitleColor = const Color.fromRGBO(66, 66, 66, 1); 35 | axisLineColor = const Color.fromRGBO(181, 181, 181, 1); 36 | majorGridLineColor = const Color.fromRGBO(219, 219, 219, 1); 37 | minorGridLineColor = const Color.fromRGBO(234, 234, 234, 1); 38 | majorTickLineColor = const Color.fromRGBO(181, 181, 181, 1); 39 | minorTickLineColor = const Color.fromRGBO(214, 214, 214, 1); 40 | chartTitleColor = const Color.fromRGBO(66, 66, 66, 1); 41 | titleBackgroundColor = Colors.transparent; 42 | legendLabelColor = const Color.fromRGBO(53, 53, 53, 1); 43 | legendBackGroundColor = const Color.fromRGBO(255, 255, 255, 1); 44 | legendTitleColor = const Color.fromRGBO(66, 66, 66, 1); 45 | plotAreaBackgroundColor = Colors.transparent; 46 | crosshairLineColor = const Color.fromRGBO(79, 79, 79, 1); 47 | crosshairFillColor = const Color.fromRGBO(79, 79, 79, 1); 48 | crosshairLabelColor = const Color.fromRGBO(229, 229, 229, 1); 49 | tooltipFillColor = const Color.fromRGBO(0, 8, 22, 0.75); 50 | tooltipLabelColor = const Color.fromRGBO(255, 255, 255, 1); 51 | tooltipHeaderLineColor = const Color.fromRGBO(255, 255, 255, 1); 52 | selectionRectFillColor = const Color.fromRGBO(41, 171, 226, 0.1); 53 | selectionRectStrokeColor = const Color.fromRGBO(41, 171, 226, 1); 54 | selectionTooltipConnectorLineColor = 55 | const Color.fromRGBO(79, 79, 79, 1); 56 | brightness = Brightness.light; 57 | break; 58 | } 59 | case Brightness.dark: 60 | { 61 | axisLabelColor = const Color.fromRGBO(242, 242, 242, 1); 62 | axisTitleColor = const Color.fromRGBO(255, 255, 255, 1); 63 | axisLineColor = const Color.fromRGBO(255, 255, 255, 1); 64 | majorGridLineColor = const Color.fromRGBO(70, 74, 86, 1); 65 | minorGridLineColor = const Color.fromRGBO(70, 74, 86, 1); 66 | majorTickLineColor = const Color.fromRGBO(191, 191, 191, 1); 67 | minorTickLineColor = const Color.fromRGBO(150, 150, 150, 1); 68 | chartTitleColor = const Color.fromRGBO(255, 255, 255, 1); 69 | titleBackgroundColor = Colors.transparent; 70 | legendLabelColor = const Color.fromRGBO(255, 255, 255, 1); 71 | legendBackGroundColor = const Color.fromRGBO(0, 0, 0, 1); 72 | legendTitleColor = const Color.fromRGBO(255, 255, 255, 1); 73 | plotAreaBackgroundColor = Colors.transparent; 74 | crosshairLineColor = const Color.fromRGBO(255, 255, 255, 1); 75 | crosshairFillColor = const Color.fromRGBO(255, 255, 255, 1); 76 | crosshairLabelColor = const Color.fromRGBO(0, 0, 0, 1); 77 | tooltipFillColor = const Color.fromRGBO(255, 255, 255, 1); 78 | tooltipLabelColor = const Color.fromRGBO(0, 0, 0, 1); 79 | tooltipHeaderLineColor = const Color.fromRGBO(150, 150, 150, 1); 80 | selectionRectFillColor = const Color.fromRGBO(255, 217, 57, 0.3); 81 | selectionRectStrokeColor = const Color.fromRGBO(255, 255, 255, 1); 82 | selectionTooltipConnectorLineColor = 83 | const Color.fromRGBO(150, 150, 150, 1); 84 | brightness = Brightness.dark; 85 | break; 86 | } 87 | default: 88 | { 89 | axisLabelColor = const Color.fromRGBO(104, 104, 104, 1); 90 | axisTitleColor = const Color.fromRGBO(66, 66, 66, 1); 91 | axisLineColor = const Color.fromRGBO(181, 181, 181, 1); 92 | majorGridLineColor = const Color.fromRGBO(219, 219, 219, 1); 93 | minorGridLineColor = const Color.fromRGBO(234, 234, 234, 1); 94 | majorTickLineColor = const Color.fromRGBO(181, 181, 181, 1); 95 | minorTickLineColor = const Color.fromRGBO(214, 214, 214, 1); 96 | chartTitleColor = const Color.fromRGBO(66, 66, 66, 1); 97 | titleBackgroundColor = Colors.transparent; 98 | legendLabelColor = const Color.fromRGBO(53, 53, 53, 1); 99 | legendBackGroundColor = const Color.fromRGBO(255, 255, 255, 1); 100 | legendTitleColor = const Color.fromRGBO(66, 66, 66, 1); 101 | plotAreaBackgroundColor = Colors.transparent; 102 | crosshairLineColor = const Color.fromRGBO(79, 79, 79, 1); 103 | crosshairFillColor = const Color.fromRGBO(79, 79, 79, 1); 104 | crosshairLabelColor = const Color.fromRGBO(229, 229, 229, 1); 105 | tooltipFillColor = const Color.fromRGBO(0, 8, 22, 0.75); 106 | tooltipLabelColor = const Color.fromRGBO(255, 255, 255, 1); 107 | tooltipHeaderLineColor = const Color.fromRGBO(255, 255, 255, 1); 108 | selectionRectFillColor = const Color.fromRGBO(41, 171, 226, 0.1); 109 | selectionRectStrokeColor = const Color.fromRGBO(41, 171, 226, 1); 110 | selectionTooltipConnectorLineColor = 111 | const Color.fromRGBO(79, 79, 79, 1); 112 | brightness = Brightness.light; 113 | break; 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/scatter_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the scatter series. 4 | class ScatterSeries extends XyDataSeries { 5 | ScatterSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | String xAxisName, 13 | String yAxisName, 14 | String name, 15 | Color color, 16 | MarkerSettings markerSettings, 17 | EmptyPointSettings emptyPointSettings, 18 | bool isVisible, 19 | DataLabelSettings dataLabelSettings, 20 | bool enableTooltip, 21 | double animationDuration, 22 | Color borderColor, 23 | double borderWidth, 24 | LinearGradient gradient, 25 | SelectionSettings selectionSettings, 26 | bool isVisibleInLegend, 27 | LegendIconType legendIconType, 28 | SortingOrder sortingOrder, 29 | String legendItemText, 30 | double opacity, 31 | List initialSelectedDataIndexes}) 32 | : super( 33 | xValueMapper: xValueMapper, 34 | yValueMapper: yValueMapper, 35 | sortFieldValueMapper: sortFieldValueMapper, 36 | pointColorMapper: pointColorMapper, 37 | dataLabelMapper: dataLabelMapper, 38 | dataSource: dataSource, 39 | xAxisName: xAxisName, 40 | yAxisName: yAxisName, 41 | name: name, 42 | color: color, 43 | markerSettings: markerSettings, 44 | dataLabelSettings: dataLabelSettings, 45 | emptyPointSettings: emptyPointSettings, 46 | enableTooltip: enableTooltip, 47 | isVisible: isVisible, 48 | animationDuration: animationDuration, 49 | borderColor: borderColor, 50 | borderWidth: borderWidth, 51 | gradient: gradient, 52 | selectionSettings: selectionSettings, 53 | legendItemText: legendItemText, 54 | isVisibleInLegend: isVisibleInLegend, 55 | legendIconType: legendIconType, 56 | sortingOrder: sortingOrder, 57 | opacity: opacity, 58 | initialSelectedDataIndexes: initialSelectedDataIndexes); 59 | 60 | /// Creates a segment for a data point in the series. 61 | @override 62 | ChartSegment createSegment() => ScatterSegment(); 63 | 64 | _CartesianChartPoint _point; 65 | 66 | /// Creates a collection of segments for the points in the series. 67 | @override 68 | void createSegments() { 69 | int segmentIndex = 0; 70 | for (int i = 0; i < _dataPoints.length; i++) { 71 | if (_dataPoints[i].isVisible && _dataPoints[i].isGap != true) { 72 | final List values = []; 73 | values.add(_dataPoints[i].xValue); 74 | values.add(_dataPoints[i].yValue); 75 | values.add(markerSettings.width); 76 | _point = _dataPoints[i]; 77 | _createSegment(values, _dataPoints[i], segmentIndex); 78 | segmentIndex++; 79 | } 80 | } 81 | } 82 | 83 | /// Scatter segment is created here 84 | void _createSegment(List values, 85 | _CartesianChartPoint currentPoint, int segmentIndex) { 86 | final ScatterSegment segment = createSegment(); 87 | _isRectSeries = false; 88 | if (segment != null) { 89 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 90 | segment.currentSegmentIndex = segmentIndex; 91 | segment.series = this; 92 | segment._point = currentPoint; 93 | segment._currentPoint = currentPoint; 94 | segment._setData(values); 95 | segment.calculateSegmentPoints(); 96 | if (_chart._chartState.widgetNeedUpdate && 97 | !_chart._chartState._isLegendToggled && 98 | _xAxis._zoomFactor == 1 && 99 | _yAxis._zoomFactor == 1 && 100 | _chart._chartState.prevWidgetSeries != null && 101 | _chart._chartState.prevWidgetSeries.isNotEmpty && 102 | _chart._chartState.prevWidgetSeries.length - 1 >= 103 | segment._seriesIndex && 104 | _chart._chartState.prevWidgetSeries[segment._seriesIndex] 105 | ._seriesName == 106 | segment.series._seriesName) { 107 | segment.oldSeries = 108 | _chart._chartState.prevWidgetSeries[segment._seriesIndex]; 109 | segment._oldPoint = (segment.oldSeries.segments.isNotEmpty && 110 | segment.oldSeries.segments[0] is ScatterSegment && 111 | segment.oldSeries._dataPoints.length - 1 >= segmentIndex) 112 | ? segment.oldSeries._dataPoints[segmentIndex] 113 | : null; 114 | } 115 | segment.segmentRect = 116 | RRect.fromRectAndRadius(currentPoint.region, Radius.zero); 117 | customizeSegment(segment); 118 | segment.strokePaint = segment.getStrokePaint(); 119 | segment.fillPaint = segment.getFillPaint(); 120 | segments.add(segment); 121 | } 122 | } 123 | 124 | /// Changes the series color, border color, and border width. 125 | @override 126 | void customizeSegment(ChartSegment segment) { 127 | segment.color = segment.series._seriesColor; 128 | segment.strokeColor = segment.series.borderColor; 129 | segment.strokeWidth = segment.series.borderWidth; 130 | } 131 | 132 | ///Draws marker with different shape and color of the appropriate data point in the series. 133 | @override 134 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 135 | Paint strokePaint, double pointX, double pointY) { 136 | canvas.drawPath(_markerShapes[index], strokePaint); 137 | canvas.drawPath(_markerShapes[index], fillPaint); 138 | } 139 | 140 | /// Draws data label text of the appropriate data point in a series. 141 | @override 142 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 143 | double pointY, int angle, ChartTextStyle style) => 144 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 145 | } 146 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/range_column_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for range column series. 4 | class RangeColumnSegment extends ChartSegment { 5 | num x1; 6 | dynamic topLeft, topRight, bottomRight, bottomLeft, borderWidth; 7 | Path path; 8 | num low1, high1; 9 | BorderRadius _borderRadius; 10 | RRect _trackRect; 11 | _CartesianChartPoint _currentPoint; 12 | Paint _trackerFillPaint; 13 | Paint _trackerStrokePaint; 14 | 15 | /// Gets the color of the series. 16 | @override 17 | Paint getFillPaint() { 18 | final bool hasPointColor = series.pointColorMapper != null ? true : false; 19 | 20 | /// Get and set the paint options for range column series. 21 | if (series.gradient == null) { 22 | if (color != null) { 23 | fillPaint = Paint() 24 | ..color = _currentPoint.isEmpty == true 25 | ? series.emptyPointSettings.color 26 | : ((hasPointColor && _currentPoint.pointColorMapper != null) 27 | ? _currentPoint.pointColorMapper 28 | : color) 29 | ..style = PaintingStyle.fill; 30 | } 31 | } else { 32 | fillPaint = _getLinearGradientPaint(series.gradient, _currentPoint.region, 33 | series._chart._requireInvertedAxis); 34 | } 35 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 36 | _defaultFillColor = fillPaint; 37 | return fillPaint; 38 | } 39 | 40 | /// Gets the border color of the series. 41 | @override 42 | Paint getStrokePaint() { 43 | if (strokeColor != null) { 44 | strokePaint = Paint() 45 | ..color = _currentPoint.isEmpty == true 46 | ? series.emptyPointSettings.borderColor 47 | : strokeColor 48 | ..style = PaintingStyle.stroke 49 | ..strokeWidth = _currentPoint.isEmpty == true 50 | ? series.emptyPointSettings.borderWidth 51 | : strokeWidth; 52 | _defaultStrokeColor = strokePaint; 53 | } 54 | series.borderWidth == 0 55 | ? strokePaint.color = Colors.transparent 56 | : strokePaint.color; 57 | strokePaint.color = strokePaint.color == Colors.transparent 58 | ? strokePaint.color 59 | : strokePaint.color.withOpacity(series.opacity); 60 | return strokePaint; 61 | } 62 | 63 | /// Method to get series tracker fill. 64 | Paint _getTrackerFillPaint() { 65 | final RangeColumnSeries _series = series; 66 | if (color != null) { 67 | _trackerFillPaint = Paint() 68 | ..color = _series.trackColor 69 | ..style = PaintingStyle.fill; 70 | } 71 | return _trackerFillPaint; 72 | } 73 | 74 | /// Method to get series tracker stroke color. 75 | Paint _getTrackerStrokePaint() { 76 | final RangeColumnSeries _series = series; 77 | _trackerStrokePaint = Paint() 78 | ..color = _series.trackBorderColor 79 | ..strokeWidth = _series.trackBorderWidth 80 | ..style = PaintingStyle.stroke; 81 | _series.trackBorderWidth == 0 82 | ? _trackerStrokePaint.color = Colors.transparent 83 | : _trackerStrokePaint.color; 84 | return _trackerStrokePaint; 85 | } 86 | 87 | /// Calculates the rendering bounds of a segment. 88 | @override 89 | void calculateSegmentPoints() { 90 | final RangeColumnSeries _series = series; 91 | _borderRadius = _series.borderRadius; 92 | if (_currentPoint.region != null) { 93 | segmentRect = RRect.fromRectAndCorners( 94 | _currentPoint.region, 95 | bottomLeft: _borderRadius.bottomLeft, 96 | bottomRight: _borderRadius.bottomRight, 97 | topLeft: _borderRadius.topLeft, 98 | topRight: _borderRadius.topRight, 99 | ); 100 | //Tracker rect 101 | if (_series.isTrackVisible) { 102 | _trackRect = RRect.fromRectAndCorners( 103 | _currentPoint.trackerRectRegion, 104 | bottomLeft: _borderRadius.bottomLeft, 105 | bottomRight: _borderRadius.bottomRight, 106 | topLeft: _borderRadius.topLeft, 107 | topRight: _borderRadius.topRight, 108 | ); 109 | } 110 | } 111 | topLeft = _currentPoint.region.topLeft; 112 | topRight = _currentPoint.region.topRight; 113 | bottomLeft = _currentPoint.region.bottomLeft; 114 | bottomRight = _currentPoint.region.bottomRight; 115 | borderWidth = series.borderWidth / 2; 116 | path = Path(); 117 | path.moveTo(topLeft.dx + borderWidth, topLeft.dy + borderWidth); 118 | path.lineTo(topRight.dx - borderWidth, topRight.dy + borderWidth); 119 | path.lineTo(bottomRight.dx - borderWidth, bottomRight.dy - borderWidth); 120 | path.lineTo(bottomLeft.dx + borderWidth, bottomLeft.dy - borderWidth); 121 | path.lineTo(topLeft.dx + borderWidth, topLeft.dy + borderWidth); 122 | path.close(); 123 | } 124 | 125 | /// Draws segment in series bounds. 126 | @override 127 | void onPaint(Canvas canvas) { 128 | final RangeColumnSeries _series = series; 129 | 130 | series.selectionSettings._selectionRenderer._checkWithSelectionState( 131 | series.segments[currentSegmentIndex], series._chart); 132 | 133 | if (_trackerFillPaint != null && _series.isTrackVisible) { 134 | canvas.drawRRect(_trackRect, _trackerFillPaint); 135 | } 136 | 137 | if (_trackerStrokePaint != null && _series.isTrackVisible) { 138 | canvas.drawRRect(_trackRect, _trackerStrokePaint); 139 | } 140 | 141 | if (fillPaint != null) { 142 | series.animationDuration > 0 143 | ? _animateRangeColumn( 144 | canvas, series, fillPaint, segmentRect, animationFactor) 145 | : canvas.drawRRect(segmentRect, fillPaint); 146 | } 147 | if (strokePaint != null) { 148 | if (series.dashArray[0] != 0 && series.dashArray[1] != 0) { 149 | _drawDashedLine(canvas, series, strokePaint, path); 150 | } else { 151 | series.animationDuration > 0 152 | ? _animateRangeColumn( 153 | canvas, series, strokePaint, segmentRect, animationFactor) 154 | : canvas.drawRRect(segmentRect, strokePaint); 155 | } 156 | } 157 | } 158 | 159 | /// Method to set data. 160 | void _setData(List values) { 161 | x1 = values[0]; 162 | low1 = values[1]; 163 | high1 = values[2]; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /lib/src/pyramid_chart/utils/helper.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | bool _isPointInPolygon(List polygon, dynamic point) { 4 | bool p = false; 5 | num i = -1; 6 | final num l = polygon.length; 7 | num j; 8 | for (j = l - 1; ++i < l; j = i) 9 | ((polygon[i].dy <= point.dy && point.dy < polygon[j].dy) || 10 | (polygon[j].dy <= point.dy && point.dy < polygon[i].dy)) && 11 | (point.dx < 12 | (polygon[j].dx - polygon[i].dx) * 13 | (point.dy - polygon[i].dy) / 14 | (polygon[j].dy - polygon[i].dy) + 15 | polygon[i].dx) && 16 | // ignore: unnecessary_statements 17 | (p = !p); 18 | return p; 19 | } 20 | 21 | void _findTemplates(dynamic chart) { 22 | Offset labelLocation; 23 | const num lineLength = 10; 24 | _PointInfo point; 25 | Widget labelWidget; 26 | chart._chartState.templates = <_ChartTemplateInfo>[]; 27 | chart._chartState.dataLabelTemplateRegions = []; 28 | for (int k = 0; k < chart._chartSeries.visibleSeries.length; k++) { 29 | final dynamic series = chart._chartSeries.visibleSeries[k]; 30 | if (series.dataLabelSettings.isVisible && 31 | series.dataLabelSettings.builder != null) { 32 | for (int i = 0; i < series._renderPoints.length; i++) { 33 | point = series._renderPoints[i]; 34 | ChartAlignment labelAlign; 35 | if (point.isVisible) { 36 | labelWidget = series.dataLabelSettings 37 | .builder(series.dataSource[i], point, series, i, k); 38 | if (series.dataLabelSettings.labelPosition == 39 | ChartDataLabelPosition.inside) { 40 | labelLocation = point.symbolLocation; 41 | labelAlign = ChartAlignment.center; 42 | } else { 43 | labelLocation = point.symbolLocation; 44 | labelLocation = Offset( 45 | point.dataLabelPosition == Position.right 46 | ? labelLocation.dx + lineLength + 5 47 | : labelLocation.dx - lineLength - 5, 48 | labelLocation.dy); 49 | labelAlign = point.dataLabelPosition == Position.left 50 | ? ChartAlignment.far 51 | : ChartAlignment.near; 52 | } 53 | chart._chartState.templates.add(_ChartTemplateInfo( 54 | key: GlobalKey(), 55 | templateType: 'DataLabel', 56 | pointIndex: i, 57 | seriesIndex: k, 58 | needMeasure: true, 59 | clipRect: chart._chartState.chartAreaRect, 60 | animationDuration: 500, 61 | widget: labelWidget, 62 | horizontalAlignment: labelAlign, 63 | verticalAlignment: ChartAlignment.center, 64 | location: labelLocation)); 65 | } 66 | } 67 | } 68 | } 69 | } 70 | 71 | void _renderTemplates(dynamic chart) { 72 | if (chart._chartState.templates.isNotEmpty) { 73 | for (int i = 0; i < chart._chartState.templates.length; i++) { 74 | final _ChartTemplateInfo chartTemplateInfo = 75 | chart._chartState.templates[i]; 76 | chartTemplateInfo.animationDuration = !chart._chartState.initialRender 77 | ? 0 78 | : chartTemplateInfo.animationDuration; 79 | } 80 | chart._chartState._chartTemplate = _ChartTemplate( 81 | templates: chart._chartState.templates, 82 | render: chart._chartState.animateCompleted, 83 | chart: chart); 84 | chart._chartState._chartWidgets.add(chart._chartState._chartTemplate); 85 | } 86 | } 87 | 88 | ///To get circular series data label saturation color 89 | Color _getPyramidFunnelColor( 90 | dynamic currentPoint, dynamic series, dynamic chart) { 91 | Color color; 92 | final DataLabelSettings dataLabel = series.dataLabelSettings; 93 | if (currentPoint.renderPosition == ChartDataLabelPosition.inside && 94 | !currentPoint.saturationRegionOutside) { 95 | color = _innerColor(dataLabel.color, currentPoint.fill, chart._chartTheme); 96 | } else { 97 | color = _outerColor( 98 | dataLabel.color, 99 | dataLabel.useSeriesColor 100 | ? currentPoint.fill 101 | : (chart.backgroundColor != null 102 | ? chart._chartTheme.plotAreaBackgroundColor 103 | : null), 104 | chart._chartTheme); 105 | } 106 | 107 | return _getSaturationColor(color); 108 | } 109 | 110 | ///To get inner data label color 111 | Color _innerColor(Color dataLabelColor, Color pointColor, _ChartTheme theme) => 112 | dataLabelColor != null 113 | ? dataLabelColor 114 | : pointColor != null ? pointColor : Colors.black; 115 | 116 | ///To get outer data label color 117 | Color _outerColor( 118 | Color dataLabelColor, Color backgroundColor, _ChartTheme theme) => 119 | dataLabelColor != null 120 | ? dataLabelColor 121 | : backgroundColor != null 122 | ? backgroundColor 123 | : theme.brightness == Brightness.light 124 | ? const Color.fromRGBO(255, 255, 255, 1) 125 | : Colors.black; 126 | 127 | ChartTextStyle _getDataLabelTextStyle( 128 | dynamic series, dynamic point, dynamic chart) { 129 | final DataLabelSettings dataLabel = series.dataLabelSettings; 130 | final Color fontColor = dataLabel.textStyle.color != null 131 | ? dataLabel.textStyle.color 132 | : _getPyramidFunnelColor(point, series, chart); 133 | final ChartTextStyle textStyle = ChartTextStyle( 134 | color: fontColor, 135 | fontSize: dataLabel.textStyle.fontSize, 136 | fontFamily: dataLabel.textStyle.fontFamily, 137 | fontStyle: dataLabel.textStyle.fontStyle, 138 | fontWeight: dataLabel.textStyle.fontWeight); 139 | return textStyle; 140 | } 141 | 142 | bool _isNeedExplode(int pointIndex, dynamic series, dynamic chart) { 143 | bool isNeedExplode = false; 144 | if (series.explode) { 145 | if (chart._chartState.initialRender) { 146 | if (pointIndex == series.explodeIndex) { 147 | chart._chartState.explodedPoints.add(pointIndex); 148 | isNeedExplode = true; 149 | } 150 | } else if (chart._chartState.widgetNeedUpdate || 151 | chart._chartState._isLegendToggled) { 152 | isNeedExplode = chart._chartState.explodedPoints.contains(pointIndex); 153 | } 154 | } 155 | return isNeedExplode; 156 | } 157 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/stacked_bar_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for stacked bar series. 4 | class StackedBarSegment extends ChartSegment { 5 | num x1, y1, x2, y2; 6 | double stackValues; 7 | BorderRadius _borderRadius; 8 | _CartesianChartPoint _currentPoint; 9 | Path path; 10 | RRect rect; 11 | RRect _trackRect; 12 | dynamic topLeft, topRight, bottomRight, bottomLeft, borderWidth; 13 | Paint _trackerFillPaint; 14 | Paint _trackerStrokePaint; 15 | 16 | /// Gets the color of the series. 17 | @override 18 | Paint getFillPaint() { 19 | final bool hasPointColor = series.pointColorMapper != null ? true : false; 20 | 21 | /// Get and set the paint options for column series. 22 | if (series.gradient == null) { 23 | if (color != null) { 24 | fillPaint = Paint() 25 | ..color = _currentPoint.isEmpty == true 26 | ? series.emptyPointSettings.color 27 | : ((hasPointColor && _currentPoint.pointColorMapper != null) 28 | ? _currentPoint.pointColorMapper 29 | : color) 30 | ..style = PaintingStyle.fill; 31 | } 32 | } else { 33 | fillPaint = _getLinearGradientPaint(series.gradient, _currentPoint.region, 34 | series._chart._requireInvertedAxis); 35 | } 36 | _defaultFillColor = fillPaint; 37 | if (fillPaint.color != Colors.transparent) 38 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 39 | return fillPaint; 40 | } 41 | 42 | /// Gets the border color of the series. 43 | @override 44 | Paint getStrokePaint() { 45 | if (strokeColor != null) { 46 | strokePaint = Paint() 47 | ..color = _currentPoint.isEmpty == true 48 | ? series.emptyPointSettings.borderColor 49 | : strokeColor 50 | ..style = PaintingStyle.stroke 51 | ..strokeWidth = _currentPoint.isEmpty == true 52 | ? series.emptyPointSettings.borderWidth 53 | : strokeWidth; 54 | _defaultStrokeColor = strokePaint; 55 | } 56 | series.borderWidth == 0 57 | ? strokePaint.color = Colors.transparent 58 | : strokePaint.color; 59 | strokePaint.color = strokePaint.color == Colors.transparent 60 | ? strokePaint.color 61 | : strokePaint.color.withOpacity(series.opacity); 62 | return strokePaint; 63 | } 64 | 65 | /// Method to get series tracker fill. 66 | Paint _getTrackerFillPaint() { 67 | final StackedBarSeries columnSeries = series; 68 | if (color != null) { 69 | _trackerFillPaint = Paint() 70 | ..color = columnSeries.trackColor 71 | ..style = PaintingStyle.fill; 72 | } 73 | return _trackerFillPaint; 74 | } 75 | 76 | /// Method to get series tracker stroke color. 77 | Paint _getTrackerStrokePaint() { 78 | final StackedBarSeries columnSeries = series; 79 | _trackerStrokePaint = Paint() 80 | ..color = columnSeries.trackBorderColor 81 | ..strokeWidth = columnSeries.trackBorderWidth 82 | ..style = PaintingStyle.stroke; 83 | columnSeries.trackBorderWidth == 0 84 | ? _trackerStrokePaint.color = Colors.transparent 85 | : _trackerStrokePaint.color; 86 | return _trackerStrokePaint; 87 | } 88 | 89 | /// Calculates the rendering bounds of a segment. 90 | @override 91 | void calculateSegmentPoints() { 92 | final StackedBarSeries stackedBarSeries = series; 93 | _borderRadius = stackedBarSeries.borderRadius; 94 | if (_currentPoint.region != null) { 95 | segmentRect = RRect.fromRectAndCorners( 96 | _currentPoint.region, 97 | bottomLeft: _borderRadius.bottomLeft, 98 | bottomRight: _borderRadius.bottomRight, 99 | topLeft: _borderRadius.topLeft, 100 | topRight: _borderRadius.topRight, 101 | ); 102 | if (stackedBarSeries.isTrackVisible) { 103 | _trackRect = RRect.fromRectAndCorners( 104 | _currentPoint.trackerRectRegion, 105 | bottomLeft: _borderRadius.bottomLeft, 106 | bottomRight: _borderRadius.bottomRight, 107 | topLeft: _borderRadius.topLeft, 108 | topRight: _borderRadius.topRight, 109 | ); 110 | } 111 | } 112 | topLeft = _currentPoint.region.topLeft; 113 | topRight = _currentPoint.region.topRight; 114 | bottomLeft = _currentPoint.region.bottomLeft; 115 | bottomRight = _currentPoint.region.bottomRight; 116 | borderWidth = series.borderWidth / 2; 117 | path = Path(); 118 | path.moveTo(topLeft.dx + borderWidth, topLeft.dy + borderWidth); 119 | path.lineTo(topRight.dx - borderWidth, topRight.dy + borderWidth); 120 | path.lineTo(bottomRight.dx - borderWidth, bottomRight.dy - borderWidth); 121 | path.lineTo(bottomLeft.dx + borderWidth, bottomLeft.dy - borderWidth); 122 | path.lineTo(topLeft.dx + borderWidth, topLeft.dy + borderWidth); 123 | path.close(); 124 | } 125 | 126 | /// Draws segment in series bounds. 127 | @override 128 | void onPaint(Canvas canvas) { 129 | final StackedBarSeries stackedBarSeries = series; 130 | 131 | if (_trackerFillPaint != null && stackedBarSeries.isTrackVisible) { 132 | canvas.drawRRect(_trackRect, _trackerFillPaint); 133 | } 134 | 135 | if (_trackerStrokePaint != null && stackedBarSeries.isTrackVisible) { 136 | canvas.drawRRect(_trackRect, _trackerStrokePaint); 137 | } 138 | 139 | if (fillPaint != null) { 140 | series.animationDuration > 0 141 | ? _animateStackedRectSeries(canvas, segmentRect, fillPaint, series, 142 | animationFactor, _currentPoint, series._chart) 143 | : canvas.drawRRect(segmentRect, fillPaint); 144 | } 145 | if (strokePaint != null) { 146 | if (series.dashArray != null) { 147 | series.animationDuration > 0 148 | ? _animateStackedRectSeries(canvas, segmentRect, strokePaint, 149 | series, animationFactor, _currentPoint, series._chart) 150 | : _drawDashedLine(canvas, series, strokePaint, path); 151 | } else 152 | series.animationDuration > 0 153 | ? _animateStackedRectSeries(canvas, segmentRect, fillPaint, series, 154 | animationFactor, _currentPoint, series._chart) 155 | : canvas.drawPath(path, strokePaint); 156 | } 157 | } 158 | 159 | /// Method to set data. 160 | void _setData(List values) { 161 | x1 = values[0]; 162 | y1 = values[1]; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/stacked_column_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for stacked column series. 4 | class StackedColumnSegment extends ChartSegment { 5 | num x1, y1, x2, y2; 6 | double stackValues; 7 | BorderRadius _borderRadius; 8 | _CartesianChartPoint _currentPoint; 9 | Path path; 10 | RRect _trackRect; 11 | dynamic topLeft, topRight, bottomRight, bottomLeft, borderWidth; 12 | Paint _trackerFillPaint; 13 | Paint _trackerStrokePaint; 14 | 15 | /// Gets the color of the series. 16 | @override 17 | Paint getFillPaint() { 18 | final bool hasPointColor = series.pointColorMapper != null ? true : false; 19 | 20 | /// Get and set the paint options for column series. 21 | if (series.gradient == null) { 22 | if (color != null) { 23 | fillPaint = Paint() 24 | ..color = _currentPoint.isEmpty == true 25 | ? series.emptyPointSettings.color 26 | : ((hasPointColor && _currentPoint.pointColorMapper != null) 27 | ? _currentPoint.pointColorMapper 28 | : color) 29 | ..style = PaintingStyle.fill; 30 | } 31 | } else { 32 | fillPaint = _getLinearGradientPaint(series.gradient, _currentPoint.region, 33 | series._chart._requireInvertedAxis); 34 | } 35 | if (fillPaint.color != Colors.transparent) 36 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 37 | _defaultFillColor = fillPaint; 38 | return fillPaint; 39 | } 40 | 41 | /// Gets the border color of the series. 42 | @override 43 | Paint getStrokePaint() { 44 | if (strokeColor != null) { 45 | strokePaint = Paint() 46 | ..color = _currentPoint.isEmpty == true 47 | ? series.emptyPointSettings.borderColor 48 | : strokeColor 49 | ..style = PaintingStyle.stroke 50 | ..strokeWidth = _currentPoint.isEmpty == true 51 | ? series.emptyPointSettings.borderWidth 52 | : strokeWidth; 53 | _defaultStrokeColor = strokePaint; 54 | } 55 | series.borderWidth == 0 56 | ? strokePaint.color = Colors.transparent 57 | : strokePaint.color; 58 | strokePaint.color = strokePaint.color == Colors.transparent 59 | ? strokePaint.color 60 | : strokePaint.color.withOpacity(series.opacity); 61 | return strokePaint; 62 | } 63 | 64 | /// Method to get series tracker fill. 65 | Paint _getTrackerFillPaint() { 66 | final StackedColumnSeries columnSeries = series; 67 | if (color != null) { 68 | _trackerFillPaint = Paint() 69 | ..color = columnSeries.trackColor 70 | ..style = PaintingStyle.fill; 71 | } 72 | return _trackerFillPaint; 73 | } 74 | 75 | /// Method to get series tracker stroke color. 76 | Paint _getTrackerStrokePaint() { 77 | final StackedColumnSeries columnSeries = series; 78 | _trackerStrokePaint = Paint() 79 | ..color = columnSeries.trackBorderColor 80 | ..strokeWidth = columnSeries.trackBorderWidth 81 | ..style = PaintingStyle.stroke; 82 | columnSeries.trackBorderWidth == 0 83 | ? _trackerStrokePaint.color = Colors.transparent 84 | : _trackerStrokePaint.color; 85 | return _trackerStrokePaint; 86 | } 87 | 88 | /// Calculates the rendering bounds of a segment. 89 | @override 90 | void calculateSegmentPoints() { 91 | final StackedColumnSeries stackedColumnSeries = series; 92 | _borderRadius = stackedColumnSeries.borderRadius; 93 | if (_currentPoint.region != null) { 94 | segmentRect = RRect.fromRectAndCorners( 95 | _currentPoint.region, 96 | bottomLeft: _borderRadius.bottomLeft, 97 | bottomRight: _borderRadius.bottomRight, 98 | topLeft: _borderRadius.topLeft, 99 | topRight: _borderRadius.topRight, 100 | ); 101 | if (stackedColumnSeries.isTrackVisible) { 102 | _trackRect = RRect.fromRectAndCorners( 103 | _currentPoint.trackerRectRegion, 104 | bottomLeft: _borderRadius.bottomLeft, 105 | bottomRight: _borderRadius.bottomRight, 106 | topLeft: _borderRadius.topLeft, 107 | topRight: _borderRadius.topRight, 108 | ); 109 | } 110 | } 111 | 112 | topLeft = _currentPoint.region.topLeft; 113 | topRight = _currentPoint.region.topRight; 114 | bottomLeft = _currentPoint.region.bottomLeft; 115 | bottomRight = _currentPoint.region.bottomRight; 116 | borderWidth = series.borderWidth / 2; 117 | path = Path(); 118 | path.moveTo(topLeft.dx + borderWidth, topLeft.dy + borderWidth); 119 | path.lineTo(topRight.dx - borderWidth, topRight.dy + borderWidth); 120 | path.lineTo(bottomRight.dx - borderWidth, bottomRight.dy - borderWidth); 121 | path.lineTo(bottomLeft.dx + borderWidth, bottomLeft.dy - borderWidth); 122 | path.lineTo(topLeft.dx + borderWidth, topLeft.dy + borderWidth); 123 | path.close(); 124 | } 125 | 126 | /// Draws segment in series bounds. 127 | @override 128 | void onPaint(Canvas canvas) { 129 | final StackedColumnSeries stackedColumnSeries = series; 130 | 131 | if (_trackerFillPaint != null && stackedColumnSeries.isTrackVisible) { 132 | canvas.drawRRect(_trackRect, _trackerFillPaint); 133 | } 134 | 135 | if (_trackerStrokePaint != null && stackedColumnSeries.isTrackVisible) { 136 | canvas.drawRRect(_trackRect, _trackerStrokePaint); 137 | } 138 | 139 | if (fillPaint != null) { 140 | series.animationDuration > 0 141 | ? _animateStackedRectSeries(canvas, segmentRect, fillPaint, series, 142 | animationFactor, _currentPoint, series._chart) 143 | : canvas.drawRRect(segmentRect, fillPaint); 144 | } 145 | if (strokePaint != null) { 146 | if (series.dashArray[0] != 0 && series.dashArray[1] != 0) { 147 | series.animationDuration > 0 148 | ? _animateStackedRectSeries(canvas, segmentRect, strokePaint, 149 | series, animationFactor, _currentPoint, series._chart) 150 | : _drawDashedLine(canvas, series, strokePaint, path); 151 | } else 152 | series.animationDuration > 0 153 | ? _animateStackedRectSeries(canvas, segmentRect, strokePaint, 154 | series, animationFactor, _currentPoint, series._chart) 155 | : canvas.drawPath(path, strokePaint); 156 | } 157 | } 158 | 159 | /// Method to set data. 160 | void _setData(List values) { 161 | x1 = values[0]; 162 | y1 = values[1]; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/area_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for area series. 4 | class AreaSegment extends ChartSegment { 5 | Path _path, _strokePath; 6 | Rect _pathRect; 7 | 8 | ///Area series 9 | XyDataSeries series; 10 | 11 | /// Gets the color of the series. 12 | @override 13 | Paint getFillPaint() { 14 | fillPaint = Paint(); 15 | if (series.gradient == null) { 16 | if (color != null) { 17 | fillPaint.color = color; 18 | fillPaint.style = PaintingStyle.fill; 19 | } 20 | } else { 21 | fillPaint = (_pathRect != null) 22 | ? _getLinearGradientPaint( 23 | series.gradient, _pathRect, series._chart._requireInvertedAxis) 24 | : fillPaint; 25 | } 26 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 27 | _defaultFillColor = fillPaint; 28 | return fillPaint; 29 | } 30 | 31 | /// Gets the border color of the series. 32 | @override 33 | Paint getStrokePaint() { 34 | final Paint strokePaint = Paint(); 35 | if (strokeColor != null) { 36 | strokePaint 37 | ..color = series.borderColor.withOpacity(series.opacity) 38 | ..style = PaintingStyle.stroke 39 | ..strokeWidth = series.borderWidth; 40 | series.borderWidth == 0 41 | ? strokePaint.color = Colors.transparent 42 | : strokePaint.color; 43 | } 44 | strokePaint.strokeCap = StrokeCap.round; 45 | _defaultStrokeColor = strokePaint; 46 | return strokePaint; 47 | } 48 | 49 | /// Calculates the rendering bounds of a segment. 50 | @override 51 | void calculateSegmentPoints() {} 52 | 53 | /// Draws segment in series bounds. 54 | @override 55 | void onPaint(Canvas canvas) { 56 | final Rect rect = series._chart._chartAxis._axisClipRect; 57 | _CartesianChartPoint prevPoint; 58 | _ChartLocation currentPoint, originPoint, oldPoint; 59 | final ChartAxis xAxis = series._xAxis; 60 | final ChartAxis yAxis = series._yAxis; 61 | _CartesianChartPoint point, _point; 62 | final AreaSeries areaSeries = series; 63 | _path = Path(); 64 | _strokePath = Path(); 65 | for (int pointIndex = 0; 66 | pointIndex < series._dataPoints.length; 67 | pointIndex++) { 68 | point = series._dataPoints[pointIndex]; 69 | if (point.isVisible || point.isDrop) { 70 | _point = (series.animationDuration > 0 && 71 | series._chart._chartState.widgetNeedUpdate && 72 | !series._chart._chartState._isLegendToggled && 73 | series._chart._chartState.prevWidgetSeries != null && 74 | series._chart._chartState.prevWidgetSeries.isNotEmpty && 75 | oldSeries != null && 76 | oldSeries.segments.isNotEmpty && 77 | oldSeries.segments[0] is AreaSegment && 78 | series._chart._chartState.prevWidgetSeries.length - 1 >= 79 | _seriesIndex && 80 | oldSeries._dataPoints.length - 1 >= pointIndex) 81 | ? oldSeries._dataPoints[pointIndex] 82 | : null; 83 | oldPoint = _point != null 84 | ? _calculatePoint( 85 | _point.xValue, 86 | _point.yValue, 87 | oldSeries._xAxis, 88 | oldSeries._yAxis, 89 | series._chart._requireInvertedAxis, 90 | oldSeries, 91 | rect) 92 | : null; 93 | currentPoint = _calculatePoint(point.xValue, point.yValue, xAxis, yAxis, 94 | series._chart._requireInvertedAxis, series, rect); 95 | originPoint = _calculatePoint( 96 | point.xValue, 97 | math_lib.max(yAxis._visibleRange.minimum, 0), 98 | xAxis, 99 | yAxis, 100 | series._chart._requireInvertedAxis, 101 | series, 102 | rect); 103 | num x = currentPoint.x; 104 | num y = currentPoint.y; 105 | if (oldPoint != null) { 106 | if (series._chart.isTransposed) { 107 | x = _getValue(animationFactor, x, oldPoint.x, currentPoint.x); 108 | } else { 109 | y = _getValue(animationFactor, y, oldPoint.y, currentPoint.y); 110 | } 111 | } 112 | if (prevPoint == null || 113 | series._dataPoints[pointIndex - 1].isGap == true || 114 | (series._dataPoints[pointIndex].isGap == true) || 115 | (series._dataPoints[pointIndex - 1].isVisible == false && 116 | series.emptyPointSettings.mode == EmptyPointMode.gap)) { 117 | _path.moveTo(originPoint.x, originPoint.y); 118 | if (areaSeries.borderMode == AreaBorderMode.excludeBottom) { 119 | if (series._dataPoints[pointIndex].isGap != true) { 120 | _strokePath.moveTo(originPoint.x, originPoint.y); 121 | _strokePath.lineTo(x, y); 122 | } 123 | } else if (areaSeries.borderMode == AreaBorderMode.all) { 124 | if (series._dataPoints[pointIndex].isGap != true) { 125 | _strokePath.moveTo(originPoint.x, originPoint.y); 126 | _strokePath.lineTo(x, y); 127 | } 128 | } else if (areaSeries.borderMode == AreaBorderMode.top) { 129 | _strokePath.moveTo(x, y); 130 | } 131 | _path.lineTo(x, y); 132 | } else if (pointIndex == series._dataPoints.length - 1 || 133 | series._dataPoints[pointIndex + 1].isGap == true) { 134 | _strokePath.lineTo(x, y); 135 | if (areaSeries.borderMode == AreaBorderMode.excludeBottom) 136 | _strokePath.lineTo(originPoint.x, originPoint.y); 137 | else if (areaSeries.borderMode == AreaBorderMode.all) { 138 | _strokePath.lineTo(originPoint.x, originPoint.y); 139 | _strokePath.close(); 140 | } 141 | _path.lineTo(x, y); 142 | _path.lineTo(originPoint.x, originPoint.y); 143 | } else { 144 | _strokePath.lineTo(x, y); 145 | _path.lineTo(x, y); 146 | } 147 | prevPoint = point; 148 | } 149 | } 150 | _pathRect = _path.getBounds(); 151 | series.selectionSettings._selectionRenderer 152 | ._checkWithSelectionState(series.segments[0], series._chart); 153 | canvas.drawPath( 154 | _path, (series.gradient == null) ? fillPaint : getFillPaint()); 155 | 156 | if (strokePaint.color != Colors.transparent) 157 | _drawDashedLine(canvas, series, strokePaint, _strokePath); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/stepline_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the step line series. 4 | class StepLineSeries extends XyDataSeries { 5 | StepLineSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | String xAxisName, 13 | String yAxisName, 14 | String name, 15 | Color color, 16 | double width, 17 | LinearGradient gradient, 18 | MarkerSettings markerSettings, 19 | EmptyPointSettings emptyPointSettings, 20 | DataLabelSettings dataLabelSettings, 21 | bool isVisible, 22 | bool enableTooltip, 23 | List dashArray, 24 | double animationDuration, 25 | SelectionSettings selectionSettings, 26 | SortingOrder sortingOrder, 27 | bool isVisibleInLegend, 28 | LegendIconType legendIconType, 29 | String legendItemText, 30 | double opacity}) 31 | : super( 32 | xValueMapper: xValueMapper, 33 | yValueMapper: yValueMapper, 34 | sortFieldValueMapper: sortFieldValueMapper, 35 | pointColorMapper: pointColorMapper, 36 | dataLabelMapper: dataLabelMapper, 37 | dataSource: dataSource, 38 | xAxisName: xAxisName, 39 | yAxisName: yAxisName, 40 | name: name, 41 | color: color, 42 | width: width ?? 2, 43 | gradient: gradient, 44 | markerSettings: markerSettings, 45 | dataLabelSettings: dataLabelSettings, 46 | emptyPointSettings: emptyPointSettings, 47 | enableTooltip: enableTooltip, 48 | dashArray: dashArray, 49 | isVisible: isVisible, 50 | animationDuration: animationDuration, 51 | selectionSettings: selectionSettings, 52 | legendItemText: legendItemText, 53 | isVisibleInLegend: isVisibleInLegend, 54 | legendIconType: legendIconType, 55 | sortingOrder: sortingOrder, 56 | opacity: opacity); 57 | 58 | /// Creates a segment for a data point in the series. 59 | @override 60 | ChartSegment createSegment() => StepLineSegment(); 61 | 62 | /// Creates a collection of segments for the points in the series. 63 | @override 64 | void createSegments() { 65 | List values = []; 66 | int segmentIndex = 0; 67 | for (int i = 0; i < _dataPoints.length; i++) { 68 | if (_dataPoints.length > 1) { 69 | if (values != null && values.isNotEmpty && values.length > 2) { 70 | values[0] = values[2]; 71 | values[1] = values[3]; 72 | values.removeRange(2, 4); 73 | } 74 | if (_dataPoints[i].isVisible) { 75 | if (_dataPoints[i].isGap != true) { 76 | if (values.isEmpty || 77 | (values.isNotEmpty && 78 | !(values[0] == _dataPoints[i].xValue && 79 | values[1] == _dataPoints[i].yValue))) { 80 | values.add(_dataPoints[i].xValue); 81 | values.add(_dataPoints[i].yValue); 82 | } 83 | } 84 | if ((i == 0 || _dataPoints[i - 1].isGap == true) && 85 | _dataPoints[i].isGap != true && 86 | _dataPoints[i + 1].isGap != true && 87 | _dataPoints[i + 1].isVisible) { 88 | values.add(_dataPoints[i + 1].xValue); 89 | values.add(_dataPoints[i + 1].yValue); 90 | } else if (_dataPoints[i].isGap == true) { 91 | values = []; 92 | } 93 | } 94 | } 95 | 96 | if (values != null && values.isNotEmpty && values.length > 2) { 97 | _createSegment(values, _dataPoints[i], segmentIndex, 98 | _dataPoints[segmentIndex].pointColorMapper); 99 | segmentIndex++; 100 | } 101 | } 102 | } 103 | 104 | /// StepLine segment is created here 105 | void _createSegment( 106 | List values, 107 | _CartesianChartPoint currentPoint, 108 | int segmentIndex, 109 | Color pointColorMapper) { 110 | final StepLineSegment segment = createSegment(); 111 | _isRectSeries = false; 112 | if (segment != null) { 113 | segment.currentSegmentIndex = segmentIndex; 114 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 115 | segment.series = this; 116 | segment._presentPoint = currentPoint; 117 | if (_chart._chartState.widgetNeedUpdate && 118 | _xAxis._zoomFactor == 1 && 119 | _yAxis._zoomFactor == 1 && 120 | _chart._chartState.prevWidgetSeries != null && 121 | _chart._chartState.prevWidgetSeries.isNotEmpty && 122 | _chart._chartState.prevWidgetSeries.length - 1 >= 123 | segment._seriesIndex && 124 | _chart._chartState.prevWidgetSeries[segment._seriesIndex] 125 | ._seriesName == 126 | segment.series._seriesName) { 127 | segment.oldSeries = 128 | _chart._chartState.prevWidgetSeries[segment._seriesIndex]; 129 | } 130 | segment._pointColorMapper = pointColorMapper; 131 | segment.setData(values); 132 | segment.calculateSegmentPoints(); 133 | customizeSegment(segment); 134 | segment.strokePaint = segment.getStrokePaint(); 135 | segment.fillPaint = segment.getFillPaint(); 136 | segments.add(segment); 137 | } 138 | } 139 | 140 | /// Changes the series color, border color, and border width. 141 | @override 142 | void customizeSegment(ChartSegment segment) { 143 | segment.color = segment.series._seriesColor; 144 | segment.strokeColor = segment.series._seriesColor; 145 | segment.strokeWidth = segment.series.width; 146 | } 147 | 148 | ///Draws marker with different shape and color of the appropriate data point in the series. 149 | @override 150 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 151 | Paint strokePaint, double pointX, double pointY) { 152 | canvas.drawPath(_markerShapes[index], strokePaint); 153 | canvas.drawPath(_markerShapes[index], fillPaint); 154 | } 155 | 156 | /// Draws data label text of the appropriate data point in a series. 157 | @override 158 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 159 | double pointY, int angle, ChartTextStyle style) => 160 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 161 | } 162 | -------------------------------------------------------------------------------- /lib/charts.dart: -------------------------------------------------------------------------------- 1 | library charts; 2 | 3 | import 'dart:async'; 4 | import 'dart:math' as math_lib; 5 | import 'dart:math' as math; 6 | import 'dart:math'; 7 | import 'dart:typed_data'; 8 | import 'dart:ui'; 9 | import 'dart:ui' as dart_ui; 10 | import 'package:flutter/material.dart'; 11 | import 'package:flutter/gestures.dart'; 12 | import 'package:flutter/rendering.dart'; 13 | import 'package:flutter/scheduler.dart'; 14 | import 'package:intl/intl.dart' show DateFormat; 15 | import 'package:intl/intl.dart' show NumberFormat; 16 | import 'package:flutter/services.dart' show rootBundle; 17 | import 'package:vector_math/vector_math.dart' as vector; 18 | import 'package:syncfusion_flutter_core/core.dart'; 19 | 20 | // export circular library 21 | part './src/circular_chart/base/circular_base.dart'; 22 | part './src/circular_chart/base/series_base.dart'; 23 | part './src/circular_chart/renderer/circular-series.dart'; 24 | part './src/circular_chart/renderer/common.dart'; 25 | part './src/circular_chart/renderer/data_label_renderer.dart'; 26 | part './src/circular_chart/renderer/doughnut_series.dart'; 27 | part './src/circular_chart/renderer/pie_series.dart'; 28 | part './src/circular_chart/renderer/radial_bar_series.dart'; 29 | part './src/circular_chart/utils/enum.dart'; 30 | part './src/circular_chart/utils/helper.dart'; 31 | 32 | // export pyramid library 33 | part './src/pyramid_chart/base/pyramid_base.dart'; 34 | part './src/pyramid_chart/base/series_base.dart'; 35 | part './src/pyramid_chart/renderer/pyramid_series.dart'; 36 | part './src/pyramid_chart/utils/common.dart'; 37 | part './src/pyramid_chart/utils/helper.dart'; 38 | part './src/pyramid_chart/renderer/data_label_renderer.dart'; 39 | 40 | // export funnel library 41 | part './src/funnel_chart/base/funnel_base.dart'; 42 | part './src/funnel_chart/base/series_base.dart'; 43 | part './src/funnel_chart/renderer/funnel_series.dart'; 44 | part './src/funnel_chart/renderer/data_label_renderer.dart'; 45 | 46 | // export chart library 47 | part './src/chart/annotation/annotation_settings.dart'; 48 | part './src/chart/axis/axis.dart'; 49 | part './src/chart/axis/axis_panel.dart'; 50 | part './src/chart/axis/axis_renderer.dart'; 51 | part './src/chart/axis/category_axis.dart'; 52 | part './src/chart/axis/datetime_axis.dart'; 53 | part './src/chart/axis/logarithmic_axis.dart'; 54 | part './src/chart/axis/numeric_axis.dart'; 55 | part './src/chart/axis/plotband.dart'; 56 | part './src/chart/base/chart_base.dart'; 57 | part './src/chart/base/series_base.dart'; 58 | part './src/chart/chart_behavior/chart_behavior.dart'; 59 | part './src/chart/chart_behavior/selection_behavior.dart'; 60 | part './src/chart/chart_behavior/zoom_behavior.dart'; 61 | part './src/chart/chart_segment/area_segment.dart'; 62 | part './src/chart/chart_segment/stacked_area_segment.dart'; 63 | part './src/chart/chart_segment/bar_segment.dart'; 64 | part './src/chart/chart_segment/bubble_segment.dart'; 65 | part './src/chart/chart_segment/chart_segment.dart'; 66 | part './src/chart/chart_segment/column_segment.dart'; 67 | part './src/chart/chart_segment/fastline_segment.dart'; 68 | part './src/chart/chart_segment/line_segment.dart'; 69 | part './src/chart/chart_segment/stacked_line_segment.dart'; 70 | part './src/chart/chart_segment/range_column_segment.dart'; 71 | part './src/chart/chart_segment/scatter_segment.dart'; 72 | part './src/chart/chart_segment/spline_segment.dart'; 73 | part './src/chart/chart_series/stacked_series_base.dart'; 74 | part './src/chart/chart_segment/stacked_bar_segment.dart'; 75 | part './src/chart/chart_segment/stacked_column_segment.dart'; 76 | part './src/chart/chart_segment/stepline_segment.dart'; 77 | part './src/chart/chart_series/area_series.dart'; 78 | part './src/chart/chart_series/stacked_area_series.dart'; 79 | part './src/chart/chart_series/bar_series.dart'; 80 | part './src/chart/chart_series/bubble_series.dart'; 81 | part './src/chart/chart_series/column_series.dart'; 82 | part './src/chart/chart_series/fastline_series.dart'; 83 | part './src/chart/chart_series/line_series.dart'; 84 | part './src/chart/chart_series/stacked_line_series.dart'; 85 | part './src/chart/chart_series/range_column_series.dart'; 86 | part './src/chart/chart_series/scatter_series.dart'; 87 | part './src/chart/chart_series/stacked_bar_series.dart'; 88 | part './src/chart/chart_series/stacked_column_series.dart'; 89 | part './src/chart/chart_series/series.dart'; 90 | part './src/chart/chart_series/spline_series.dart'; 91 | part './src/chart/chart_series/stepline_series.dart'; 92 | part './src/chart/chart_series/xy_data_series.dart'; 93 | part './src/chart/common/common.dart'; 94 | part './src/chart/common/data_label.dart'; 95 | part './src/chart/common/data_label_renderer.dart'; 96 | part './src/chart/common/marker.dart'; 97 | part './src/chart/common/renderer.dart'; 98 | part './src/chart/series_painter/area_painter.dart'; 99 | part './src/chart/series_painter/stacked_area_painter.dart'; 100 | part './src/chart/series_painter/bar_painter.dart'; 101 | part './src/chart/series_painter/bubble_painter.dart'; 102 | part './src/chart/series_painter/column_painter.dart'; 103 | part './src/chart/series_painter/fastline_painter.dart'; 104 | part './src/chart/series_painter/line_painter.dart'; 105 | part './src/chart/series_painter/stacked_line_painter.dart'; 106 | part './src/chart/series_painter/range_column_painter.dart'; 107 | part './src/chart/series_painter/scatter_painter.dart'; 108 | part './src/chart/series_painter/spline_painter.dart'; 109 | part './src/chart/series_painter/stacked_bar_painter.dart'; 110 | part './src/chart/series_painter/stacked_column_painter.dart'; 111 | part './src/chart/series_painter/stepline_painter.dart'; 112 | part './src/chart/themes/chart_theme.dart'; 113 | part './src/chart/user_interaction/crosshair.dart'; 114 | part './src/chart/user_interaction/crosshair_painter.dart'; 115 | part './src/chart/user_interaction/selection_renderer.dart'; 116 | part './src/chart/user_interaction/tooltip_painter.dart'; 117 | part './src/chart/user_interaction/tooltip_template.dart'; 118 | part './src/chart/user_interaction/trackball.dart'; 119 | part './src/chart/user_interaction/trackball_painter.dart'; 120 | part './src/chart/user_interaction/zooming_painter.dart'; 121 | part './src/chart/user_interaction/zooming_panning.dart'; 122 | part './src/chart/utils/enum.dart'; 123 | part './src/chart/utils/helper.dart'; 124 | 125 | // export common library 126 | part './src/common/common.dart'; 127 | part './src/common/event_args.dart'; 128 | part './src/common/legend/legend.dart'; 129 | part './src/common/legend/renderer.dart'; 130 | part './src/common/series/chart_series.dart'; 131 | part './src/common/template/rendering.dart'; 132 | part './src/common/user_interaction/selection.dart'; 133 | part './src/common/user_interaction/tooltip.dart'; 134 | part './src/common/utils/enum.dart'; 135 | part './src/common/utils/helper.dart'; 136 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/line_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the line series. 4 | class LineSeries extends XyDataSeries { 5 | LineSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | String xAxisName, 13 | String yAxisName, 14 | Color color, 15 | double width, 16 | LinearGradient gradient, 17 | MarkerSettings markerSettings, 18 | EmptyPointSettings emptyPointSettings, 19 | DataLabelSettings dataLabelSettings, 20 | bool isVisible, 21 | String name, 22 | bool enableTooltip, 23 | List dashArray, 24 | double animationDuration, 25 | SelectionSettings selectionSettings, 26 | bool isVisibleInLegend, 27 | LegendIconType legendIconType, 28 | SortingOrder sortingOrder, 29 | String legendItemText, 30 | double opacity, 31 | List initialSelectedDataIndexes}) 32 | : super( 33 | name: name, 34 | xValueMapper: xValueMapper, 35 | yValueMapper: yValueMapper, 36 | sortFieldValueMapper: sortFieldValueMapper, 37 | pointColorMapper: pointColorMapper, 38 | dataLabelMapper: dataLabelMapper, 39 | dataSource: dataSource, 40 | xAxisName: xAxisName, 41 | yAxisName: yAxisName, 42 | color: color, 43 | width: width ?? 2, 44 | gradient: gradient, 45 | markerSettings: markerSettings, 46 | emptyPointSettings: emptyPointSettings, 47 | dataLabelSettings: dataLabelSettings, 48 | isVisible: isVisible, 49 | enableTooltip: enableTooltip, 50 | dashArray: dashArray, 51 | animationDuration: animationDuration, 52 | selectionSettings: selectionSettings, 53 | legendItemText: legendItemText, 54 | isVisibleInLegend: isVisibleInLegend, 55 | legendIconType: legendIconType, 56 | sortingOrder: sortingOrder, 57 | opacity: opacity, 58 | initialSelectedDataIndexes: initialSelectedDataIndexes); 59 | 60 | /// Creates a segment for a data point in the series. 61 | @override 62 | ChartSegment createSegment() => LineSegment(); 63 | 64 | /// Creates a collection of segments for the points in the series. 65 | @override 66 | void createSegments() { 67 | List values = []; 68 | int segmentIndex = 0; 69 | for (int i = 0; i < _dataPoints.length; i++) { 70 | if (_dataPoints.length > 1) { 71 | if (values != null && values.isNotEmpty && values.length > 2) { 72 | values[0] = values[2]; 73 | values[1] = values[3]; 74 | values.removeRange(2, 4); 75 | } 76 | if (_dataPoints[i].isVisible) { 77 | if (_dataPoints[i].isGap != true) { 78 | if (values.isEmpty || 79 | (values.isNotEmpty && 80 | !(values[0] == _dataPoints[i].xValue && 81 | values[1] == _dataPoints[i].yValue))) { 82 | values.add(_dataPoints[i].xValue); 83 | values.add(_dataPoints[i].yValue); 84 | } 85 | } 86 | if ((i == 0 || _dataPoints[i - 1].isGap == true) && 87 | _dataPoints[i].isGap != true && 88 | i != _dataPoints.length - 1 && 89 | _dataPoints[i + 1].isGap != true && 90 | _dataPoints[i + 1].isVisible) { 91 | values.add(_dataPoints[i + 1].xValue); 92 | values.add(_dataPoints[i + 1].yValue); 93 | } else if (_dataPoints[i].isGap == true) { 94 | values = []; 95 | } 96 | } 97 | } 98 | 99 | if (values != null && values.isNotEmpty && values.length > 2) { 100 | _createSegment(values, _dataPoints[i], segmentIndex, 101 | _dataPoints[segmentIndex].pointColorMapper); 102 | segmentIndex++; 103 | } 104 | } 105 | } 106 | 107 | /// Line segement is created here 108 | void _createSegment( 109 | List values, 110 | _CartesianChartPoint currentPoint, 111 | int segmentIndex, 112 | Color pointColorMapper) { 113 | final LineSegment segment = createSegment(); 114 | _isRectSeries = false; 115 | if (segment != null) { 116 | segment.currentSegmentIndex = segmentIndex; 117 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 118 | segment.series = this; 119 | segment._currentPoint = currentPoint; 120 | if (_chart._chartState.widgetNeedUpdate && 121 | _xAxis._zoomFactor == 1 && 122 | _yAxis._zoomFactor == 1 && 123 | _chart._chartState.prevWidgetSeries != null && 124 | _chart._chartState.prevWidgetSeries.isNotEmpty && 125 | _chart._chartState.prevWidgetSeries.length - 1 >= 126 | segment._seriesIndex && 127 | _chart._chartState.prevWidgetSeries[segment._seriesIndex] 128 | ._seriesName == 129 | segment.series._seriesName) { 130 | segment.oldSeries = 131 | _chart._chartState.prevWidgetSeries[segment._seriesIndex]; 132 | } 133 | segment._pointColorMapper = pointColorMapper; 134 | segment._setData(values); 135 | segment.calculateSegmentPoints(); 136 | customizeSegment(segment); 137 | segment.strokePaint = segment.getStrokePaint(); 138 | segment.fillPaint = segment.getFillPaint(); 139 | segments.add(segment); 140 | } 141 | } 142 | 143 | /// Changes the series color, border color, and border width. 144 | @override 145 | void customizeSegment(ChartSegment segment) { 146 | segment.color = segment.series._seriesColor; 147 | segment.strokeColor = segment.series._seriesColor; 148 | segment.strokeWidth = segment.series.width; 149 | } 150 | 151 | ///Draws marker with different shape and color of the appropriate data point in the series. 152 | @override 153 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 154 | Paint strokePaint, double pointX, double pointY) { 155 | canvas.drawPath(_markerShapes[index], strokePaint); 156 | canvas.drawPath(_markerShapes[index], fillPaint); 157 | } 158 | 159 | /// Draws data label text of the appropriate data point in a series. 160 | @override 161 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 162 | double pointY, int angle, ChartTextStyle style) => 163 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 164 | } 165 | -------------------------------------------------------------------------------- /lib/src/chart/chart_segment/stacked_area_segment.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Creates the segments for stacked area series. 4 | class StackedAreaSegment extends ChartSegment { 5 | Path _path, _strokePath; 6 | Rect _pathRect; 7 | 8 | ///Area series 9 | XyDataSeries series; 10 | 11 | /// Gets the color of the series. 12 | @override 13 | Paint getFillPaint() { 14 | fillPaint = Paint(); 15 | if (series.gradient == null) { 16 | if (color != null) { 17 | fillPaint.color = color; 18 | fillPaint.style = PaintingStyle.fill; 19 | } 20 | } else { 21 | fillPaint = (_pathRect != null) 22 | ? _getLinearGradientPaint( 23 | series.gradient, _pathRect, series._chart._requireInvertedAxis) 24 | : fillPaint; 25 | } 26 | fillPaint.color = fillPaint.color.withOpacity(series.opacity); 27 | _defaultFillColor = fillPaint; 28 | return fillPaint; 29 | } 30 | 31 | /// Gets the border color of the series. 32 | @override 33 | Paint getStrokePaint() { 34 | final Paint strokePaint = Paint(); 35 | if (strokeColor != null) { 36 | strokePaint 37 | ..color = series.borderColor.withOpacity(series.opacity) 38 | ..style = PaintingStyle.stroke 39 | ..strokeWidth = series.borderWidth; 40 | series.borderWidth == 0 41 | ? strokePaint.color = Colors.transparent 42 | : strokePaint.color; 43 | } 44 | strokePaint.strokeCap = StrokeCap.round; 45 | _defaultStrokeColor = strokePaint; 46 | return strokePaint; 47 | } 48 | 49 | /// Calculates the rendering bounds of a segment. 50 | @override 51 | void calculateSegmentPoints() {} 52 | 53 | CartesianSeries getPreviousSeries( 54 | List> seriesCollection, 55 | num seriesIndex) { 56 | for (int i = 0; i < seriesCollection.length; i++) { 57 | if (seriesIndex == seriesCollection.indexOf(seriesCollection[i]) && 58 | i != 0) { 59 | return seriesCollection[i - 1]; 60 | } 61 | } 62 | return seriesCollection[0]; 63 | } 64 | 65 | /// Draws segment in series bounds. 66 | @override 67 | void onPaint(Canvas canvas) { 68 | final Rect rect = series._chart._chartAxis._axisClipRect; 69 | _ChartLocation point1, point2; 70 | final ChartAxis xAxis = series._xAxis; 71 | final ChartAxis yAxis = series._yAxis; 72 | _CartesianChartPoint point; 73 | StackedAreaSeries stackedAreaSeries; 74 | stackedAreaSeries = series; 75 | int startPoint = 0; 76 | final _StackedValues stackedValues = series._stackingValues[0]; 77 | List> seriesCollection; 78 | CartesianSeries previousSeries; 79 | _path = Path(); 80 | _strokePath = Path(); 81 | seriesCollection = _findSeriesCollection(series._chart); 82 | point1 = _calculatePoint( 83 | series._dataPoints[0].xValue, 84 | math_lib.max(yAxis._visibleRange.minimum, stackedValues.startValues[0]), 85 | xAxis, 86 | yAxis, 87 | series._chart._requireInvertedAxis, 88 | series, 89 | rect); 90 | _path.moveTo(point1.x, point1.y); 91 | _strokePath.moveTo(point1.x, point1.y); 92 | for (int pointIndex = 0; 93 | pointIndex < series._dataPoints.length; 94 | pointIndex++) { 95 | point = series._dataPoints[pointIndex]; 96 | point.symbolLocations = <_ChartLocation>[]; 97 | point.regions = []; 98 | if (point.isVisible) { 99 | point1 = _calculatePoint( 100 | series._dataPoints[pointIndex].xValue, 101 | stackedValues.endValues[pointIndex], 102 | xAxis, 103 | yAxis, 104 | series._chart._requireInvertedAxis, 105 | series, 106 | rect); 107 | _path.lineTo(point1.x, point1.y); 108 | _strokePath.lineTo(point1.x, point1.y); 109 | } else { 110 | if (series.emptyPointSettings.mode != EmptyPointMode.drop) { 111 | for (int j = pointIndex - 1; j >= startPoint; j--) { 112 | point2 = _calculatePoint( 113 | series._dataPoints[j].xValue, 114 | stackedValues.startValues[j], 115 | xAxis, 116 | yAxis, 117 | series._chart._requireInvertedAxis, 118 | series, 119 | rect); 120 | _path.lineTo(point2.x, point2.y); 121 | if (stackedAreaSeries.borderMode == AreaBorderMode.excludeBottom) 122 | _strokePath.lineTo(point1.x, point2.y); 123 | else if (stackedAreaSeries.borderMode == AreaBorderMode.all) 124 | _strokePath.lineTo(point2.x, point2.y); 125 | } 126 | if (series._dataPoints.length > pointIndex + 1 && 127 | series._dataPoints[pointIndex + 1] != null && 128 | series._dataPoints[pointIndex + 1].isVisible) { 129 | point1 = _calculatePoint( 130 | series._dataPoints[pointIndex + 1].xValue, 131 | stackedValues.startValues[pointIndex + 1], 132 | xAxis, 133 | yAxis, 134 | series._chart._requireInvertedAxis, 135 | series, 136 | rect); 137 | _path.moveTo(point1.x, point1.y); 138 | _strokePath.moveTo(point1.x, point1.y); 139 | } 140 | startPoint = pointIndex + 1; 141 | } 142 | } 143 | } 144 | for (int j = series._dataPoints.length - 1; j >= startPoint; j--) { 145 | previousSeries = getPreviousSeries(seriesCollection, _seriesIndex); 146 | if (previousSeries.emptyPointSettings.mode != EmptyPointMode.drop || 147 | previousSeries._dataPoints[j].isVisible) { 148 | point2 = _calculatePoint( 149 | series._dataPoints[j].xValue, 150 | stackedValues.startValues[j], 151 | xAxis, 152 | yAxis, 153 | series._chart._requireInvertedAxis, 154 | series, 155 | rect); 156 | _path.lineTo(point2.x, point2.y); 157 | if (stackedAreaSeries.borderMode == AreaBorderMode.excludeBottom) 158 | _strokePath.lineTo(point1.x, point2.y); 159 | else if (stackedAreaSeries.borderMode == AreaBorderMode.all) 160 | _strokePath.lineTo(point2.x, point2.y); 161 | } 162 | } 163 | 164 | _pathRect = _path.getBounds(); 165 | series.selectionSettings._selectionRenderer 166 | ._checkWithSelectionState(series.segments[0], series._chart); 167 | canvas.drawPath( 168 | _path, (series.gradient == null) ? fillPaint : getFillPaint()); 169 | 170 | if (strokePaint.color != Colors.transparent) 171 | _drawDashedLine(canvas, series, strokePaint, _strokePath); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/xy_data_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the xy series. 4 | abstract class XyDataSeries extends CartesianSeries { 5 | XyDataSeries( 6 | {ChartValueMapper xValueMapper, 7 | ChartValueMapper yValueMapper, 8 | ChartValueMapper dataLabelMapper, 9 | String name, 10 | @required List dataSource, 11 | String xAxisName, 12 | String yAxisName, 13 | ChartValueMapper pointColorMapper, 14 | String legendItemText, 15 | ChartValueMapper sortFieldValueMapper, 16 | LinearGradient gradient, 17 | ChartValueMapper sizeValueMapper, 18 | ChartValueMapper highValueMapper, 19 | ChartValueMapper lowValueMapper, 20 | double width, 21 | MarkerSettings markerSettings, 22 | bool isVisible, 23 | bool enableTooltip, 24 | EmptyPointSettings emptyPointSettings, 25 | DataLabelSettings dataLabelSettings, 26 | double animationDuration, 27 | List dashArray, 28 | Color borderColor, 29 | double borderWidth, 30 | SelectionSettings selectionSettings, 31 | bool isVisibleInLegend, 32 | LegendIconType legendIconType, 33 | double opacity, 34 | List segments, 35 | Color color, 36 | List initialSelectedDataIndexes, 37 | SortingOrder sortingOrder}) 38 | : super( 39 | isVisible: isVisible, 40 | legendItemText: legendItemText, 41 | xAxisName: xAxisName, 42 | dashArray: dashArray, 43 | segments: segments, 44 | isVisibleInLegend: isVisibleInLegend, 45 | borderColor: borderColor, 46 | borderWidth: borderWidth, 47 | yAxisName: yAxisName, 48 | color: color, 49 | name: name, 50 | width: width, 51 | xValueMapper: (int index) => xValueMapper(dataSource[index], index), 52 | yValueMapper: (int index) => yValueMapper(dataSource[index], index), 53 | sortFieldValueMapper: sortFieldValueMapper != null 54 | ? (int index) => sortFieldValueMapper(dataSource[index], index) 55 | : null, 56 | pointColorMapper: pointColorMapper != null 57 | ? (int index) => pointColorMapper(dataSource[index], index) 58 | : null, 59 | dataLabelMapper: dataLabelMapper != null 60 | ? (int index) => dataLabelMapper(dataSource[index], index) 61 | : null, 62 | sizeValueMapper: sizeValueMapper != null 63 | ? (int index) => sizeValueMapper(dataSource[index], index) 64 | : null, 65 | highValueMapper: highValueMapper != null 66 | ? (int index) => highValueMapper(dataSource[index], index) 67 | : null, 68 | lowValueMapper: lowValueMapper != null 69 | ? (int index) => lowValueMapper(dataSource[index], index) 70 | : null, 71 | dataSource: dataSource, 72 | emptyPointSettings: emptyPointSettings, 73 | dataLabelSettings: dataLabelSettings, 74 | enableTooltip: enableTooltip, 75 | animationDuration: animationDuration, 76 | selectionSettings: selectionSettings, 77 | legendIconType: legendIconType, 78 | sortingOrder: sortingOrder, 79 | opacity: opacity, 80 | gradient: gradient, 81 | markerSettings: markerSettings, 82 | initialSelectedDataIndexes: initialSelectedDataIndexes); 83 | 84 | /// Stores the series type 85 | String _seriesType; 86 | 87 | /// Holds the collection of cartesian data points 88 | List<_CartesianChartPoint> _dataPoints; 89 | 90 | /// Whether to check the series is rect series or not 91 | bool _isRectSeries; 92 | } 93 | 94 | /// Returns the widget. 95 | typedef ChartDataLabelTemplateBuilder = Widget Function( 96 | T data, _CartesianChartPoint point, int pointIndex, 97 | {int seriesIndex, CartesianSeries series}); 98 | 99 | class _CartesianChartPoint { 100 | _CartesianChartPoint(this.x, this.y, 101 | [this.dataLabelMapper, 102 | this.pointColorMapper, 103 | this.bubbleSize, 104 | this.high, 105 | this.low]) { 106 | x = x; 107 | y = y; 108 | sortValue = sortValue; 109 | markerPoint = markerPoint; 110 | isEmpty = isEmpty; 111 | isGap = isGap; 112 | isVisible = isVisible; 113 | bubbleSize = bubbleSize; 114 | pointColorMapper = pointColorMapper; 115 | dataLabelMapper = dataLabelMapper; 116 | high = high; 117 | low = low; 118 | markerPoint2 = markerPoint2; 119 | } 120 | 121 | D x; 122 | D y; 123 | D xValue; 124 | D yValue; 125 | D sortValue; 126 | D high; 127 | D low; 128 | _ChartLocation markerPoint; 129 | _ChartLocation markerPoint2; 130 | double bubbleSize; 131 | bool isEmpty; 132 | bool isGap = false; 133 | bool isDrop = false; 134 | bool isVisible = true; 135 | Color pointColorMapper; 136 | String dataLabelMapper; 137 | Rect region; 138 | List<_ChartLocation> symbolLocations; 139 | List regions; 140 | double cumulativeValue; 141 | Rect trackerRectRegion; 142 | String label; 143 | String label2; 144 | RRect labelFillRect; 145 | RRect labelFillRect2; 146 | _ChartLocation labelLocation; 147 | _ChartLocation labelLocation2; 148 | bool dataLabelSaturationRegionInside = false; 149 | 150 | ///Stores the data label region 151 | Rect dataLabelRegion; 152 | Rect dataLabelRegion2; 153 | } 154 | 155 | class _ChartLocation { 156 | _ChartLocation(this.x, this.y); 157 | num x; 158 | num y; 159 | } 160 | 161 | /// To calculate dash array path for series 162 | Path _dashPath( 163 | Path source, { 164 | @required _CircularIntervalList dashArray, 165 | }) { 166 | if (source == null) { 167 | return null; 168 | } 169 | const double intialValue = 0.0; 170 | final Path path = Path(); 171 | for (final PathMetric measurePath in source.computeMetrics()) { 172 | double distance = intialValue; 173 | bool draw = true; 174 | while (distance < measurePath.length) { 175 | final double length = dashArray.next; 176 | if (draw) { 177 | path.addPath( 178 | measurePath.extractPath(distance, distance + length), Offset.zero); 179 | } 180 | distance += length; 181 | draw = !draw; 182 | } 183 | } 184 | return path; 185 | } 186 | 187 | /// A circular array for dash offsets and lengths. 188 | class _CircularIntervalList { 189 | _CircularIntervalList(this._values); 190 | final List _values; 191 | int _index = 0; 192 | T get next { 193 | if (_index >= _values.length) { 194 | _index = 0; 195 | } 196 | return _values[_index++]; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /lib/src/chart/chart_series/stacked_line_series.dart: -------------------------------------------------------------------------------- 1 | part of charts; 2 | 3 | /// Renders the stacked line series. 4 | class StackedLineSeries extends _StackedSeriesBase { 5 | StackedLineSeries( 6 | {@required List dataSource, 7 | @required ChartValueMapper xValueMapper, 8 | @required ChartValueMapper yValueMapper, 9 | ChartValueMapper sortFieldValueMapper, 10 | ChartValueMapper pointColorMapper, 11 | ChartValueMapper dataLabelMapper, 12 | String xAxisName, 13 | String yAxisName, 14 | Color color, 15 | double width, 16 | LinearGradient gradient, 17 | MarkerSettings markerSettings, 18 | EmptyPointSettings emptyPointSettings, 19 | DataLabelSettings dataLabelSettings, 20 | bool isVisible, 21 | String name, 22 | bool enableTooltip, 23 | List dashArray, 24 | double animationDuration, 25 | String groupName, 26 | SelectionSettings selectionSettings, 27 | bool isVisibleInLegend, 28 | LegendIconType legendIconType, 29 | SortingOrder sortingOrder, 30 | String legendItemText, 31 | double opacity, 32 | List initialSelectedDataIndexes}) 33 | : super( 34 | name: name, 35 | xValueMapper: xValueMapper, 36 | yValueMapper: yValueMapper, 37 | sortFieldValueMapper: sortFieldValueMapper, 38 | pointColorMapper: pointColorMapper, 39 | dataLabelMapper: dataLabelMapper, 40 | dataSource: dataSource, 41 | xAxisName: xAxisName, 42 | yAxisName: yAxisName, 43 | color: color, 44 | width: width ?? 2, 45 | gradient: gradient, 46 | markerSettings: markerSettings, 47 | emptyPointSettings: emptyPointSettings, 48 | dataLabelSettings: dataLabelSettings, 49 | isVisible: isVisible, 50 | enableTooltip: enableTooltip, 51 | dashArray: dashArray, 52 | animationDuration: animationDuration, 53 | selectionSettings: selectionSettings, 54 | legendItemText: legendItemText, 55 | isVisibleInLegend: isVisibleInLegend, 56 | legendIconType: legendIconType, 57 | sortingOrder: sortingOrder, 58 | groupName: groupName, 59 | opacity: opacity, 60 | initialSelectedDataIndexes: initialSelectedDataIndexes); 61 | 62 | /// Creates a segment for a data point in the series. 63 | @override 64 | ChartSegment createSegment() => StackedLineSegment(); 65 | 66 | /// Creates a collection of segments for the points in the series. 67 | @override 68 | void createSegments() { 69 | List values = []; 70 | int segmentIndex = 0; 71 | for (int i = 0; i < _dataPoints.length; i++) { 72 | if (_dataPoints.length > 1) { 73 | if (values != null && values.isNotEmpty && values.length > 2) { 74 | values[0] = values[2]; 75 | values[1] = values[3]; 76 | values.removeRange(2, 4); 77 | } 78 | if (_dataPoints[i].isVisible) { 79 | if (_dataPoints[i].isGap != true) { 80 | if (values.isEmpty || 81 | (values.isNotEmpty && 82 | !(values[0] == _dataPoints[i].xValue && 83 | values[1] == _dataPoints[i].yValue))) { 84 | values.add(_dataPoints[i].xValue); 85 | values.add(_dataPoints[i].yValue); 86 | } 87 | } 88 | if ((i == 0 || _dataPoints[i - 1].isGap == true) && 89 | _dataPoints[i].isGap != true && 90 | i != _dataPoints.length - 1 && 91 | _dataPoints[i + 1].isGap != true && 92 | _dataPoints[i + 1].isVisible) { 93 | values.add(_dataPoints[i + 1].xValue); 94 | values.add(_dataPoints[i + 1].yValue); 95 | } else if (_dataPoints[i].isGap == true) { 96 | values = []; 97 | } 98 | } 99 | } 100 | 101 | if (values != null && values.isNotEmpty && values.length > 2) { 102 | _createSegment(values, _dataPoints[i], segmentIndex, 103 | _dataPoints[segmentIndex].pointColorMapper); 104 | segmentIndex++; 105 | } 106 | } 107 | } 108 | 109 | /// Line segement is created here 110 | void _createSegment( 111 | List values, 112 | _CartesianChartPoint currentPoint, 113 | int segmentIndex, 114 | Color pointColorMapper) { 115 | final StackedLineSegment segment = createSegment(); 116 | _isRectSeries = false; 117 | if (segment != null) { 118 | segment.currentSegmentIndex = segmentIndex; 119 | segment._seriesIndex = _chart._chartSeries.visibleSeries.indexOf(this); 120 | segment.series = this; 121 | segment._currentPoint = currentPoint; 122 | if (_chart._chartState.widgetNeedUpdate && 123 | _xAxis._zoomFactor == 1 && 124 | _yAxis._zoomFactor == 1 && 125 | _chart._chartState.prevWidgetSeries != null && 126 | _chart._chartState.prevWidgetSeries.isNotEmpty && 127 | _chart._chartState.prevWidgetSeries.length - 1 >= 128 | segment._seriesIndex && 129 | _chart._chartState.prevWidgetSeries[segment._seriesIndex] 130 | ._seriesName == 131 | segment.series._seriesName) { 132 | segment.oldSeries = 133 | _chart._chartState.prevWidgetSeries[segment._seriesIndex]; 134 | } 135 | segment._pointColorMapper = pointColorMapper; 136 | segment._setData(values); 137 | segment.calculateSegmentPoints(); 138 | customizeSegment(segment); 139 | segment.strokePaint = segment.getStrokePaint(); 140 | segment.fillPaint = segment.getFillPaint(); 141 | segments.add(segment); 142 | } 143 | } 144 | 145 | /// Changes the series color, border color, and border width. 146 | @override 147 | void customizeSegment(ChartSegment segment) { 148 | segment.color = segment.series._seriesColor; 149 | segment.strokeColor = segment.series._seriesColor; 150 | segment.strokeWidth = segment.series.width; 151 | } 152 | 153 | ///Draws marker with different shape and color of the appropriate data point in the series. 154 | @override 155 | void drawDataMarker(int index, Canvas canvas, Paint fillPaint, 156 | Paint strokePaint, double pointX, double pointY) { 157 | canvas.drawPath(_markerShapes[index], strokePaint); 158 | canvas.drawPath(_markerShapes[index], fillPaint); 159 | } 160 | 161 | /// Draws data label text of the appropriate data point in a series. 162 | @override 163 | void drawDataLabel(int index, Canvas canvas, String dataLabel, double pointX, 164 | double pointY, int angle, ChartTextStyle style) => 165 | _drawText(canvas, dataLabel, Offset(pointX, pointY), style, angle); 166 | } 167 | --------------------------------------------------------------------------------