David PHAM-VAN

Improve Chart widget

@@ -33,36 +33,39 @@ class Chart extends Widget { @@ -33,36 +33,39 @@ class Chart extends Widget {
33 Chart({ 33 Chart({
34 @required this.grid, 34 @required this.grid,
35 @required this.data, 35 @required this.data,
36 - this.width = 500,  
37 - this.height = 250,  
38 - this.fit = BoxFit.contain,  
39 }); 36 });
40 37
41 - final double width;  
42 - final double height;  
43 - final BoxFit fit;  
44 - final Grid grid; 38 + final ChartGrid grid;
  39 +
45 final List<DataSet> data; 40 final List<DataSet> data;
46 - PdfRect gridBox; 41 +
  42 + PdfPoint _computeSize(BoxConstraints constraints) {
  43 + if (constraints.isTight) {
  44 + return constraints.smallest;
  45 + }
  46 +
  47 + double width = constraints.maxWidth;
  48 + double height = constraints.maxHeight;
  49 +
  50 + const double aspectRatio = 1;
  51 +
  52 + if (!width.isFinite) {
  53 + width = height * aspectRatio;
  54 + }
  55 +
  56 + if (!height.isFinite) {
  57 + height = width * aspectRatio;
  58 + }
  59 +
  60 + return constraints.constrain(PdfPoint(width, height));
  61 + }
47 62
48 @override 63 @override
49 void layout(Context context, BoxConstraints constraints, 64 void layout(Context context, BoxConstraints constraints,
50 {bool parentUsesSize = false}) { 65 {bool parentUsesSize = false}) {
51 - final double w = constraints.hasBoundedWidth  
52 - ? constraints.maxWidth  
53 - : constraints.constrainWidth(width.toDouble());  
54 - final double h = constraints.hasBoundedHeight  
55 - ? constraints.maxHeight  
56 - : constraints.constrainHeight(height.toDouble());  
57 -  
58 - final FittedSizes sizes =  
59 - applyBoxFit(fit, PdfPoint(width, height), PdfPoint(w, h));  
60 -  
61 - box = PdfRect.fromPoints(PdfPoint.zero, sizes.destination);  
62 - grid.layout(context, box);  
63 - for (DataSet dataSet in data) {  
64 - dataSet.layout(context, grid.gridBox);  
65 - } 66 + box = PdfRect.fromPoints(PdfPoint.zero, _computeSize(constraints));
  67 +
  68 + grid.layout(context, box.size);
66 } 69 }
67 70
68 @override 71 @override
@@ -75,26 +78,27 @@ class Chart extends Widget { @@ -75,26 +78,27 @@ class Chart extends Widget {
75 ..saveContext() 78 ..saveContext()
76 ..setTransform(mat); 79 ..setTransform(mat);
77 80
78 - grid.paint(context, box); 81 + grid.paintBackground(context, box.size);
79 for (DataSet dataSet in data) { 82 for (DataSet dataSet in data) {
80 - dataSet.paint(context, grid); 83 + dataSet.paintBackground(context, grid);
81 } 84 }
  85 + for (DataSet dataSet in data) {
  86 + dataSet.paintForeground(context, grid);
  87 + }
  88 + grid.paintForeground(context, box.size);
82 context.canvas.restoreContext(); 89 context.canvas.restoreContext();
83 } 90 }
84 } 91 }
85 92
86 -abstract class Grid {  
87 - PdfRect gridBox;  
88 - double xOffset;  
89 - double xTotal;  
90 - double yOffset;  
91 - double yTotal; 93 +abstract class ChartGrid {
  94 + void layout(Context context, PdfPoint size);
  95 + void paintBackground(Context context, PdfPoint size);
  96 + void paintForeground(Context context, PdfPoint size);
92 97
93 - void layout(Context context, PdfRect box);  
94 - void paint(Context context, PdfRect box); 98 + PdfPoint tochart(PdfPoint p);
95 } 99 }
96 100
97 -class LinearGrid extends Grid { 101 +class LinearGrid extends ChartGrid {
98 LinearGrid({ 102 LinearGrid({
99 @required this.xAxis, 103 @required this.xAxis,
100 @required this.yAxis, 104 @required this.yAxis,
@@ -115,16 +119,29 @@ class LinearGrid extends Grid { @@ -115,16 +119,29 @@ class LinearGrid extends Grid {
115 final TextStyle textStyle; 119 final TextStyle textStyle;
116 final double lineWidth; 120 final double lineWidth;
117 final PdfColor color; 121 final PdfColor color;
  122 + final double separatorLineWidth;
  123 + final PdfColor separatorColor;
118 124
119 TextStyle style; 125 TextStyle style;
120 PdfFont font; 126 PdfFont font;
121 PdfFontMetrics xAxisFontMetric; 127 PdfFontMetrics xAxisFontMetric;
122 PdfFontMetrics yAxisFontMetric; 128 PdfFontMetrics yAxisFontMetric;
123 - double separatorLineWidth;  
124 - PdfColor separatorColor; 129 + PdfRect gridBox;
  130 + double xOffset;
  131 + double xTotal;
  132 + double yOffset;
  133 + double yTotal;
  134 +
  135 + @override
  136 + PdfPoint tochart(PdfPoint p) {
  137 + return PdfPoint(
  138 + gridBox.left + gridBox.width * (p.x - xOffset) / xTotal,
  139 + gridBox.bottom + gridBox.height * (p.y - yOffset) / yTotal,
  140 + );
  141 + }
125 142
126 @override 143 @override
127 - void layout(Context context, PdfRect box) { 144 + void layout(Context context, PdfPoint size) {
128 style = Theme.of(context).defaultTextStyle.merge(textStyle); 145 style = Theme.of(context).defaultTextStyle.merge(textStyle);
129 font = style.font.getFont(context); 146 font = style.font.getFont(context);
130 147
@@ -136,10 +153,10 @@ class LinearGrid extends Grid { @@ -136,10 +153,10 @@ class LinearGrid extends Grid {
136 (style.fontSize); 153 (style.fontSize);
137 154
138 gridBox = PdfRect.fromLTRB( 155 gridBox = PdfRect.fromLTRB(
139 - box.left + yAxisFontMetric.width + xMargin,  
140 - box.bottom + xAxisFontMetric.height + yMargin,  
141 - box.right - xAxisFontMetric.width / 2,  
142 - box.top - yAxisFontMetric.height / 2); 156 + yAxisFontMetric.width + xMargin,
  157 + xAxisFontMetric.height + yMargin,
  158 + size.x - xAxisFontMetric.width / 2,
  159 + size.y - yAxisFontMetric.height / 2);
143 160
144 xOffset = xAxis.reduce(math.min); 161 xOffset = xAxis.reduce(math.min);
145 yOffset = yAxis.reduce(math.min); 162 yOffset = yAxis.reduce(math.min);
@@ -148,7 +165,7 @@ class LinearGrid extends Grid { @@ -148,7 +165,7 @@ class LinearGrid extends Grid {
148 } 165 }
149 166
150 @override 167 @override
151 - void paint(Context context, PdfRect box) { 168 + void paintBackground(Context context, PdfPoint size) {
152 xAxis.asMap().forEach((int i, double x) { 169 xAxis.asMap().forEach((int i, double x) {
153 context.canvas 170 context.canvas
154 ..setColor(style.color) 171 ..setColor(style.color)
@@ -195,63 +212,59 @@ class LinearGrid extends Grid { @@ -195,63 +212,59 @@ class LinearGrid extends Grid {
195 ..drawLine(gridBox.left, gridBox.bottom, gridBox.left, gridBox.top) 212 ..drawLine(gridBox.left, gridBox.bottom, gridBox.left, gridBox.top)
196 ..strokePath(); 213 ..strokePath();
197 } 214 }
  215 +
  216 + @override
  217 + void paintForeground(Context context, PdfPoint size) {}
198 } 218 }
199 219
200 -class ChartValue {  
201 - ChartValue(this.x, this.y); 220 +@immutable
  221 +abstract class ChartValue {
  222 + const ChartValue();
  223 +}
  224 +
  225 +class LineChartValue extends ChartValue {
  226 + const LineChartValue(this.x, this.y);
202 final double x; 227 final double x;
203 final double y; 228 final double y;
  229 +
  230 + PdfPoint get point => PdfPoint(x, y);
204 } 231 }
205 232
206 abstract class DataSet { 233 abstract class DataSet {
207 - void layout(Context context, PdfRect box);  
208 - void paint(Context context, Grid grid); 234 + void paintBackground(Context context, ChartGrid grid);
  235 + void paintForeground(Context context, ChartGrid grid);
209 } 236 }
210 237
211 class LineDataSet extends DataSet { 238 class LineDataSet extends DataSet {
212 LineDataSet({ 239 LineDataSet({
213 @required this.data, 240 @required this.data,
214 - this.pointColor = PdfColors.green,  
215 - this.pointSize = 8, 241 + this.pointColor = PdfColors.blue,
  242 + this.pointSize = 3,
216 this.lineColor = PdfColors.blue, 243 this.lineColor = PdfColors.blue,
217 this.lineWidth = 2, 244 this.lineWidth = 2,
218 this.drawLine = true, 245 this.drawLine = true,
219 this.drawPoints = true, 246 this.drawPoints = true,
220 - this.lineStartingPoint,  
221 }) : assert(drawLine || drawPoints); 247 }) : assert(drawLine || drawPoints);
222 248
223 - final List<ChartValue> data; 249 + final List<LineChartValue> data;
224 final PdfColor pointColor; 250 final PdfColor pointColor;
225 final double pointSize; 251 final double pointSize;
226 final PdfColor lineColor; 252 final PdfColor lineColor;
227 final double lineWidth; 253 final double lineWidth;
228 final bool drawLine; 254 final bool drawLine;
229 final bool drawPoints; 255 final bool drawPoints;
230 - final ChartValue lineStartingPoint;  
231 256
232 double maxValue; 257 double maxValue;
233 258
234 @override 259 @override
235 - void layout(Context context, PdfRect box) {}  
236 -  
237 - @override  
238 - void paint(Context context, Grid grid) { 260 + void paintBackground(Context context, ChartGrid grid) {
239 if (drawLine) { 261 if (drawLine) {
240 - ChartValue lastValue = lineStartingPoint;  
241 - for (ChartValue value in data) { 262 + LineChartValue lastValue;
  263 + for (LineChartValue value in data) {
242 if (lastValue != null) { 264 if (lastValue != null) {
243 - context.canvas.drawLine(  
244 - grid.gridBox.left +  
245 - grid.gridBox.width * (lastValue.x - grid.xOffset) / grid.xTotal,  
246 - grid.gridBox.bottom +  
247 - grid.gridBox.height *  
248 - (lastValue.y - grid.yOffset) /  
249 - grid.yTotal,  
250 - grid.gridBox.left +  
251 - grid.gridBox.width * (value.x - grid.xOffset) / grid.xTotal,  
252 - grid.gridBox.bottom +  
253 - grid.gridBox.height * (value.y - grid.yOffset) / grid.yTotal,  
254 - ); 265 + final PdfPoint p1 = grid.tochart(lastValue.point);
  266 + final PdfPoint p2 = grid.tochart(value.point);
  267 + context.canvas.drawLine(p1.x, p1.y, p2.x, p2.y);
255 } 268 }
256 lastValue = value; 269 lastValue = value;
257 } 270 }
@@ -265,18 +278,16 @@ class LineDataSet extends DataSet { @@ -265,18 +278,16 @@ class LineDataSet extends DataSet {
265 } 278 }
266 279
267 if (drawPoints) { 280 if (drawPoints) {
268 - for (ChartValue value in data) { 281 + for (LineChartValue value in data) {
  282 + final PdfPoint p = grid.tochart(value.point);
269 context.canvas 283 context.canvas
270 ..setColor(pointColor) 284 ..setColor(pointColor)
271 - ..drawEllipse(  
272 - grid.gridBox.left +  
273 - grid.gridBox.width * (value.x - grid.xOffset) / grid.xTotal,  
274 - grid.gridBox.bottom +  
275 - grid.gridBox.height * (value.y - grid.yOffset) / grid.yTotal,  
276 - pointSize,  
277 - pointSize) 285 + ..drawEllipse(p.x, p.y, pointSize, pointSize)
278 ..fillPath(); 286 ..fillPath();
279 } 287 }
280 } 288 }
281 } 289 }
  290 +
  291 + @override
  292 + void paintForeground(Context context, ChartGrid grid) {}
282 } 293 }
@@ -31,6 +31,7 @@ import 'ttf_test.dart' as ttf; @@ -31,6 +31,7 @@ import 'ttf_test.dart' as ttf;
31 import 'type1_test.dart' as type1; 31 import 'type1_test.dart' as type1;
32 import 'widget_barcode_test.dart' as widget_barcode; 32 import 'widget_barcode_test.dart' as widget_barcode;
33 import 'widget_basic_test.dart' as widget_basic; 33 import 'widget_basic_test.dart' as widget_basic;
  34 +import 'widget_chart_test.dart' as widget_chart;
34 import 'widget_clip_test.dart' as widget_clip; 35 import 'widget_clip_test.dart' as widget_clip;
35 import 'widget_container_test.dart' as widget_container; 36 import 'widget_container_test.dart' as widget_container;
36 import 'widget_flex_test.dart' as widget_flex; 37 import 'widget_flex_test.dart' as widget_flex;
@@ -59,8 +60,9 @@ void main() { @@ -59,8 +60,9 @@ void main() {
59 roll.main(); 60 roll.main();
60 ttf.main(); 61 ttf.main();
61 type1.main(); 62 type1.main();
62 - widget_basic.main();  
63 widget_barcode.main(); 63 widget_barcode.main();
  64 + widget_basic.main();
  65 + widget_chart.main();
64 widget_clip.main(); 66 widget_clip.main();
65 widget_container.main(); 67 widget_container.main();
66 widget_flex.main(); 68 widget_flex.main();
@@ -40,10 +40,10 @@ void main() { @@ -40,10 +40,10 @@ void main() {
40 ), 40 ),
41 data: <DataSet>[ 41 data: <DataSet>[
42 LineDataSet( 42 LineDataSet(
43 - data: <ChartValue>[  
44 - ChartValue(1, 1),  
45 - ChartValue(2, 3),  
46 - ChartValue(3, 7), 43 + data: const <LineChartValue>[
  44 + LineChartValue(1, 1),
  45 + LineChartValue(2, 3),
  46 + LineChartValue(3, 7),
47 ], 47 ],
48 ), 48 ),
49 ], 49 ],
@@ -60,10 +60,10 @@ void main() { @@ -60,10 +60,10 @@ void main() {
60 ), 60 ),
61 data: <DataSet>[ 61 data: <DataSet>[
62 LineDataSet( 62 LineDataSet(
63 - data: <ChartValue>[  
64 - ChartValue(1, 1),  
65 - ChartValue(2, 3),  
66 - ChartValue(3, 7), 63 + data: const <LineChartValue>[
  64 + LineChartValue(1, 1),
  65 + LineChartValue(2, 3),
  66 + LineChartValue(3, 7),
67 ], 67 ],
68 drawLine: false, 68 drawLine: false,
69 ), 69 ),
@@ -81,10 +81,10 @@ void main() { @@ -81,10 +81,10 @@ void main() {
81 ), 81 ),
82 data: <DataSet>[ 82 data: <DataSet>[
83 LineDataSet( 83 LineDataSet(
84 - data: <ChartValue>[  
85 - ChartValue(1, 1),  
86 - ChartValue(2, 3),  
87 - ChartValue(3, 7), 84 + data: const <LineChartValue>[
  85 + LineChartValue(1, 1),
  86 + LineChartValue(2, 3),
  87 + LineChartValue(3, 7),
88 ], 88 ],
89 drawPoints: false, 89 drawPoints: false,
90 ), 90 ),
@@ -102,10 +102,10 @@ void main() { @@ -102,10 +102,10 @@ void main() {
102 ), 102 ),
103 data: <DataSet>[ 103 data: <DataSet>[
104 LineDataSet( 104 LineDataSet(
105 - data: <ChartValue>[  
106 - ChartValue(1, 1),  
107 - ChartValue(2, 3),  
108 - ChartValue(3, 7), 105 + data: const <LineChartValue>[
  106 + LineChartValue(1, 1),
  107 + LineChartValue(2, 3),
  108 + LineChartValue(3, 7),
109 ], 109 ],
110 drawLine: false, 110 drawLine: false,
111 pointColor: PdfColors.red, 111 pointColor: PdfColors.red,
@@ -120,22 +120,24 @@ void main() { @@ -120,22 +120,24 @@ void main() {
120 120
121 test('ScatterChart with custom size', () { 121 test('ScatterChart with custom size', () {
122 pdf.addPage(Page( 122 pdf.addPage(Page(
123 - build: (Context context) => Chart( 123 + build: (Context context) => SizedBox(
124 width: 200, 124 width: 200,
125 height: 100, 125 height: 100,
126 - grid: LinearGrid(  
127 - xAxis: <double>[0, 1, 2, 3, 4, 5, 6],  
128 - yAxis: <double>[0, 3, 6, 9],  
129 - ),  
130 - data: <DataSet>[  
131 - LineDataSet(  
132 - data: <ChartValue>[  
133 - ChartValue(1, 1),  
134 - ChartValue(2, 3),  
135 - ChartValue(3, 7),  
136 - ], 126 + child: Chart(
  127 + grid: LinearGrid(
  128 + xAxis: <double>[0, 1, 2, 3, 4, 5, 6],
  129 + yAxis: <double>[0, 3, 6, 9],
137 ), 130 ),
138 - ], 131 + data: <DataSet>[
  132 + LineDataSet(
  133 + data: const <LineChartValue>[
  134 + LineChartValue(1, 1),
  135 + LineChartValue(2, 3),
  136 + LineChartValue(3, 7),
  137 + ],
  138 + ),
  139 + ],
  140 + ),
139 ), 141 ),
140 )); 142 ));
141 }); 143 });
No preview for this file type