Showing
4 changed files
with
125 additions
and
107 deletions
@@ -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 | }); |
test/golden/widgets-chart.pdf
0 → 100644
No preview for this file type
-
Please register or login to post a comment