Skip to content

Commit

Permalink
Draw badges on PieChart using MultiChildRenderObjectWidget in pie_cha…
Browse files Browse the repository at this point in the history
…rt_renderer.dart
  • Loading branch information
imaNNeo committed Mar 17, 2021
1 parent 4d4a286 commit f5a5b17
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 9 deletions.
4 changes: 2 additions & 2 deletions lib/src/chart/pie_chart/pie_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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;

Expand Down
35 changes: 35 additions & 0 deletions lib/src/chart/pie_chart/pie_chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,39 @@ class PieChartPainter extends BaseChartPainter<PieChartData> {
return PieTouchResponse(
foundSectionData, foundSectionDataPosition, touchAngle, touchR, touchInput);
}

/// Exposes offset for laying out the badge widgets upon the chart.
Map<int, Offset> getBadgeOffsets(Size viewSize) {
final center = viewSize.center(Offset.zero);
final badgeWidgetsOffsets = <int, Offset>{};

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;
}
}
49 changes: 42 additions & 7 deletions lib/src/chart/pie_chart/pie_chart_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -36,28 +39,34 @@ class PieChartLeaf extends LeafRenderObjectWidget {
}
}

class RenderPieChart extends RenderBox {
class RenderPieChart extends RenderBox
with
ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
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;
PieChartData _targetData;
set targetData(PieChartData value) {
if (_targetData == value) return;
_targetData = value;
markNeedsPaint();
// We must update layout to draw badges correctly!
markNeedsLayout();
}

double get textScale => _textScale;
Expand All @@ -75,16 +84,41 @@ 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
Size computeDryLayout(BoxConstraints constraints) {
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;
Expand All @@ -95,6 +129,7 @@ class RenderPieChart extends RenderBox {
_painter.paint(canvas, size);

canvas.restore();
defaultPaint(context, offset);
}

@override
Expand Down

0 comments on commit f5a5b17

Please sign in to comment.