David PHAM-VAN

Update Chart Widget

@@ -30,10 +30,12 @@ export 'package:barcode/barcode.dart'; @@ -30,10 +30,12 @@ export 'package:barcode/barcode.dart';
30 part 'widgets/annotations.dart'; 30 part 'widgets/annotations.dart';
31 part 'widgets/barcode.dart'; 31 part 'widgets/barcode.dart';
32 part 'widgets/basic.dart'; 32 part 'widgets/basic.dart';
  33 +part 'widgets/chart/bar_chart.dart';
33 part 'widgets/chart/chart.dart'; 34 part 'widgets/chart/chart.dart';
34 -part 'widgets/chart/linear_grid.dart'; 35 +part 'widgets/chart/grid_axis.dart';
  36 +part 'widgets/chart/grid_cartesian.dart';
  37 +part 'widgets/chart/legend.dart';
35 part 'widgets/chart/line_chart.dart'; 38 part 'widgets/chart/line_chart.dart';
36 -part 'widgets/chart/bar_chart.dart';  
37 part 'widgets/clip.dart'; 39 part 'widgets/clip.dart';
38 part 'widgets/container.dart'; 40 part 'widgets/container.dart';
39 part 'widgets/content.dart'; 41 part 'widgets/content.dart';
@@ -18,53 +18,60 @@ @@ -18,53 +18,60 @@
18 18
19 part of widget; 19 part of widget;
20 20
21 -class BarDataSet extends DataSet { 21 +class BarDataSet extends Dataset {
22 BarDataSet({ 22 BarDataSet({
23 @required this.data, 23 @required this.data,
  24 + String legend,
24 this.borderColor, 25 this.borderColor,
25 this.borderWidth = 1.5, 26 this.borderWidth = 1.5,
26 - this.color = PdfColors.blue, 27 + PdfColor color = PdfColors.blue,
27 this.drawBorder = true, 28 this.drawBorder = true,
28 this.drawSurface = true, 29 this.drawSurface = true,
29 this.surfaceOpacity = 1, 30 this.surfaceOpacity = 1,
30 - this.width = 20, 31 + this.width = 10,
31 this.offset = 0, 32 this.offset = 0,
32 - this.margin = 5,  
33 - }) : assert(drawBorder || drawSurface); 33 + }) : assert(drawBorder || drawSurface),
  34 + super(
  35 + legend: legend,
  36 + color: color,
  37 + );
34 38
35 final List<LineChartValue> data; 39 final List<LineChartValue> data;
36 - final double width;  
37 - final double offset;  
38 - final double margin;  
39 40
40 final bool drawBorder; 41 final bool drawBorder;
41 final PdfColor borderColor; 42 final PdfColor borderColor;
42 final double borderWidth; 43 final double borderWidth;
43 44
44 final bool drawSurface; 45 final bool drawSurface;
45 - final PdfColor color; 46 +
46 final double surfaceOpacity; 47 final double surfaceOpacity;
47 48
  49 + final double width;
  50 + final double offset;
  51 +
48 void _drawSurface(Context context, ChartGrid grid, LineChartValue value) { 52 void _drawSurface(Context context, ChartGrid grid, LineChartValue value) {
49 - final double y = (grid is LinearGrid) ? grid.xAxisOffset : 0;  
50 - final PdfPoint p = grid.tochart(value.point); 53 + final double y = (grid is CartesianGrid) ? grid.xAxisOffset : 0;
  54 + final PdfPoint p = grid.toChart(value.point);
51 55
52 context.canvas.drawRect(p.x + offset - width / 2, y, width, p.y); 56 context.canvas.drawRect(p.x + offset - width / 2, y, width, p.y);
53 } 57 }
54 58
55 @override 59 @override
56 - void paintBackground(Context context, ChartGrid grid) {} 60 + void layout(Context context, BoxConstraints constraints,
  61 + {bool parentUsesSize = false}) {
  62 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  63 + }
57 64
58 @override 65 @override
59 - void paintForeground(Context context, ChartGrid grid) {  
60 - if (data.isEmpty) {  
61 - return;  
62 - } 66 + void paint(Context context) {
  67 + super.paint(context);
63 68
64 if (data.isEmpty) { 69 if (data.isEmpty) {
65 return; 70 return;
66 } 71 }
67 72
  73 + final ChartGrid grid = Chart.of(context).grid;
  74 +
68 if (drawSurface) { 75 if (drawSurface) {
69 for (final LineChartValue value in data) { 76 for (final LineChartValue value in data) {
70 _drawSurface(context, grid, value); 77 _drawSurface(context, grid, value);
@@ -19,15 +19,39 @@ @@ -19,15 +19,39 @@
19 part of widget; 19 part of widget;
20 20
21 /// This widget is in preview and the API is subject to change 21 /// This widget is in preview and the API is subject to change
22 -class Chart extends Widget { 22 +class Chart extends Widget implements Inherited {
23 Chart({ 23 Chart({
24 @required this.grid, 24 @required this.grid,
25 - @required this.data, 25 + @required this.datasets,
  26 + this.overlay,
  27 + this.title,
  28 + this.bottom,
  29 + this.left,
  30 + this.right,
26 }); 31 });
27 32
  33 + /// The Coordinate system that will layout the content
28 final ChartGrid grid; 34 final ChartGrid grid;
29 35
30 - final List<DataSet> data; 36 + /// The list of dataset to display
  37 + final List<Dataset> datasets;
  38 +
  39 + /// Legend for this chart
  40 + final Widget overlay;
  41 +
  42 + final Widget title;
  43 +
  44 + final Widget bottom;
  45 +
  46 + final Widget left;
  47 +
  48 + final Widget right;
  49 +
  50 + Context _context;
  51 +
  52 + Widget _child;
  53 +
  54 + static Chart of(Context context) => context.inherited[Chart];
31 55
32 PdfPoint _computeSize(BoxConstraints constraints) { 56 PdfPoint _computeSize(BoxConstraints constraints) {
33 if (constraints.isTight) { 57 if (constraints.isTight) {
@@ -50,51 +74,64 @@ class Chart extends Widget { @@ -50,51 +74,64 @@ class Chart extends Widget {
50 return constraints.constrain(PdfPoint(width, height)); 74 return constraints.constrain(PdfPoint(width, height));
51 } 75 }
52 76
  77 + Widget _build(Context context) {
  78 + return Column(
  79 + children: <Widget>[
  80 + if (title != null) title,
  81 + Expanded(
  82 + child: Row(
  83 + children: <Widget>[
  84 + if (left != null) left,
  85 + Expanded(
  86 + child: Stack(
  87 + children: <Widget>[
  88 + grid,
  89 + if (overlay != null) overlay,
  90 + ],
  91 + ),
  92 + ),
  93 + if (right != null) right,
  94 + ],
  95 + ),
  96 + ),
  97 + if (bottom != null) bottom,
  98 + ],
  99 + );
  100 + }
  101 +
53 @override 102 @override
54 void layout(Context context, BoxConstraints constraints, 103 void layout(Context context, BoxConstraints constraints,
55 {bool parentUsesSize = false}) { 104 {bool parentUsesSize = false}) {
56 box = PdfRect.fromPoints(PdfPoint.zero, _computeSize(constraints)); 105 box = PdfRect.fromPoints(PdfPoint.zero, _computeSize(constraints));
57 -  
58 - grid.layout(context, box.size); 106 + _context = context.inheritFrom(this);
  107 + _child = _build(_context);
  108 + _child.layout(_context, BoxConstraints.tight(box.size));
59 } 109 }
60 110
61 @override 111 @override
62 void paint(Context context) { 112 void paint(Context context) {
63 - super.paint(context); 113 + super.paint(_context);
64 114
65 final Matrix4 mat = Matrix4.identity(); 115 final Matrix4 mat = Matrix4.identity();
66 mat.translate(box.x, box.y); 116 mat.translate(box.x, box.y);
67 - context.canvas 117 + _context.canvas
68 ..saveContext() 118 ..saveContext()
69 ..setTransform(mat); 119 ..setTransform(mat);
70 120
71 - grid.paintBackground(context, box.size);  
72 - grid.clip(context, box.size);  
73 - for (DataSet dataSet in data) {  
74 - dataSet.paintBackground(context, grid);  
75 - }  
76 - grid.unClip(context, box.size);  
77 - grid.paint(context, box.size);  
78 - grid.clip(context, box.size);  
79 - for (DataSet dataSet in data) {  
80 - dataSet.paintForeground(context, grid);  
81 - }  
82 - grid.unClip(context, box.size);  
83 - grid.paintForeground(context, box.size);  
84 - context.canvas.restoreContext(); 121 + _child.paint(_context);
  122 +
  123 + _context.canvas.restoreContext();
85 } 124 }
86 } 125 }
87 126
88 -abstract class ChartGrid {  
89 - void layout(Context context, PdfPoint size);  
90 - void paintBackground(Context context, PdfPoint size);  
91 - void paint(Context context, PdfPoint size);  
92 - void paintForeground(Context context, PdfPoint size);  
93 -  
94 - void clip(Context context, PdfPoint size);  
95 - void unClip(Context context, PdfPoint size); 127 +abstract class ChartGrid extends Widget {
  128 + @override
  129 + void layout(Context context, BoxConstraints constraints,
  130 + {bool parentUsesSize = false}) {
  131 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  132 + }
96 133
97 - PdfPoint tochart(PdfPoint p); 134 + PdfPoint toChart(PdfPoint p);
98 } 135 }
99 136
100 @immutable 137 @immutable
@@ -102,7 +139,31 @@ abstract class ChartValue { @@ -102,7 +139,31 @@ abstract class ChartValue {
102 const ChartValue(); 139 const ChartValue();
103 } 140 }
104 141
105 -abstract class DataSet {  
106 - void paintBackground(Context context, ChartGrid grid);  
107 - void paintForeground(Context context, ChartGrid grid); 142 +abstract class Dataset extends Widget {
  143 + Dataset({
  144 + this.legend,
  145 + this.color,
  146 + });
  147 +
  148 + final String legend;
  149 +
  150 + final PdfColor color;
  151 +
  152 + void paintBackground(Context context) {}
  153 +
  154 + Widget legendeShape() {
  155 + return Container(
  156 + decoration: BoxDecoration(
  157 + color: color,
  158 + border: const BoxBorder(
  159 + left: true,
  160 + top: true,
  161 + bottom: true,
  162 + right: true,
  163 + color: PdfColors.black,
  164 + width: .5,
  165 + ),
  166 + ),
  167 + );
  168 + }
108 } 169 }
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +// ignore_for_file: omit_local_variable_types
  18 +
  19 +part of widget;
  20 +
  21 +typedef GridAxisFormat = String Function(num value);
  22 +
  23 +abstract class GridAxis extends Widget {
  24 + GridAxis({
  25 + GridAxisFormat format,
  26 + this.textStyle,
  27 + this.margin,
  28 + double marginStart,
  29 + double marginEnd,
  30 + PdfColor color,
  31 + double width,
  32 + bool divisions,
  33 + double divisionsWidth,
  34 + PdfColor divisionsColor,
  35 + bool divisionsDashed,
  36 + bool ticks,
  37 + bool axisTick,
  38 + }) : format = format ?? _defaultFormat,
  39 + color = color ?? PdfColors.black,
  40 + width = width ?? 1,
  41 + divisions = divisions ?? false,
  42 + divisionsWidth = divisionsWidth ?? .5,
  43 + divisionsColor = divisionsColor ?? PdfColors.grey,
  44 + _marginStart = marginStart ?? 0,
  45 + _marginEnd = marginEnd ?? 0,
  46 + ticks = ticks ?? false,
  47 + _axisTick = axisTick,
  48 + divisionsDashed = divisionsDashed ?? false;
  49 +
  50 + Axis direction;
  51 +
  52 + final GridAxisFormat format;
  53 +
  54 + final TextStyle textStyle;
  55 +
  56 + final double margin;
  57 +
  58 + double _crossAxisPosition = 0;
  59 +
  60 + double _textMargin;
  61 +
  62 + final double _marginStart;
  63 +
  64 + double _marginEnd;
  65 +
  66 + final PdfColor color;
  67 +
  68 + final double width;
  69 +
  70 + final bool divisions;
  71 +
  72 + final double divisionsWidth;
  73 +
  74 + final PdfColor divisionsColor;
  75 +
  76 + final bool divisionsDashed;
  77 +
  78 + final bool ticks;
  79 +
  80 + bool _axisTick;
  81 +
  82 + double axisPosition = 0;
  83 +
  84 + static String _defaultFormat(num v) => v.toString();
  85 +
  86 + double transfer(num input) {
  87 + return input.toDouble();
  88 + }
  89 +
  90 + double toChart(num input);
  91 +
  92 + void paintBackground(Context context);
  93 +}
  94 +
  95 +class FixedAxis<T extends num> extends GridAxis {
  96 + FixedAxis(
  97 + this.values, {
  98 + GridAxisFormat format,
  99 + TextStyle textStyle,
  100 + double margin,
  101 + double marginStart,
  102 + double marginEnd,
  103 + PdfColor color,
  104 + double width,
  105 + bool divisions,
  106 + double divisionsWidth,
  107 + PdfColor divisionsColor,
  108 + bool divisionsDashed,
  109 + bool ticks,
  110 + bool axisTick,
  111 + }) : assert(_isSortedAscending(values)),
  112 + super(
  113 + format: format,
  114 + textStyle: textStyle,
  115 + margin: margin,
  116 + marginStart: marginStart,
  117 + marginEnd: marginEnd,
  118 + color: color,
  119 + width: width,
  120 + divisions: divisions,
  121 + divisionsWidth: divisionsWidth,
  122 + divisionsColor: divisionsColor,
  123 + divisionsDashed: divisionsDashed,
  124 + ticks: ticks,
  125 + axisTick: axisTick,
  126 + );
  127 +
  128 + static FixedAxis<int> fromStrings(
  129 + List<String> values, {
  130 + TextStyle textStyle,
  131 + double margin,
  132 + double marginStart,
  133 + double marginEnd,
  134 + PdfColor color,
  135 + double width,
  136 + bool divisions,
  137 + double divisionsWidth,
  138 + PdfColor divisionsColor,
  139 + bool divisionsDashed,
  140 + bool ticks,
  141 + bool axisTick,
  142 + }) {
  143 + return FixedAxis<int>(
  144 + List<int>.generate(values.length, (int index) => index),
  145 + format: (num v) => values[v],
  146 + textStyle: textStyle,
  147 + margin: margin,
  148 + marginStart: marginStart,
  149 + marginEnd: marginEnd,
  150 + color: color,
  151 + width: width,
  152 + divisions: divisions,
  153 + divisionsWidth: divisionsWidth,
  154 + divisionsColor: divisionsColor,
  155 + divisionsDashed: divisionsDashed,
  156 + ticks: ticks,
  157 + axisTick: axisTick,
  158 + );
  159 + }
  160 +
  161 + final List<T> values;
  162 +
  163 + static bool _isSortedAscending(List<num> list) {
  164 + num prev = list.first;
  165 + for (final num elem in list) {
  166 + if (prev > elem) {
  167 + return false;
  168 + }
  169 + prev = elem;
  170 + }
  171 + return true;
  172 + }
  173 +
  174 + @override
  175 + double toChart(num input) {
  176 + final double offset = transfer(values.first);
  177 + final double total = transfer(values.last) - offset;
  178 + final double start = _crossAxisPosition + _marginStart;
  179 + switch (direction) {
  180 + case Axis.horizontal:
  181 + return box.left +
  182 + start +
  183 + (box.width - start - _marginEnd) *
  184 + (transfer(input) - offset) /
  185 + total;
  186 + case Axis.vertical:
  187 + return box.bottom +
  188 + start +
  189 + (box.height - start - _marginEnd) *
  190 + (transfer(input) - offset) /
  191 + total;
  192 + }
  193 +
  194 + return null;
  195 + }
  196 +
  197 + @override
  198 + void layout(Context context, BoxConstraints constraints,
  199 + {bool parentUsesSize = false}) {
  200 + assert(Chart.of(context) != null,
  201 + '$runtimeType cannot be used without a Chart widget');
  202 +
  203 + final PdfPoint size = constraints.biggest;
  204 + final TextStyle style = Theme.of(context).defaultTextStyle.merge(textStyle);
  205 + final PdfFont font = style.font.getFont(context);
  206 +
  207 + double maxWidth = 0;
  208 + double maxHeight = 0;
  209 + PdfFontMetrics metricsFirst;
  210 + PdfFontMetrics metrics;
  211 + for (final T value in values) {
  212 + metrics = font.stringMetrics(format(value)) * style.fontSize;
  213 + metricsFirst ??= metrics;
  214 + maxWidth = math.max(maxWidth, metrics.maxWidth);
  215 + maxHeight = math.max(maxHeight, metrics.maxHeight);
  216 + }
  217 +
  218 + switch (direction) {
  219 + case Axis.horizontal:
  220 + _textMargin = margin ?? 2;
  221 + _axisTick ??= false;
  222 + final double minStart = metricsFirst.maxWidth / 2;
  223 + _marginEnd = math.max(_marginEnd, metrics.maxWidth / 2);
  224 + _crossAxisPosition = math.max(_crossAxisPosition, minStart);
  225 + axisPosition = math.max(axisPosition, maxHeight + _textMargin);
  226 + box = PdfRect(0, 0, size.x, axisPosition);
  227 + break;
  228 + case Axis.vertical:
  229 + _textMargin = margin ?? 10;
  230 + _axisTick ??= true;
  231 + _marginEnd = math.max(_marginEnd, metrics.maxHeight / 2);
  232 + final double minStart = metricsFirst.maxHeight / 2;
  233 + _marginEnd = math.max(_marginEnd, metrics.maxWidth / 2);
  234 + _crossAxisPosition = math.max(_crossAxisPosition, minStart);
  235 + axisPosition = math.max(axisPosition, maxWidth + _textMargin);
  236 + box = PdfRect(0, 0, axisPosition, size.y);
  237 + break;
  238 + }
  239 + }
  240 +
  241 + void _drawYValues(Context context) {
  242 + context.canvas
  243 + ..moveTo(axisPosition, box.top)
  244 + ..lineTo(axisPosition, box.bottom + _crossAxisPosition);
  245 +
  246 + if (_axisTick && _textMargin > 0) {
  247 + context.canvas
  248 + ..moveTo(axisPosition, box.bottom + _crossAxisPosition)
  249 + ..lineTo(
  250 + axisPosition - _textMargin / 2, box.bottom + _crossAxisPosition);
  251 + }
  252 +
  253 + if (ticks && _textMargin > 0) {
  254 + for (final num x in values) {
  255 + final double p = toChart(x);
  256 + context.canvas
  257 + ..moveTo(axisPosition, p)
  258 + ..lineTo(axisPosition - _textMargin / 2, p);
  259 + }
  260 + }
  261 +
  262 + context.canvas
  263 + ..setStrokeColor(color)
  264 + ..setLineWidth(width)
  265 + ..setLineCap(PdfLineCap.joinBevel)
  266 + ..strokePath();
  267 +
  268 + for (final T y in values) {
  269 + final String v = format(y);
  270 + final TextStyle style =
  271 + Theme.of(context).defaultTextStyle.merge(textStyle);
  272 + final PdfFont font = style.font.getFont(context);
  273 + final PdfFontMetrics metrics = font.stringMetrics(v) * style.fontSize;
  274 + final double p = toChart(y);
  275 +
  276 + context.canvas
  277 + ..setColor(style.color)
  278 + ..drawString(
  279 + style.font.getFont(context),
  280 + style.fontSize,
  281 + v,
  282 + axisPosition - _textMargin - metrics.maxWidth,
  283 + p - (metrics.ascent + metrics.descent) / 2,
  284 + );
  285 + }
  286 + }
  287 +
  288 + void _drawXValues(Context context) {
  289 + context.canvas
  290 + ..moveTo(box.left + _crossAxisPosition, axisPosition)
  291 + ..lineTo(box.right, axisPosition);
  292 +
  293 + if (_axisTick && _textMargin > 0) {
  294 + context.canvas
  295 + ..moveTo(box.left + _crossAxisPosition, axisPosition)
  296 + ..lineTo(box.left + _crossAxisPosition, axisPosition - _textMargin);
  297 + }
  298 +
  299 + if (ticks && _textMargin > 0) {
  300 + for (final num x in values) {
  301 + final double p = toChart(x);
  302 + context.canvas
  303 + ..moveTo(p, axisPosition)
  304 + ..lineTo(p, axisPosition - _textMargin);
  305 + }
  306 + }
  307 +
  308 + context.canvas
  309 + ..setStrokeColor(color)
  310 + ..setLineWidth(width)
  311 + ..setLineCap(PdfLineCap.joinBevel)
  312 + ..strokePath();
  313 +
  314 + for (final num x in values) {
  315 + final String v = format(x);
  316 + final TextStyle style =
  317 + Theme.of(context).defaultTextStyle.merge(textStyle);
  318 + final PdfFont font = style.font.getFont(context);
  319 + final PdfFontMetrics metrics = font.stringMetrics(v) * style.fontSize;
  320 + final double p = toChart(x);
  321 +
  322 + context.canvas
  323 + ..setColor(style.color)
  324 + ..drawString(
  325 + style.font.getFont(context),
  326 + style.fontSize,
  327 + v,
  328 + p - metrics.maxWidth / 2,
  329 + axisPosition - metrics.ascent - _textMargin,
  330 + );
  331 + }
  332 + }
  333 +
  334 + @override
  335 + void paintBackground(Context context) {
  336 + if (!divisions) {
  337 + return;
  338 + }
  339 +
  340 + final CartesianGrid grid = Chart.of(context).grid;
  341 +
  342 + switch (direction) {
  343 + case Axis.horizontal:
  344 + for (final num x in values.sublist(_marginStart > 0 ? 0 : 1)) {
  345 + final double p = toChart(x);
  346 + context.canvas.drawLine(p, grid.gridBox.top, p, grid.gridBox.bottom);
  347 + }
  348 + break;
  349 +
  350 + case Axis.vertical:
  351 + for (final num y in values.sublist(_marginStart > 0 ? 0 : 1)) {
  352 + final double p = toChart(y);
  353 + context.canvas.drawLine(grid.gridBox.left, p, grid.gridBox.right, p);
  354 + }
  355 +
  356 + break;
  357 + }
  358 +
  359 + if (divisionsDashed) {
  360 + context.canvas.setLineDashPattern(<int>[4, 2]);
  361 + }
  362 +
  363 + context.canvas
  364 + ..setStrokeColor(divisionsColor)
  365 + ..setLineWidth(divisionsWidth)
  366 + ..setLineCap(PdfLineCap.joinMiter)
  367 + ..strokePath();
  368 +
  369 + if (divisionsDashed) {
  370 + context.canvas.setLineDashPattern();
  371 + }
  372 + }
  373 +
  374 + @override
  375 + void debugPaint(Context context) {
  376 + switch (direction) {
  377 + case Axis.horizontal:
  378 + context.canvas
  379 + ..setFillColor(PdfColors.grey300)
  380 + ..drawRect(box.x, box.y, box.width, box.height)
  381 + ..fillPath();
  382 + break;
  383 + case Axis.vertical:
  384 + context.canvas
  385 + ..setFillColor(PdfColors.grey300)
  386 + ..drawRect(box.x, box.y + _crossAxisPosition, box.width,
  387 + box.height - _crossAxisPosition)
  388 + ..fillPath();
  389 + break;
  390 + }
  391 + }
  392 +
  393 + @override
  394 + void paint(Context context) {
  395 + super.paint(context);
  396 +
  397 + switch (direction) {
  398 + case Axis.horizontal:
  399 + _drawXValues(context);
  400 + break;
  401 + case Axis.vertical:
  402 + _drawYValues(context);
  403 + break;
  404 + }
  405 + }
  406 +}
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +// ignore_for_file: omit_local_variable_types
  18 +
  19 +part of widget;
  20 +
  21 +class CartesianGrid extends ChartGrid {
  22 + CartesianGrid({
  23 + @required GridAxis xAxis,
  24 + @required GridAxis yAxis,
  25 + }) : _xAxis = xAxis..direction = Axis.horizontal,
  26 + _yAxis = yAxis..direction = Axis.vertical;
  27 +
  28 + final GridAxis _xAxis;
  29 + final GridAxis _yAxis;
  30 +
  31 + PdfRect gridBox;
  32 +
  33 + @override
  34 + void layout(Context context, BoxConstraints constraints,
  35 + {bool parentUsesSize = false}) {
  36 + assert(Chart.of(context) != null,
  37 + '$runtimeType cannot be used without a Chart widget');
  38 + super.layout(context, constraints, parentUsesSize: parentUsesSize);
  39 +
  40 + final List<Dataset> datasets = Chart.of(context).datasets;
  41 + final PdfPoint size = constraints.biggest;
  42 +
  43 + // In simple conditions, this loop will run only 2 times.
  44 + int count = 5;
  45 + while (count-- > 0) {
  46 + _xAxis._crossAxisPosition = _yAxis.axisPosition;
  47 + _xAxis.axisPosition =
  48 + math.max(_xAxis.axisPosition, _yAxis._crossAxisPosition);
  49 + _xAxis.layout(context, constraints);
  50 + assert(_xAxis.box != null);
  51 + _yAxis._crossAxisPosition = _xAxis.axisPosition;
  52 + _yAxis.axisPosition =
  53 + math.max(_yAxis.axisPosition, _xAxis._crossAxisPosition);
  54 + _yAxis.layout(context, constraints);
  55 + assert(_yAxis.box != null);
  56 + if (_yAxis._crossAxisPosition == _xAxis.axisPosition &&
  57 + _xAxis._crossAxisPosition == _yAxis.axisPosition) {
  58 + break;
  59 + }
  60 + }
  61 +
  62 + final double width = _yAxis.axisPosition;
  63 + final double height = _xAxis.axisPosition;
  64 + gridBox = PdfRect(width, height, size.x - width, size.y - height);
  65 +
  66 + for (final Dataset dataset in datasets) {
  67 + dataset.layout(context, BoxConstraints.tight(gridBox.size));
  68 + dataset.box =
  69 + PdfRect.fromPoints(PdfPoint(width, height), dataset.box.size);
  70 + }
  71 + }
  72 +
  73 + @override
  74 + PdfPoint toChart(PdfPoint p) {
  75 + return PdfPoint(
  76 + _xAxis.toChart(p.x),
  77 + _yAxis.toChart(p.y),
  78 + );
  79 + }
  80 +
  81 + double get xAxisOffset => _xAxis.axisPosition;
  82 +
  83 + double get yAxisOffset => _yAxis.axisPosition;
  84 +
  85 + void paintBackground(Context context) {
  86 + _xAxis.paintBackground(context);
  87 + _yAxis.paintBackground(context);
  88 + }
  89 +
  90 + void clip(Context context, PdfPoint size) {
  91 + context.canvas
  92 + ..saveContext()
  93 + ..drawRect(
  94 + gridBox.left,
  95 + gridBox.bottom,
  96 + gridBox.width,
  97 + gridBox.height,
  98 + )
  99 + ..clipPath();
  100 + }
  101 +
  102 + @override
  103 + void paint(Context context) {
  104 + super.paint(context);
  105 +
  106 + final List<Dataset> datasets = Chart.of(context).datasets;
  107 +
  108 + clip(context, box.size);
  109 + for (Dataset dataSet in datasets) {
  110 + dataSet.paintBackground(context);
  111 + }
  112 + context.canvas.restoreContext();
  113 + paintBackground(context);
  114 + clip(context, box.size);
  115 + for (Dataset dataSet in datasets) {
  116 + dataSet.paint(context);
  117 + }
  118 + context.canvas.restoreContext();
  119 + _xAxis.paint(context);
  120 + _yAxis.paint(context);
  121 + }
  122 +}
  1 +/*
  2 + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +
  17 +// ignore_for_file: omit_local_variable_types
  18 +
  19 +part of widget;
  20 +
  21 +class ChartLegend extends StatelessWidget {
  22 + ChartLegend({
  23 + this.textStyle,
  24 + this.position = Alignment.topRight,
  25 + this.direction = Axis.vertical,
  26 + this.decoration,
  27 + this.padding = const EdgeInsets.all(5),
  28 + }) : assert(position != null);
  29 +
  30 + final TextStyle textStyle;
  31 +
  32 + final Alignment position;
  33 +
  34 + final Axis direction;
  35 +
  36 + final BoxDecoration decoration;
  37 +
  38 + final EdgeInsets padding;
  39 +
  40 + Widget _buildLegend(Context context, Dataset dataset) {
  41 + final TextStyle style = Theme.of(context).defaultTextStyle.merge(textStyle);
  42 +
  43 + return Row(
  44 + mainAxisSize: MainAxisSize.min,
  45 + children: <Widget>[
  46 + Container(
  47 + width: style.fontSize,
  48 + height: style.fontSize,
  49 + margin: const EdgeInsets.only(right: 5),
  50 + child: dataset.legendeShape(),
  51 + ),
  52 + Text(
  53 + dataset.legend,
  54 + style: textStyle,
  55 + )
  56 + ],
  57 + );
  58 + }
  59 +
  60 + @override
  61 + Widget build(Context context) {
  62 + assert(Chart.of(context) != null,
  63 + '$runtimeType cannot be used without a Chart widget');
  64 +
  65 + final List<Dataset> datasets = Chart.of(context).datasets;
  66 +
  67 + final Widget wrap = Wrap(
  68 + direction: direction,
  69 + spacing: 10,
  70 + runSpacing: 10,
  71 + children: <Widget>[
  72 + for (final Dataset dataset in datasets)
  73 + if (dataset.legend != null) _buildLegend(context, dataset)
  74 + ],
  75 + );
  76 +
  77 + return Align(
  78 + alignment: position,
  79 + child: Container(
  80 + decoration: decoration ?? const BoxDecoration(color: PdfColors.white),
  81 + padding: padding,
  82 + child: wrap,
  83 + ),
  84 + );
  85 + }
  86 +}
@@ -26,12 +26,13 @@ class LineChartValue extends ChartValue { @@ -26,12 +26,13 @@ class LineChartValue extends ChartValue {
26 PdfPoint get point => PdfPoint(x, y); 26 PdfPoint get point => PdfPoint(x, y);
27 } 27 }
28 28
29 -class LineDataSet extends DataSet { 29 +class LineDataSet extends Dataset {
30 LineDataSet({ 30 LineDataSet({
31 @required this.data, 31 @required this.data,
  32 + String legend,
32 this.pointColor, 33 this.pointColor,
33 this.pointSize = 3, 34 this.pointSize = 3,
34 - this.color = PdfColors.blue, 35 + PdfColor color = PdfColors.blue,
35 this.lineWidth = 2, 36 this.lineWidth = 2,
36 this.drawLine = true, 37 this.drawLine = true,
37 this.drawPoints = true, 38 this.drawPoints = true,
@@ -40,12 +41,16 @@ class LineDataSet extends DataSet { @@ -40,12 +41,16 @@ class LineDataSet extends DataSet {
40 this.surfaceColor, 41 this.surfaceColor,
41 this.isCurved = false, 42 this.isCurved = false,
42 this.smoothness = 0.35, 43 this.smoothness = 0.35,
43 - }) : assert(drawLine || drawPoints || drawSurface); 44 + }) : assert(drawLine || drawPoints || drawSurface),
  45 + super(
  46 + legend: legend,
  47 + color: color,
  48 + );
44 49
45 final List<LineChartValue> data; 50 final List<LineChartValue> data;
46 51
47 final bool drawLine; 52 final bool drawLine;
48 - final PdfColor color; 53 +
49 final double lineWidth; 54 final double lineWidth;
50 55
51 final bool drawPoints; 56 final bool drawPoints;
@@ -59,6 +64,12 @@ class LineDataSet extends DataSet { @@ -59,6 +64,12 @@ class LineDataSet extends DataSet {
59 final bool isCurved; 64 final bool isCurved;
60 final double smoothness; 65 final double smoothness;
61 66
  67 + @override
  68 + void layout(Context context, BoxConstraints constraints,
  69 + {bool parentUsesSize = false}) {
  70 + box = PdfRect.fromPoints(PdfPoint.zero, constraints.biggest);
  71 + }
  72 +
62 void _drawLine(Context context, ChartGrid grid, bool moveTo) { 73 void _drawLine(Context context, ChartGrid grid, bool moveTo) {
63 if (data.length < 2) { 74 if (data.length < 2) {
64 return; 75 return;
@@ -66,7 +77,7 @@ class LineDataSet extends DataSet { @@ -66,7 +77,7 @@ class LineDataSet extends DataSet {
66 77
67 PdfPoint t = const PdfPoint(0, 0); 78 PdfPoint t = const PdfPoint(0, 0);
68 79
69 - final PdfPoint p = grid.tochart(data.first.point); 80 + final PdfPoint p = grid.toChart(data.first.point);
70 if (moveTo) { 81 if (moveTo) {
71 context.canvas.moveTo(p.x, p.y); 82 context.canvas.moveTo(p.x, p.y);
72 } else { 83 } else {
@@ -74,16 +85,16 @@ class LineDataSet extends DataSet { @@ -74,16 +85,16 @@ class LineDataSet extends DataSet {
74 } 85 }
75 86
76 for (int i = 1; i < data.length; i++) { 87 for (int i = 1; i < data.length; i++) {
77 - final PdfPoint p = grid.tochart(data[i].point); 88 + final PdfPoint p = grid.toChart(data[i].point);
78 89
79 if (!isCurved) { 90 if (!isCurved) {
80 context.canvas.lineTo(p.x, p.y); 91 context.canvas.lineTo(p.x, p.y);
81 continue; 92 continue;
82 } 93 }
83 94
84 - final PdfPoint pp = grid.tochart(data[i - 1].point); 95 + final PdfPoint pp = grid.toChart(data[i - 1].point);
85 final PdfPoint pn = 96 final PdfPoint pn =
86 - grid.tochart(data[i + 1 < data.length ? i + 1 : i].point); 97 + grid.toChart(data[i + 1 < data.length ? i + 1 : i].point);
87 98
88 final PdfPoint c1 = PdfPoint(pp.x + t.x, pp.y + t.y); 99 final PdfPoint c1 = PdfPoint(pp.x + t.x, pp.y + t.y);
89 100
@@ -101,28 +112,30 @@ class LineDataSet extends DataSet { @@ -101,28 +112,30 @@ class LineDataSet extends DataSet {
101 return; 112 return;
102 } 113 }
103 114
104 - final double y = (grid is LinearGrid) ? grid.xAxisOffset : 0; 115 + final double y = (grid is CartesianGrid) ? grid.xAxisOffset : 0;
105 _drawLine(context, grid, true); 116 _drawLine(context, grid, true);
106 117
107 - final PdfPoint pe = grid.tochart(data.last.point); 118 + final PdfPoint pe = grid.toChart(data.last.point);
108 context.canvas.lineTo(pe.x, y); 119 context.canvas.lineTo(pe.x, y);
109 - final PdfPoint pf = grid.tochart(data.first.point); 120 + final PdfPoint pf = grid.toChart(data.first.point);
110 context.canvas.lineTo(pf.x, y); 121 context.canvas.lineTo(pf.x, y);
111 } 122 }
112 123
113 void _drawPoints(Context context, ChartGrid grid) { 124 void _drawPoints(Context context, ChartGrid grid) {
114 for (final LineChartValue value in data) { 125 for (final LineChartValue value in data) {
115 - final PdfPoint p = grid.tochart(value.point); 126 + final PdfPoint p = grid.toChart(value.point);
116 context.canvas.drawEllipse(p.x, p.y, pointSize, pointSize); 127 context.canvas.drawEllipse(p.x, p.y, pointSize, pointSize);
117 } 128 }
118 } 129 }
119 130
120 @override 131 @override
121 - void paintBackground(Context context, ChartGrid grid) { 132 + void paintBackground(Context context) {
122 if (data.isEmpty) { 133 if (data.isEmpty) {
123 return; 134 return;
124 } 135 }
125 136
  137 + final ChartGrid grid = Chart.of(context).grid;
  138 +
126 if (drawSurface) { 139 if (drawSurface) {
127 _drawSurface(context, grid); 140 _drawSurface(context, grid);
128 141
@@ -145,11 +158,15 @@ class LineDataSet extends DataSet { @@ -145,11 +158,15 @@ class LineDataSet extends DataSet {
145 } 158 }
146 159
147 @override 160 @override
148 - void paintForeground(Context context, ChartGrid grid) { 161 + void paint(Context context) {
  162 + super.paint(context);
  163 +
149 if (data.isEmpty) { 164 if (data.isEmpty) {
150 return; 165 return;
151 } 166 }
152 167
  168 + final ChartGrid grid = Chart.of(context).grid;
  169 +
153 if (drawLine) { 170 if (drawLine) {
154 _drawLine(context, grid, true); 171 _drawLine(context, grid, true);
155 172
1 -/*  
2 - * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com>  
3 - *  
4 - * Licensed under the Apache License, Version 2.0 (the "License");  
5 - * you may not use this file except in compliance with the License.  
6 - * You may obtain a copy of the License at  
7 - *  
8 - * http://www.apache.org/licenses/LICENSE-2.0  
9 - *  
10 - * Unless required by applicable law or agreed to in writing, software  
11 - * distributed under the License is distributed on an "AS IS" BASIS,  
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
13 - * See the License for the specific language governing permissions and  
14 - * limitations under the License.  
15 - */  
16 -  
17 -// ignore_for_file: omit_local_variable_types  
18 -  
19 -part of widget;  
20 -  
21 -typedef GridAxisFormat = String Function(double value);  
22 -  
23 -class LinearGrid extends ChartGrid {  
24 - LinearGrid({  
25 - @required this.xAxis,  
26 - @required this.yAxis,  
27 - this.xMargin = 10,  
28 - this.yMargin = 2,  
29 - this.textStyle,  
30 - this.lineWidth = 1,  
31 - this.color = PdfColors.black,  
32 - this.separatorLineWidth = .5,  
33 - this.drawXDivisions = false,  
34 - this.drawYDivisions = true,  
35 - this.separatorColor = PdfColors.grey,  
36 - this.xAxisFormat = _defaultFormat,  
37 - this.yAxisFormat = _defaultFormat,  
38 - }) : assert(_isSortedAscending(xAxis)),  
39 - assert(_isSortedAscending(yAxis));  
40 -  
41 - final List<double> xAxis;  
42 - final List<double> yAxis;  
43 - final double xMargin;  
44 - final double yMargin;  
45 - final TextStyle textStyle;  
46 - final double lineWidth;  
47 - final PdfColor color;  
48 - final double separatorLineWidth;  
49 - final PdfColor separatorColor;  
50 - final bool drawXDivisions;  
51 - final bool drawYDivisions;  
52 - final GridAxisFormat xAxisFormat;  
53 - final GridAxisFormat yAxisFormat;  
54 -  
55 - TextStyle style;  
56 - PdfFont font;  
57 - PdfRect gridBox;  
58 - double xOffset;  
59 - double xTotal;  
60 - double yOffset;  
61 - double yTotal;  
62 -  
63 - static String _defaultFormat(double v) => v.toString();  
64 -  
65 - static bool _isSortedAscending(List<double> list) {  
66 - double prev = list.first;  
67 - for (final double elem in list) {  
68 - if (prev > elem) {  
69 - return false;  
70 - }  
71 - prev = elem;  
72 - }  
73 - return true;  
74 - }  
75 -  
76 - @override  
77 - void layout(Context context, PdfPoint size) {  
78 - style = Theme.of(context).defaultTextStyle.merge(textStyle);  
79 - font = style.font.getFont(context);  
80 -  
81 - double xMaxWidth = 0;  
82 - double xMaxHeight = 0;  
83 - for (final double value in xAxis) {  
84 - final PdfFontMetrics metrics =  
85 - font.stringMetrics(xAxisFormat(value)) * style.fontSize;  
86 - xMaxWidth = math.max(xMaxWidth, metrics.width);  
87 - xMaxHeight = math.max(xMaxHeight, metrics.maxHeight);  
88 - }  
89 -  
90 - double yMaxWidth = 0;  
91 - double yMaxHeight = 0;  
92 - for (final double value in yAxis) {  
93 - final PdfFontMetrics metrics =  
94 - font.stringMetrics(yAxisFormat(value)) * style.fontSize;  
95 - yMaxWidth = math.max(yMaxWidth, metrics.width);  
96 - yMaxHeight = math.max(yMaxHeight, metrics.maxHeight);  
97 - }  
98 -  
99 - gridBox = PdfRect.fromLTRB(  
100 - yMaxWidth + xMargin,  
101 - xMaxHeight + yMargin,  
102 - size.x - xMaxWidth / 2,  
103 - size.y - yMaxHeight / 2,  
104 - );  
105 -  
106 - xOffset = xAxis.reduce(math.min);  
107 - yOffset = yAxis.reduce(math.min);  
108 - xTotal = xAxis.reduce(math.max) - xOffset;  
109 - yTotal = yAxis.reduce(math.max) - yOffset;  
110 - }  
111 -  
112 - @override  
113 - PdfPoint tochart(PdfPoint p) {  
114 - return PdfPoint(  
115 - gridBox.left + gridBox.width * (p.x - xOffset) / xTotal,  
116 - gridBox.bottom + gridBox.height * (p.y - yOffset) / yTotal,  
117 - );  
118 - }  
119 -  
120 - double get xAxisOffset => gridBox.bottom;  
121 -  
122 - double get yAxisOffset => gridBox.left;  
123 -  
124 - void _drawAxis(Context context, PdfPoint size) {  
125 - context.canvas  
126 - ..moveTo(size.x, gridBox.bottom)  
127 - ..lineTo(gridBox.left - xMargin / 2, gridBox.bottom)  
128 - ..moveTo(gridBox.left, gridBox.bottom)  
129 - ..lineTo(gridBox.left, size.y)  
130 - ..setStrokeColor(color)  
131 - ..setLineWidth(lineWidth)  
132 - ..setLineCap(PdfLineCap.joinMiter)  
133 - ..strokePath();  
134 - }  
135 -  
136 - void _drawYDivisions(Context context, PdfPoint size) {  
137 - for (final double y in yAxis.sublist(1)) {  
138 - final PdfPoint p = tochart(PdfPoint(0, y));  
139 - context.canvas.drawLine(  
140 - gridBox.left,  
141 - p.y,  
142 - size.x,  
143 - p.y,  
144 - );  
145 - }  
146 -  
147 - context.canvas  
148 - ..setStrokeColor(separatorColor)  
149 - ..setLineWidth(separatorLineWidth)  
150 - ..setLineCap(PdfLineCap.joinMiter)  
151 - ..strokePath();  
152 - }  
153 -  
154 - void _drawXDivisions(Context context, PdfPoint size) {  
155 - for (final double x in xAxis.sublist(1)) {  
156 - final PdfPoint p = tochart(PdfPoint(x, 0));  
157 - context.canvas.drawLine(  
158 - p.x,  
159 - size.y,  
160 - p.x,  
161 - gridBox.bottom,  
162 - );  
163 - }  
164 -  
165 - context.canvas  
166 - ..setStrokeColor(separatorColor)  
167 - ..setLineWidth(separatorLineWidth)  
168 - ..setLineCap(PdfLineCap.joinMiter)  
169 - ..strokePath();  
170 - }  
171 -  
172 - void _drawYValues(Context context, PdfPoint size) {  
173 - for (final double y in yAxis) {  
174 - final String v = yAxisFormat(y);  
175 - final PdfFontMetrics metrics = font.stringMetrics(v) * style.fontSize;  
176 - final PdfPoint p = tochart(PdfPoint(0, y));  
177 -  
178 - context.canvas  
179 - ..setColor(style.color)  
180 - ..drawString(  
181 - style.font.getFont(context),  
182 - style.fontSize,  
183 - v,  
184 - gridBox.left - xMargin - metrics.width,  
185 - p.y - (metrics.ascent + metrics.descent) / 2,  
186 - );  
187 - }  
188 - }  
189 -  
190 - void _drawXValues(Context context, PdfPoint size) {  
191 - for (final double x in xAxis) {  
192 - final String v = xAxisFormat(x);  
193 - final PdfFontMetrics metrics = font.stringMetrics(v) * style.fontSize;  
194 - final PdfPoint p = tochart(PdfPoint(x, 0));  
195 -  
196 - context.canvas  
197 - ..setColor(style.color)  
198 - ..drawString(  
199 - style.font.getFont(context),  
200 - style.fontSize,  
201 - v,  
202 - p.x - metrics.width / 2,  
203 - -metrics.descent,  
204 - );  
205 - }  
206 - }  
207 -  
208 - @override  
209 - void paintBackground(Context context, PdfPoint size) {}  
210 -  
211 - @override  
212 - void paint(Context context, PdfPoint size) {  
213 - if (drawXDivisions) {  
214 - _drawXDivisions(context, size);  
215 - }  
216 - if (drawYDivisions) {  
217 - _drawYDivisions(context, size);  
218 - }  
219 - }  
220 -  
221 - @override  
222 - void paintForeground(Context context, PdfPoint size) {  
223 - _drawAxis(context, size);  
224 - _drawXValues(context, size);  
225 - _drawYValues(context, size);  
226 - }  
227 -  
228 - @override  
229 - void clip(Context context, PdfPoint size) {  
230 - context.canvas  
231 - ..saveContext()  
232 - ..drawRect(  
233 - gridBox.left,  
234 - gridBox.bottom,  
235 - size.x - gridBox.left,  
236 - size.y - gridBox.bottom,  
237 - )  
238 - ..clipPath();  
239 - }  
240 -  
241 - @override  
242 - void unClip(Context context, PdfPoint size) {  
243 - context.canvas.restoreContext();  
244 - }  
245 -}  
@@ -35,11 +35,11 @@ void main() { @@ -35,11 +35,11 @@ void main() {
35 pdf.addPage(Page( 35 pdf.addPage(Page(
36 pageFormat: PdfPageFormat.standard.landscape, 36 pageFormat: PdfPageFormat.standard.landscape,
37 build: (Context context) => Chart( 37 build: (Context context) => Chart(
38 - grid: LinearGrid(  
39 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
40 - yAxis: <double>[0, 3, 6, 9], 38 + grid: CartesianGrid(
  39 + xAxis: FixedAxis<int>(<int>[0, 1, 2, 3, 4, 5, 6]),
  40 + yAxis: FixedAxis<int>(<int>[0, 3, 6, 9], divisions: true),
41 ), 41 ),
42 - data: <DataSet>[ 42 + datasets: <Dataset>[
43 LineDataSet( 43 LineDataSet(
44 data: const <LineChartValue>[ 44 data: const <LineChartValue>[
45 LineChartValue(1, 1), 45 LineChartValue(1, 1),
@@ -56,11 +56,11 @@ void main() { @@ -56,11 +56,11 @@ void main() {
56 pdf.addPage(Page( 56 pdf.addPage(Page(
57 pageFormat: PdfPageFormat.standard.landscape, 57 pageFormat: PdfPageFormat.standard.landscape,
58 build: (Context context) => Chart( 58 build: (Context context) => Chart(
59 - grid: LinearGrid(  
60 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
61 - yAxis: <double>[0, 3, 6, 9], 59 + grid: CartesianGrid(
  60 + xAxis: FixedAxis<int>(<int>[0, 1, 2, 3, 4, 5, 6]),
  61 + yAxis: FixedAxis<int>(<int>[0, 3, 6, 9], divisions: true),
62 ), 62 ),
63 - data: <DataSet>[ 63 + datasets: <Dataset>[
64 LineDataSet( 64 LineDataSet(
65 data: const <LineChartValue>[ 65 data: const <LineChartValue>[
66 LineChartValue(1, 1), 66 LineChartValue(1, 1),
@@ -77,11 +77,11 @@ void main() { @@ -77,11 +77,11 @@ void main() {
77 test('Default ScatterChart without dots', () { 77 test('Default ScatterChart without dots', () {
78 pdf.addPage(Page( 78 pdf.addPage(Page(
79 build: (Context context) => Chart( 79 build: (Context context) => Chart(
80 - grid: LinearGrid(  
81 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
82 - yAxis: <double>[0, 3, 6, 9], 80 + grid: CartesianGrid(
  81 + xAxis: FixedAxis<int>(<int>[0, 1, 2, 3, 4, 5, 6]),
  82 + yAxis: FixedAxis<int>(<int>[0, 3, 6, 9], divisions: true),
83 ), 83 ),
84 - data: <DataSet>[ 84 + datasets: <Dataset>[
85 LineDataSet( 85 LineDataSet(
86 data: const <LineChartValue>[ 86 data: const <LineChartValue>[
87 LineChartValue(1, 1), 87 LineChartValue(1, 1),
@@ -99,11 +99,11 @@ void main() { @@ -99,11 +99,11 @@ void main() {
99 pdf.addPage(Page( 99 pdf.addPage(Page(
100 pageFormat: PdfPageFormat.standard.landscape, 100 pageFormat: PdfPageFormat.standard.landscape,
101 build: (Context context) => Chart( 101 build: (Context context) => Chart(
102 - grid: LinearGrid(  
103 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
104 - yAxis: <double>[0, 3, 6, 9], 102 + grid: CartesianGrid(
  103 + xAxis: FixedAxis<int>(<int>[0, 1, 2, 3, 4, 5, 6]),
  104 + yAxis: FixedAxis<int>(<int>[0, 3, 6, 9], divisions: true),
105 ), 105 ),
106 - data: <DataSet>[ 106 + datasets: <Dataset>[
107 LineDataSet( 107 LineDataSet(
108 data: const <LineChartValue>[ 108 data: const <LineChartValue>[
109 LineChartValue(1, 1), 109 LineChartValue(1, 1),
@@ -128,11 +128,11 @@ void main() { @@ -128,11 +128,11 @@ void main() {
128 width: 200, 128 width: 200,
129 height: 100, 129 height: 100,
130 child: Chart( 130 child: Chart(
131 - grid: LinearGrid(  
132 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
133 - yAxis: <double>[0, 3, 6, 9], 131 + grid: CartesianGrid(
  132 + xAxis: FixedAxis<int>(<int>[0, 1, 2, 3, 4, 5, 6]),
  133 + yAxis: FixedAxis<int>(<int>[0, 3, 6, 9], divisions: true),
134 ), 134 ),
135 - data: <DataSet>[ 135 + datasets: <Dataset>[
136 LineDataSet( 136 LineDataSet(
137 data: const <LineChartValue>[ 137 data: const <LineChartValue>[
138 LineChartValue(1, 1), 138 LineChartValue(1, 1),
@@ -150,11 +150,11 @@ void main() { @@ -150,11 +150,11 @@ void main() {
150 pdf.addPage(Page( 150 pdf.addPage(Page(
151 pageFormat: PdfPageFormat.standard.landscape, 151 pageFormat: PdfPageFormat.standard.landscape,
152 build: (Context context) => Chart( 152 build: (Context context) => Chart(
153 - grid: LinearGrid(  
154 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
155 - yAxis: <double>[0, 3, 6, 9], 153 + grid: CartesianGrid(
  154 + xAxis: FixedAxis<int>(<int>[0, 1, 2, 3, 4, 5, 6]),
  155 + yAxis: FixedAxis<int>(<int>[0, 3, 6, 9], divisions: true),
156 ), 156 ),
157 - data: <DataSet>[ 157 + datasets: <Dataset>[
158 LineDataSet( 158 LineDataSet(
159 drawPoints: false, 159 drawPoints: false,
160 isCurved: true, 160 isCurved: true,