From f5a5b174f48628058fbb850708225534f3f8e39d Mon Sep 17 00:00:00 2001 From: imaNNeoFighT Date: Wed, 17 Mar 2021 22:47:50 +0330 Subject: [PATCH] Draw badges on PieChart using MultiChildRenderObjectWidget in pie_chart_renderer.dart --- lib/src/chart/pie_chart/pie_chart_data.dart | 4 +- .../chart/pie_chart/pie_chart_painter.dart | 35 +++++++++++++ .../chart/pie_chart/pie_chart_renderer.dart | 49 ++++++++++++++++--- 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/lib/src/chart/pie_chart/pie_chart_data.dart b/lib/src/chart/pie_chart/pie_chart_data.dart index 3abab5a68..b48dcd93c 100644 --- a/lib/src/chart/pie_chart/pie_chart_data.dart +++ b/lib/src/chart/pie_chart/pie_chart_data.dart @@ -144,7 +144,7 @@ class PieChartSectionData with EquatableMixin { /// /// This can be anything from a text, an image, an animation, and even a combination of widgets. /// Use AnimatedWidgets to animate this widget. - final Widget? badgeWidget; + final Widget badgeWidget; /// Defines position of showing title in the section. /// @@ -196,7 +196,7 @@ class PieChartSectionData with EquatableMixin { titleStyle = titleStyle ?? const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), title = title ?? value.toString(), - badgeWidget = badgeWidget, + badgeWidget = badgeWidget ?? Container(), titlePositionPercentageOffset = titlePositionPercentageOffset ?? 0.5, badgePositionPercentageOffset = badgePositionPercentageOffset ?? 0.5; diff --git a/lib/src/chart/pie_chart/pie_chart_painter.dart b/lib/src/chart/pie_chart/pie_chart_painter.dart index 26f3bb991..2e3200f7e 100644 --- a/lib/src/chart/pie_chart/pie_chart_painter.dart +++ b/lib/src/chart/pie_chart/pie_chart_painter.dart @@ -286,4 +286,39 @@ class PieChartPainter extends BaseChartPainter { return PieTouchResponse( foundSectionData, foundSectionDataPosition, touchAngle, touchR, touchInput); } + + /// Exposes offset for laying out the badge widgets upon the chart. + Map getBadgeOffsets(Size viewSize) { + final center = viewSize.center(Offset.zero); + final badgeWidgetsOffsets = {}; + + var tempAngle = data.startDegreeOffset; + + final sectionsAngle = _calculateSectionsAngle(data.sections, data.sumValue); + for (var i = 0; i < data.sections.length; i++) { + final section = data.sections[i]; + final startAngle = tempAngle; + final sweepAngle = sectionsAngle[i]; + final sectionCenterAngle = startAngle + (sweepAngle / 2); + + Offset sectionCenter(double percentageOffset) => + center + + Offset( + math.cos(radians(sectionCenterAngle)) * + (_calculateCenterRadius(viewSize, data.centerSpaceRadius) + + (section.radius * percentageOffset)), + math.sin(radians(sectionCenterAngle)) * + (_calculateCenterRadius(viewSize, data.centerSpaceRadius) + + (section.radius * percentageOffset)), + ); + + final sectionCenterOffsetBadgeWidget = sectionCenter(section.badgePositionPercentageOffset); + + badgeWidgetsOffsets[i] = sectionCenterOffsetBadgeWidget; + + tempAngle += sweepAngle; + } + + return badgeWidgetsOffsets; + } } diff --git a/lib/src/chart/pie_chart/pie_chart_renderer.dart b/lib/src/chart/pie_chart/pie_chart_renderer.dart index 4728e8dc2..e05ded728 100644 --- a/lib/src/chart/pie_chart/pie_chart_renderer.dart +++ b/lib/src/chart/pie_chart/pie_chart_renderer.dart @@ -6,13 +6,16 @@ import 'package:flutter/rendering.dart'; import 'pie_chart_painter.dart'; -class PieChartLeaf extends LeafRenderObjectWidget { - const PieChartLeaf({ +class PieChartLeaf extends MultiChildRenderObjectWidget { + PieChartLeaf({ Key? key, required this.data, required this.targetData, this.touchCallback, - }) : super(key: key); + }) : super( + key: key, + children: targetData.sections.map((e) => e.badgeWidget).toList(), + ); final PieChartData data, targetData; @@ -36,20 +39,25 @@ class PieChartLeaf extends LeafRenderObjectWidget { } } -class RenderPieChart extends RenderBox { +class RenderPieChart extends RenderBox + with + ContainerRenderObjectMixin, + RenderBoxContainerDefaultsMixin { RenderPieChart( PieChartData data, PieChartData targetData, double textScale, PieTouchCallback? touchCallback) : _data = data, _targetData = targetData, _textScale = textScale, - _touchCallback = touchCallback; + _touchCallback = touchCallback, + _painter = PieChartPainter(data, targetData, textScale: textScale); PieChartData get data => _data; PieChartData _data; set data(PieChartData value) { if (_data == value) return; _data = value; - markNeedsPaint(); + // We must update layout to draw badges correctly! + markNeedsLayout(); } PieChartData get targetData => _targetData; @@ -57,7 +65,8 @@ class RenderPieChart extends RenderBox { set targetData(PieChartData value) { if (_targetData == value) return; _targetData = value; - markNeedsPaint(); + // We must update layout to draw badges correctly! + markNeedsLayout(); } double get textScale => _textScale; @@ -75,9 +84,29 @@ class RenderPieChart extends RenderBox { late PieChartPainter _painter; + @override + void setupParentData(RenderBox child) { + if (child.parentData is! MultiChildLayoutParentData) { + child.parentData = MultiChildLayoutParentData(); + } + } + @override void performLayout() { + var child = firstChild; size = computeDryLayout(constraints); + + final childConstraints = constraints.loosen(); + + var counter = 0; + var badgeOffsets = _painter.getBadgeOffsets(size); + while (child != null) { + child.layout(childConstraints, parentUsesSize: true); + final childParentData = child.parentData! as MultiChildLayoutParentData; + final sizeOffset = Offset(child.size.width / 2, child.size.height / 2); + childParentData.offset = badgeOffsets[counter++]! - sizeOffset; + child = childParentData.nextSibling; + } } @override @@ -85,6 +114,11 @@ class RenderPieChart extends RenderBox { return Size(constraints.maxWidth, constraints.maxHeight); } + @override + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { + return defaultHitTestChildren(result, position: position); + } + @override void paint(PaintingContext context, Offset offset) { final canvas = context.canvas; @@ -95,6 +129,7 @@ class RenderPieChart extends RenderBox { _painter.paint(canvas, size); canvas.restore(); + defaultPaint(context, offset); } @override