Showing
2 changed files
with
162 additions
and
24 deletions
@@ -35,7 +35,6 @@ class ChartLegend extends StatelessWidget { | @@ -35,7 +35,6 @@ class ChartLegend extends StatelessWidget { | ||
35 | this.direction = Axis.vertical, | 35 | this.direction = Axis.vertical, |
36 | this.decoration, | 36 | this.decoration, |
37 | this.padding = const EdgeInsets.all(5), | 37 | this.padding = const EdgeInsets.all(5), |
38 | - this.maxWidth = 200, | ||
39 | }); | 38 | }); |
40 | 39 | ||
41 | final TextStyle? textStyle; | 40 | final TextStyle? textStyle; |
@@ -48,8 +47,6 @@ class ChartLegend extends StatelessWidget { | @@ -48,8 +47,6 @@ class ChartLegend extends StatelessWidget { | ||
48 | 47 | ||
49 | final EdgeInsets padding; | 48 | final EdgeInsets padding; |
50 | 49 | ||
51 | - final double maxWidth; | ||
52 | - | ||
53 | Widget _buildLegend(Context context, Dataset dataset) { | 50 | Widget _buildLegend(Context context, Dataset dataset) { |
54 | final style = Theme.of(context).defaultTextStyle.merge(textStyle); | 51 | final style = Theme.of(context).defaultTextStyle.merge(textStyle); |
55 | 52 | ||
@@ -62,13 +59,9 @@ class ChartLegend extends StatelessWidget { | @@ -62,13 +59,9 @@ class ChartLegend extends StatelessWidget { | ||
62 | margin: const EdgeInsets.only(right: 5), | 59 | margin: const EdgeInsets.only(right: 5), |
63 | child: dataset.legendShape(), | 60 | child: dataset.legendShape(), |
64 | ), | 61 | ), |
65 | - ConstrainedBox( | ||
66 | - constraints: BoxConstraints(maxWidth: maxWidth), | ||
67 | - child: Text( | 62 | + Text( |
68 | dataset.legend!, | 63 | dataset.legend!, |
69 | style: textStyle, | 64 | style: textStyle, |
70 | - softWrap: false, | ||
71 | - ), | ||
72 | ), | 65 | ), |
73 | ], | 66 | ], |
74 | ); | 67 | ); |
@@ -82,6 +75,9 @@ class ChartLegend extends StatelessWidget { | @@ -82,6 +75,9 @@ class ChartLegend extends StatelessWidget { | ||
82 | direction: direction, | 75 | direction: direction, |
83 | spacing: 10, | 76 | spacing: 10, |
84 | runSpacing: 10, | 77 | runSpacing: 10, |
78 | + crossAxisAlignment: direction == Axis.horizontal | ||
79 | + ? WrapCrossAlignment.center | ||
80 | + : WrapCrossAlignment.start, | ||
85 | children: <Widget>[ | 81 | children: <Widget>[ |
86 | for (final Dataset dataset in datasets) | 82 | for (final Dataset dataset in datasets) |
87 | if (dataset.legend != null) _buildLegend(context, dataset) | 83 | if (dataset.legend != null) _buildLegend(context, dataset) |
@@ -109,7 +109,7 @@ class PieGrid extends ChartGrid { | @@ -109,7 +109,7 @@ class PieGrid extends ChartGrid { | ||
109 | } | 109 | } |
110 | } | 110 | } |
111 | 111 | ||
112 | -enum PieLegendPosition { none, auto, inside } | 112 | +enum PieLegendPosition { none, auto, inside, outside } |
113 | 113 | ||
114 | class PieDataSet extends Dataset { | 114 | class PieDataSet extends Dataset { |
115 | PieDataSet({ | 115 | PieDataSet({ |
@@ -123,16 +123,22 @@ class PieDataSet extends Dataset { | @@ -123,16 +123,22 @@ class PieDataSet extends Dataset { | ||
123 | this.surfaceOpacity = 1, | 123 | this.surfaceOpacity = 1, |
124 | this.offset = 0, | 124 | this.offset = 0, |
125 | this.legendStyle, | 125 | this.legendStyle, |
126 | + this.legendAlign, | ||
126 | this.legendPosition = PieLegendPosition.auto, | 127 | this.legendPosition = PieLegendPosition.auto, |
128 | + this.legendLineWidth = 1.0, | ||
129 | + this.legendLineColor = PdfColors.black, | ||
130 | + Widget? legendWidget, | ||
131 | + this.legendOffset = 20, | ||
127 | }) : drawBorder = drawBorder ?? borderColor != null && color != borderColor, | 132 | }) : drawBorder = drawBorder ?? borderColor != null && color != borderColor, |
128 | assert((drawBorder ?? borderColor != null && color != borderColor) || | 133 | assert((drawBorder ?? borderColor != null && color != borderColor) || |
129 | drawSurface), | 134 | drawSurface), |
135 | + _legendWidget = legendWidget, | ||
130 | super( | 136 | super( |
131 | legend: legend, | 137 | legend: legend, |
132 | color: color, | 138 | color: color, |
133 | ); | 139 | ); |
134 | 140 | ||
135 | - final double value; | 141 | + final num value; |
136 | 142 | ||
137 | late double angleStart; | 143 | late double angleStart; |
138 | 144 | ||
@@ -150,18 +156,124 @@ class PieDataSet extends Dataset { | @@ -150,18 +156,124 @@ class PieDataSet extends Dataset { | ||
150 | 156 | ||
151 | final TextStyle? legendStyle; | 157 | final TextStyle? legendStyle; |
152 | 158 | ||
159 | + final TextAlign? legendAlign; | ||
153 | final PieLegendPosition legendPosition; | 160 | final PieLegendPosition legendPosition; |
154 | 161 | ||
162 | + Widget? _legendWidget; | ||
163 | + | ||
164 | + final double legendOffset; | ||
165 | + | ||
166 | + final double legendLineWidth; | ||
167 | + | ||
168 | + final PdfColor legendLineColor; | ||
169 | + | ||
170 | + PdfPoint? _legendAnchor; | ||
171 | + PdfPoint? _legendPivot; | ||
172 | + PdfPoint? _legendStart; | ||
173 | + | ||
174 | + bool get _isFullCircle => angleEnd - angleStart >= pi * 2; | ||
175 | + | ||
155 | @override | 176 | @override |
156 | void layout(Context context, BoxConstraints constraints, | 177 | void layout(Context context, BoxConstraints constraints, |
157 | {bool parentUsesSize = false}) { | 178 | {bool parentUsesSize = false}) { |
158 | - // final size = constraints.biggest; | 179 | + final _offset = _isFullCircle ? 0 : offset; |
159 | 180 | ||
160 | // ignore: avoid_as | 181 | // ignore: avoid_as |
161 | final grid = Chart.of(context).grid as PieGrid; | 182 | final grid = Chart.of(context).grid as PieGrid; |
162 | - final len = grid.pieSize + offset; | 183 | + final len = grid.pieSize + _offset; |
184 | + var x = -len; | ||
185 | + var y = -len; | ||
186 | + var w = len * 2; | ||
187 | + var h = len * 2; | ||
188 | + | ||
189 | + final lp = legendPosition == PieLegendPosition.auto | ||
190 | + ? (angleEnd - angleStart > pi / 6 | ||
191 | + ? PieLegendPosition.inside | ||
192 | + : PieLegendPosition.outside) | ||
193 | + : legendPosition; | ||
194 | + | ||
195 | + // Find the legend position | ||
196 | + final bisect = _isFullCircle ? 1 / 4 * pi : (angleStart + angleEnd) / 2; | ||
197 | + | ||
198 | + final _legendAlign = legendAlign ?? | ||
199 | + (lp == PieLegendPosition.inside | ||
200 | + ? TextAlign.center | ||
201 | + : (bisect > pi ? TextAlign.right : TextAlign.left)); | ||
202 | + | ||
203 | + _legendWidget ??= legend == null | ||
204 | + ? null | ||
205 | + : Text(legend!, style: legendStyle, textAlign: _legendAlign); | ||
206 | + | ||
207 | + if (_legendWidget != null) { | ||
208 | + _legendWidget!.layout(context, | ||
209 | + BoxConstraints(maxWidth: grid.pieSize, maxHeight: grid.pieSize)); | ||
210 | + assert(_legendWidget!.box != null); | ||
211 | + | ||
212 | + final ls = _legendWidget!.box!.size; | ||
213 | + | ||
214 | + // final style = Theme.of(context).defaultTextStyle.merge(legendStyle); | ||
215 | + | ||
216 | + switch (lp) { | ||
217 | + case PieLegendPosition.outside: | ||
218 | + final o = grid.pieSize + legendOffset; | ||
219 | + final cx = sin(bisect) * (_offset + o); | ||
220 | + final cy = cos(bisect) * (_offset + o); | ||
221 | + | ||
222 | + _legendStart = PdfPoint( | ||
223 | + sin(bisect) * (_offset + grid.pieSize + legendOffset * 0.1), | ||
224 | + cos(bisect) * (_offset + grid.pieSize + legendOffset * 0.1), | ||
225 | + ); | ||
226 | + | ||
227 | + _legendPivot = PdfPoint(cx, cy); | ||
228 | + if (bisect > pi) { | ||
229 | + _legendAnchor = PdfPoint( | ||
230 | + cx - legendOffset / 2 * 0.8, | ||
231 | + cy, | ||
232 | + ); | ||
233 | + _legendWidget!.box = PdfRect.fromPoints( | ||
234 | + PdfPoint( | ||
235 | + cx - legendOffset / 2 - ls.x, | ||
236 | + cy - ls.y / 2, | ||
237 | + ), | ||
238 | + ls); | ||
239 | + w = max(w, (-cx + legendOffset / 2 + ls.x) * 2); | ||
240 | + h = max(h, cy.abs() * 2 + ls.y); | ||
241 | + x = -w / 2; | ||
242 | + y = -h / 2; | ||
243 | + } else { | ||
244 | + _legendAnchor = PdfPoint( | ||
245 | + cx + legendOffset / 2 * 0.8, | ||
246 | + cy, | ||
247 | + ); | ||
248 | + _legendWidget!.box = PdfRect.fromPoints( | ||
249 | + PdfPoint( | ||
250 | + cx + legendOffset / 2, | ||
251 | + cy - ls.y / 2, | ||
252 | + ), | ||
253 | + ls); | ||
254 | + w = max(w, (cx + legendOffset / 2 + ls.x) * 2); | ||
255 | + h = max(h, cy.abs() * 2 + ls.y); | ||
256 | + x = -w / 2; | ||
257 | + y = -h / 2; | ||
258 | + } | ||
259 | + break; | ||
260 | + case PieLegendPosition.inside: | ||
261 | + final o = _isFullCircle ? 0 : grid.pieSize * 2 / 3; | ||
262 | + final cx = sin(bisect) * (_offset + o); | ||
263 | + final cy = cos(bisect) * (_offset + o); | ||
264 | + _legendWidget!.box = PdfRect.fromPoints( | ||
265 | + PdfPoint( | ||
266 | + cx - ls.x / 2, | ||
267 | + cy - ls.y / 2, | ||
268 | + ), | ||
269 | + ls); | ||
270 | + break; | ||
271 | + default: | ||
272 | + break; | ||
273 | + } | ||
274 | + } | ||
163 | 275 | ||
164 | - box = PdfRect(-len, -len, len * 2, len * 2); | 276 | + box = PdfRect(x, y, w, h); |
165 | } | 277 | } |
166 | 278 | ||
167 | void _shape(Context context) { | 279 | void _shape(Context context) { |
@@ -178,12 +290,16 @@ class PieDataSet extends Dataset { | @@ -178,12 +290,16 @@ class PieDataSet extends Dataset { | ||
178 | final ex = cx + sin(angleEnd) * grid.pieSize; | 290 | final ex = cx + sin(angleEnd) * grid.pieSize; |
179 | final ey = cy + cos(angleEnd) * grid.pieSize; | 291 | final ey = cy + cos(angleEnd) * grid.pieSize; |
180 | 292 | ||
293 | + if (_isFullCircle) { | ||
294 | + context.canvas.drawEllipse(0, 0, grid.pieSize, grid.pieSize); | ||
295 | + } else { | ||
181 | context.canvas | 296 | context.canvas |
182 | ..moveTo(cx, cy) | 297 | ..moveTo(cx, cy) |
183 | ..lineTo(sx, sy) | 298 | ..lineTo(sx, sy) |
184 | ..bezierArc(sx, sy, grid.pieSize, grid.pieSize, ex, ey, | 299 | ..bezierArc(sx, sy, grid.pieSize, grid.pieSize, ex, ey, |
185 | large: angleEnd - angleStart > pi); | 300 | large: angleEnd - angleStart > pi); |
186 | } | 301 | } |
302 | + } | ||
187 | 303 | ||
188 | @override | 304 | @override |
189 | void paintBackground(Context context) { | 305 | void paintBackground(Context context) { |
@@ -224,23 +340,49 @@ class PieDataSet extends Dataset { | @@ -224,23 +340,49 @@ class PieDataSet extends Dataset { | ||
224 | } | 340 | } |
225 | 341 | ||
226 | void paintLegend(Context context) { | 342 | void paintLegend(Context context) { |
227 | - if (legendPosition != PieLegendPosition.none && legend != null) { | 343 | + if (legendPosition != PieLegendPosition.none && _legendWidget != null) { |
344 | + if (_legendAnchor != null && | ||
345 | + _legendPivot != null && | ||
346 | + _legendStart != null) { | ||
347 | + context.canvas | ||
348 | + ..saveContext() | ||
349 | + ..moveTo(_legendStart!.x, _legendStart!.y) | ||
350 | + ..lineTo(_legendPivot!.x, _legendPivot!.y) | ||
351 | + ..lineTo(_legendAnchor!.x, _legendAnchor!.y) | ||
352 | + ..setLineWidth(legendLineWidth) | ||
353 | + ..setLineCap(PdfLineCap.round) | ||
354 | + ..setLineJoin(PdfLineJoin.round) | ||
355 | + ..setStrokeColor(legendLineColor) | ||
356 | + ..strokePath() | ||
357 | + ..restoreContext(); | ||
358 | + } | ||
359 | + | ||
360 | + _legendWidget!.paint(context); | ||
361 | + } | ||
362 | + } | ||
363 | + | ||
364 | + @override | ||
365 | + void debugPaint(Context context) { | ||
366 | + super.debugPaint(context); | ||
367 | + | ||
228 | // ignore: avoid_as | 368 | // ignore: avoid_as |
229 | final grid = Chart.of(context).grid as PieGrid; | 369 | final grid = Chart.of(context).grid as PieGrid; |
230 | 370 | ||
231 | final bisect = (angleStart + angleEnd) / 2; | 371 | final bisect = (angleStart + angleEnd) / 2; |
232 | 372 | ||
233 | - final o = grid.pieSize * 2 / 3; | ||
234 | - final cx = sin(bisect) * (offset + o); | ||
235 | - final cy = cos(bisect) * (offset + o); | 373 | + final cx = sin(bisect) * (offset + grid.pieSize + legendOffset); |
374 | + final cy = cos(bisect) * (offset + grid.pieSize + legendOffset); | ||
236 | 375 | ||
237 | - Widget.draw( | ||
238 | - Text(legend!, style: legendStyle, textAlign: TextAlign.center), | ||
239 | - offset: PdfPoint(cx, cy), | ||
240 | - context: context, | ||
241 | - alignment: Alignment.center, | ||
242 | - constraints: const BoxConstraints(maxWidth: 200, maxHeight: 200), | ||
243 | - ); | 376 | + if (_legendWidget != null) { |
377 | + context.canvas | ||
378 | + ..saveContext() | ||
379 | + ..moveTo(0, 0) | ||
380 | + ..lineTo(cx, cy) | ||
381 | + ..setLineWidth(0.5) | ||
382 | + ..setLineDashPattern([3, 1]) | ||
383 | + ..setStrokeColor(PdfColors.blue) | ||
384 | + ..strokePath() | ||
385 | + ..restoreContext(); | ||
244 | } | 386 | } |
245 | } | 387 | } |
246 | } | 388 | } |
-
Please register or login to post a comment