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