From 00e404bb0e3af3045d9a4386d48bc5d67c8221d7 Mon Sep 17 00:00:00 2001 From: MatthiasMRC Date: Wed, 4 Feb 2026 11:02:49 +0100 Subject: [PATCH 1/3] feat(pie_chart): add individual border control for pie chart sections Add PieChartSectionBorder class to allow controlling each border side independently (outer arc, inner arc, left radial, right radial). This solves the issue where adjacent sections have doubled borders when using a single borderSide for all edges. New features: - PieChartSectionBorder class with outer, inner, left, right properties - PieChartSectionBorder.all() - same border on all sides - PieChartSectionBorder.arcsOnly() - borders only on arcs (no radial lines) - PieChartSectionBorder.outerOnly() - border only on outer arc The existing borderSide parameter is deprecated but still supported for backwards compatibility. --- lib/src/chart/pie_chart/pie_chart_data.dart | 121 ++++++++++++++++- .../chart/pie_chart/pie_chart_painter.dart | 128 +++++++++++++++++- 2 files changed, 244 insertions(+), 5 deletions(-) diff --git a/lib/src/chart/pie_chart/pie_chart_data.dart b/lib/src/chart/pie_chart/pie_chart_data.dart index 816e36c08..373901b48 100644 --- a/lib/src/chart/pie_chart/pie_chart_data.dart +++ b/lib/src/chart/pie_chart/pie_chart_data.dart @@ -132,6 +132,99 @@ class PieChartData extends BaseChartData with EquatableMixin { ]; } +/// Defines individual border sides for a [PieChartSectionData]. +/// +/// A pie chart section has 4 sides: +/// - [outer]: the outer arc (furthest from center) +/// - [inner]: the inner arc (closest to center) +/// - [left]: the left radial line (start of the section) +/// - [right]: the right radial line (end of the section) +/// +/// This allows you to control which borders are visible, useful when +/// sections are adjacent and you don't want doubled borders. +class PieChartSectionBorder with EquatableMixin { + /// Creates a border configuration for a pie chart section. + /// + /// By default, all sides use [BorderSide.none]. + const PieChartSectionBorder({ + this.outer = BorderSide.none, + this.inner = BorderSide.none, + this.left = BorderSide.none, + this.right = BorderSide.none, + }); + + /// Creates a border with the same [BorderSide] on all sides. + const PieChartSectionBorder.all(BorderSide side) + : outer = side, + inner = side, + left = side, + right = side; + + /// Creates a border with only the arcs (outer and inner) visible. + /// Useful for adjacent sections where you don't want doubled radial borders. + const PieChartSectionBorder.arcsOnly(BorderSide side) + : outer = side, + inner = side, + left = BorderSide.none, + right = BorderSide.none; + + /// Creates a border with only the outer arc visible. + const PieChartSectionBorder.outerOnly(BorderSide side) + : outer = side, + inner = BorderSide.none, + left = BorderSide.none, + right = BorderSide.none; + + /// The border on the outer arc (furthest from center). + final BorderSide outer; + + /// The border on the inner arc (closest to center). + final BorderSide inner; + + /// The border on the left radial line (start of the section). + final BorderSide left; + + /// The border on the right radial line (end of the section). + final BorderSide right; + + /// Copies this border with the given fields replaced. + PieChartSectionBorder copyWith({ + BorderSide? outer, + BorderSide? inner, + BorderSide? left, + BorderSide? right, + }) => + PieChartSectionBorder( + outer: outer ?? this.outer, + inner: inner ?? this.inner, + left: left ?? this.left, + right: right ?? this.right, + ); + + /// Linearly interpolates between two [PieChartSectionBorder]s. + static PieChartSectionBorder lerp( + PieChartSectionBorder a, + PieChartSectionBorder b, + double t, + ) => + PieChartSectionBorder( + outer: BorderSide.lerp(a.outer, b.outer, t), + inner: BorderSide.lerp(a.inner, b.inner, t), + left: BorderSide.lerp(a.left, b.left, t), + right: BorderSide.lerp(a.right, b.right, t), + ); + + /// Returns true if any border side is visible. + bool get hasVisibleBorder => + (outer.width > 0 && outer.color.a > 0) || + (inner.width > 0 && inner.color.a > 0) || + (left.width > 0 && left.color.a > 0) || + (right.width > 0 && right.color.a > 0); + + @override + List get props => [outer, inner, left, right]; +} + /// Holds data related to drawing each [PieChart] section. class PieChartSectionData with EquatableMixin { /// [PieChart] draws section from right side of the circle (0 degrees), @@ -161,7 +254,9 @@ class PieChartSectionData with EquatableMixin { bool? showTitle, this.titleStyle, String? title, + @Deprecated('Use border instead for individual border control') BorderSide? borderSide, + PieChartSectionBorder? border, this.badgeWidget, double? titlePositionPercentageOffset, double? badgePositionPercentageOffset, @@ -171,6 +266,10 @@ class PieChartSectionData with EquatableMixin { showTitle = showTitle ?? true, title = title ?? (value == null ? '' : value.toString()), borderSide = borderSide ?? const BorderSide(width: 0), + border = border ?? + (borderSide != null + ? PieChartSectionBorder.all(borderSide) + : const PieChartSectionBorder()), titlePositionPercentageOffset = titlePositionPercentageOffset ?? 0.5, badgePositionPercentageOffset = badgePositionPercentageOffset ?? 0.5; @@ -200,9 +299,23 @@ class PieChartSectionData with EquatableMixin { /// Defines text of showing title at the middle of section. final String title; - /// Defines border stroke around the section + /// Defines border stroke around the section. + /// + /// @deprecated Use [border] instead for individual border control. final BorderSide borderSide; + /// Defines individual border sides for the section. + /// + /// This allows you to control which borders are visible on each side: + /// - [PieChartSectionBorder.outer]: the outer arc + /// - [PieChartSectionBorder.inner]: the inner arc + /// - [PieChartSectionBorder.left]: the left radial line + /// - [PieChartSectionBorder.right]: the right radial line + /// + /// Useful when sections are adjacent and you don't want doubled borders. + /// For example, use [PieChartSectionBorder.arcsOnly] to only show arc borders. + final PieChartSectionBorder border; + /// Defines a widget that represents the section. /// /// This can be anything from a text, an image, an animation, and even a combination of widgets. @@ -233,7 +346,8 @@ class PieChartSectionData with EquatableMixin { bool? showTitle, TextStyle? titleStyle, String? title, - BorderSide? borderSide, + @Deprecated('Use border instead') BorderSide? borderSide, + PieChartSectionBorder? border, Widget? badgeWidget, double? titlePositionPercentageOffset, double? badgePositionPercentageOffset, @@ -247,6 +361,7 @@ class PieChartSectionData with EquatableMixin { titleStyle: titleStyle ?? this.titleStyle, title: title ?? this.title, borderSide: borderSide ?? this.borderSide, + border: border ?? this.border, badgeWidget: badgeWidget ?? this.badgeWidget, titlePositionPercentageOffset: titlePositionPercentageOffset ?? this.titlePositionPercentageOffset, @@ -269,6 +384,7 @@ class PieChartSectionData with EquatableMixin { titleStyle: TextStyle.lerp(a.titleStyle, b.titleStyle, t), title: b.title, borderSide: BorderSide.lerp(a.borderSide, b.borderSide, t), + border: PieChartSectionBorder.lerp(a.border, b.border, t), badgeWidget: b.badgeWidget, titlePositionPercentageOffset: lerpDouble( a.titlePositionPercentageOffset, @@ -293,6 +409,7 @@ class PieChartSectionData with EquatableMixin { titleStyle, title, borderSide, + border, badgeWidget, titlePositionPercentageOffset, badgePositionPercentageOffset, diff --git a/lib/src/chart/pie_chart/pie_chart_painter.dart b/lib/src/chart/pie_chart/pie_chart_painter.dart index 86d885764..d8aad571a 100644 --- a/lib/src/chart/pie_chart/pie_chart_painter.dart +++ b/lib/src/chart/pie_chart/pie_chart_painter.dart @@ -142,8 +142,36 @@ class PieChartPainter extends BaseChartPainter { ) ..restore(); _sectionPaint.blendMode = BlendMode.srcOver; - if (section.borderSide.width != 0.0 && + + // Draw borders for full circle section + final border = section.border; + if (border.hasVisibleBorder) { + // Outer circle border + if (border.outer.width > 0 && border.outer.color.a > 0) { + _sectionStrokePaint + ..strokeWidth = border.outer.width + ..color = border.outer.color; + canvasWrapper.drawCircle( + center, + centerRadius + section.radius - (border.outer.width / 2), + _sectionStrokePaint, + ); + } + + // Inner circle border + if (border.inner.width > 0 && border.inner.color.a > 0) { + _sectionStrokePaint + ..strokeWidth = border.inner.width + ..color = border.inner.color; + canvasWrapper.drawCircle( + center, + centerRadius + (border.inner.width / 2), + _sectionStrokePaint, + ); + } + } else if (section.borderSide.width != 0.0 && section.borderSide.color.a != 0.0) { + // Legacy borderSide support _sectionStrokePaint ..strokeWidth = section.borderSide.width ..color = section.borderSide.color; @@ -154,7 +182,6 @@ class PieChartPainter extends BaseChartPainter { centerRadius + section.radius - (section.borderSide.width / 2), _sectionStrokePaint, ) - // Inner ..drawCircle( center, @@ -176,6 +203,15 @@ class PieChartPainter extends BaseChartPainter { drawSection(section, sectionPath, canvasWrapper); drawSectionStroke(section, sectionPath, canvasWrapper, viewSize); + drawSectionIndividualBorders( + section, + data.sectionsSpace, + tempAngle, + sectionDegree, + center, + centerRadius, + canvasWrapper, + ); tempAngle += sectionDegree; } } @@ -329,7 +365,10 @@ class PieChartPainter extends BaseChartPainter { CanvasWrapper canvasWrapper, Size viewSize, ) { - if (section.borderSide.width != 0.0 && section.borderSide.color.a != 0.0) { + // Legacy support: if borderSide is set and border is default, use borderSide + if (section.borderSide.width != 0.0 && + section.borderSide.color.a != 0.0 && + !section.border.hasVisibleBorder) { canvasWrapper ..saveLayer( Rect.fromLTWH(0, 0, viewSize.width, viewSize.height), @@ -349,6 +388,89 @@ class PieChartPainter extends BaseChartPainter { } } + @visibleForTesting + void drawSectionIndividualBorders( + PieChartSectionData section, + double sectionSpace, + double tempAngle, + double sectionDegree, + Offset center, + double centerRadius, + CanvasWrapper canvasWrapper, + ) { + final border = section.border; + if (!border.hasVisibleBorder) return; + + final startRadians = Utils().radians(tempAngle); + final sweepRadians = Utils().radians(sectionDegree); + final endRadians = startRadians + sweepRadians; + + final outerRadius = centerRadius + section.radius; + + // Draw outer arc + if (border.outer.width > 0 && border.outer.color.a > 0) { + _sectionStrokePaint + ..strokeWidth = border.outer.width + ..color = border.outer.color + ..style = PaintingStyle.stroke; + + final outerRect = Rect.fromCircle(center: center, radius: outerRadius); + canvasWrapper.drawArc( + outerRect, + startRadians, + sweepRadians, + false, + _sectionStrokePaint, + ); + } + + // Draw inner arc + if (border.inner.width > 0 && border.inner.color.a > 0) { + _sectionStrokePaint + ..strokeWidth = border.inner.width + ..color = border.inner.color + ..style = PaintingStyle.stroke; + + final innerRect = Rect.fromCircle(center: center, radius: centerRadius); + canvasWrapper.drawArc( + innerRect, + startRadians, + sweepRadians, + false, + _sectionStrokePaint, + ); + } + + // Draw left radial line (start of section) + if (border.left.width > 0 && border.left.color.a > 0) { + _sectionStrokePaint + ..strokeWidth = border.left.width + ..color = border.left.color + ..style = PaintingStyle.stroke; + + final startDirection = + Offset(math.cos(startRadians), math.sin(startRadians)); + final startFrom = center + startDirection * centerRadius; + final startTo = center + startDirection * outerRadius; + + canvasWrapper.drawLine(startFrom, startTo, _sectionStrokePaint); + } + + // Draw right radial line (end of section) + if (border.right.width > 0 && border.right.color.a > 0) { + _sectionStrokePaint + ..strokeWidth = border.right.width + ..color = border.right.color + ..style = PaintingStyle.stroke; + + final endDirection = Offset(math.cos(endRadians), math.sin(endRadians)); + final endFrom = center + endDirection * centerRadius; + final endTo = center + endDirection * outerRadius; + + canvasWrapper.drawLine(endFrom, endTo, _sectionStrokePaint); + } + } + /// Calculates layout of overlaying elements, includes: /// - title text /// - badge widget positions From 165bf89757c922f10e058606f036bec774dd3778 Mon Sep 17 00:00:00 2001 From: MatthiasMRC Date: Wed, 4 Feb 2026 12:01:51 +0100 Subject: [PATCH 2/3] feat(pie_chart): add individual border control for pie chart sections Add PieChartSectionBorder class to allow controlling each border side independently (outer arc, inner arc, left radial, right radial). This solves the issue where adjacent sections have doubled borders when using a single borderSide for all edges. New constructors: - PieChartSectionBorder.all() - same border on all sides - PieChartSectionBorder.arcsOnly() - borders only on arcs - PieChartSectionBorder.outerOnly() - border only on outer arc - PieChartSectionBorder.smart() - outer + inner + right (avoids doubled borders) The existing borderSide parameter is deprecated but still supported for backwards compatibility. --- lib/src/chart/pie_chart/pie_chart_data.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/src/chart/pie_chart/pie_chart_data.dart b/lib/src/chart/pie_chart/pie_chart_data.dart index 373901b48..b713a49a0 100644 --- a/lib/src/chart/pie_chart/pie_chart_data.dart +++ b/lib/src/chart/pie_chart/pie_chart_data.dart @@ -175,6 +175,17 @@ class PieChartSectionBorder with EquatableMixin { left = BorderSide.none, right = BorderSide.none; + /// Creates a "smart" border that avoids doubled borders between adjacent sections. + /// + /// This shows borders on outer arc, inner arc, and right side only. + /// Since each section only draws its right border, adjacent sections + /// won't have overlapping borders (section A's right = section B's left). + const PieChartSectionBorder.smart(BorderSide side) + : outer = side, + inner = side, + left = BorderSide.none, + right = side; + /// The border on the outer arc (furthest from center). final BorderSide outer; From 78cfb9d390048ff9f6c89af43bf12bc752010670 Mon Sep 17 00:00:00 2001 From: MatthiasMRC Date: Wed, 4 Feb 2026 12:11:34 +0100 Subject: [PATCH 3/3] feat(pie_chart): add individual border control for pie chart sections Add PieChartSectionBorder class to allow controlling each border side independently (outer arc, inner arc, left radial, right radial). New constructors: - PieChartSectionBorder.all() - same border on all sides - PieChartSectionBorder.arcsOnly() - borders only on arcs - PieChartSectionBorder.outerOnly() - border only on outer arc The existing borderSide parameter is deprecated but still supported for backwards compatibility. Added unit tests for PieChartSectionBorder and drawSectionIndividualBorders. --- lib/src/chart/pie_chart/pie_chart_data.dart | 16 +- test/chart/pie_chart/pie_chart_data_test.dart | 153 +++++++++++++++ .../pie_chart/pie_chart_painter_test.dart | 185 ++++++++++++++++++ 3 files changed, 339 insertions(+), 15 deletions(-) diff --git a/lib/src/chart/pie_chart/pie_chart_data.dart b/lib/src/chart/pie_chart/pie_chart_data.dart index b713a49a0..02c7d0349 100644 --- a/lib/src/chart/pie_chart/pie_chart_data.dart +++ b/lib/src/chart/pie_chart/pie_chart_data.dart @@ -175,17 +175,6 @@ class PieChartSectionBorder with EquatableMixin { left = BorderSide.none, right = BorderSide.none; - /// Creates a "smart" border that avoids doubled borders between adjacent sections. - /// - /// This shows borders on outer arc, inner arc, and right side only. - /// Since each section only draws its right border, adjacent sections - /// won't have overlapping borders (section A's right = section B's left). - const PieChartSectionBorder.smart(BorderSide side) - : outer = side, - inner = side, - left = BorderSide.none, - right = side; - /// The border on the outer arc (furthest from center). final BorderSide outer; @@ -277,10 +266,7 @@ class PieChartSectionData with EquatableMixin { showTitle = showTitle ?? true, title = title ?? (value == null ? '' : value.toString()), borderSide = borderSide ?? const BorderSide(width: 0), - border = border ?? - (borderSide != null - ? PieChartSectionBorder.all(borderSide) - : const PieChartSectionBorder()), + border = border ?? const PieChartSectionBorder(), titlePositionPercentageOffset = titlePositionPercentageOffset ?? 0.5, badgePositionPercentageOffset = badgePositionPercentageOffset ?? 0.5; diff --git a/test/chart/pie_chart/pie_chart_data_test.dart b/test/chart/pie_chart/pie_chart_data_test.dart index c39349b92..f2977269c 100644 --- a/test/chart/pie_chart/pie_chart_data_test.dart +++ b/test/chart/pie_chart/pie_chart_data_test.dart @@ -165,4 +165,157 @@ void main() { expect(sample1 == zeroLongPressDuration, false); }); }); + + group('PieChartSectionBorder', () { + test('default constructor creates border with all sides none', () { + const border = PieChartSectionBorder(); + expect(border.outer, BorderSide.none); + expect(border.inner, BorderSide.none); + expect(border.left, BorderSide.none); + expect(border.right, BorderSide.none); + expect(border.hasVisibleBorder, false); + }); + + test('.all constructor creates border with same side on all', () { + const side = BorderSide(color: Colors.red, width: 2); + const border = PieChartSectionBorder.all(side); + expect(border.outer, side); + expect(border.inner, side); + expect(border.left, side); + expect(border.right, side); + expect(border.hasVisibleBorder, true); + }); + + test('.arcsOnly constructor creates border with only arcs', () { + const side = BorderSide(color: Colors.blue, width: 3); + const border = PieChartSectionBorder.arcsOnly(side); + expect(border.outer, side); + expect(border.inner, side); + expect(border.left, BorderSide.none); + expect(border.right, BorderSide.none); + expect(border.hasVisibleBorder, true); + }); + + test('.outerOnly constructor creates border with only outer arc', () { + const side = BorderSide(color: Colors.green, width: 1); + const border = PieChartSectionBorder.outerOnly(side); + expect(border.outer, side); + expect(border.inner, BorderSide.none); + expect(border.left, BorderSide.none); + expect(border.right, BorderSide.none); + expect(border.hasVisibleBorder, true); + }); + + test('hasVisibleBorder returns false for zero width', () { + const border = PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 0), + ); + expect(border.hasVisibleBorder, false); + }); + + test('hasVisibleBorder returns false for transparent color', () { + const border = PieChartSectionBorder( + outer: BorderSide(color: Colors.transparent, width: 2), + ); + expect(border.hasVisibleBorder, false); + }); + + test('copyWith creates new instance with replaced values', () { + const original = PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 1), + inner: BorderSide(color: Colors.blue, width: 2), + ); + final copied = original.copyWith( + outer: const BorderSide(color: Colors.green, width: 3), + ); + expect(copied.outer, const BorderSide(color: Colors.green, width: 3)); + expect(copied.inner, const BorderSide(color: Colors.blue, width: 2)); + expect(copied.left, BorderSide.none); + expect(copied.right, BorderSide.none); + }); + + test('lerp interpolates between two borders', () { + const a = PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 0), + ); + const b = PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 10), + ); + final result = PieChartSectionBorder.lerp(a, b, 0.5); + expect(result.outer.width, 5); + }); + + test('equality check works correctly', () { + const border1 = PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 2), + ); + const border2 = PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 2), + ); + const border3 = PieChartSectionBorder( + outer: BorderSide(color: Colors.blue, width: 2), + ); + expect(border1 == border2, true); + expect(border1 == border3, false); + }); + }); + + group('PieChartSectionData with border', () { + test('section with border has correct equality', () { + final section1 = PieChartSectionData( + value: 10, + border: const PieChartSectionBorder.all( + BorderSide(color: Colors.red, width: 2), + ), + ); + final section2 = PieChartSectionData( + value: 10, + border: const PieChartSectionBorder.all( + BorderSide(color: Colors.red, width: 2), + ), + ); + final section3 = PieChartSectionData( + value: 10, + border: const PieChartSectionBorder.all( + BorderSide(color: Colors.blue, width: 2), + ), + ); + expect(section1 == section2, true); + expect(section1 == section3, false); + }); + + test('copyWith border works correctly', () { + final section = PieChartSectionData( + value: 10, + border: const PieChartSectionBorder.all( + BorderSide(color: Colors.red, width: 2), + ), + ); + final copied = section.copyWith( + border: const PieChartSectionBorder.outerOnly( + BorderSide(color: Colors.blue, width: 3), + ), + ); + expect(copied.border.outer.color, Colors.blue); + expect(copied.border.outer.width, 3); + expect(copied.border.inner, BorderSide.none); + }); + + test('lerp interpolates border correctly', () { + final a = PieChartSectionData( + value: 10, + border: const PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 0), + ), + ); + final b = PieChartSectionData( + value: 10, + border: const PieChartSectionBorder( + outer: BorderSide(color: Colors.red, width: 10), + ), + ); + final result = PieChartSectionData.lerp(a, b, 0.5); + expect(result.border.outer.width, 5); + }); + }); } diff --git a/test/chart/pie_chart/pie_chart_painter_test.dart b/test/chart/pie_chart/pie_chart_painter_test.dart index 2bfa5c826..7bb17bdce 100644 --- a/test/chart/pie_chart/pie_chart_painter_test.dart +++ b/test/chart/pie_chart/pie_chart_painter_test.dart @@ -1263,4 +1263,189 @@ void main() { ); }); }); + + group('drawSectionIndividualBorders()', () { + test('does nothing when border has no visible sides', () { + const viewSize = Size(200, 200); + final section = PieChartSectionData( + color: MockData.color1, + value: 1, + radius: 40, + border: const PieChartSectionBorder(), + ); + + final pieChartPainter = PieChartPainter(); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + pieChartPainter.drawSectionIndividualBorders( + section, + 0, + 0, + 90, + const Offset(100, 100), + 30, + mockCanvasWrapper, + ); + + verifyNever(mockCanvasWrapper.drawArc(any, any, any, any, any)); + verifyNever(mockCanvasWrapper.drawLine(any, any, any)); + }); + + test('draws outer arc when outer border is set', () { + const viewSize = Size(200, 200); + final section = PieChartSectionData( + color: MockData.color1, + value: 1, + radius: 40, + border: const PieChartSectionBorder( + outer: BorderSide(color: MockData.color2, width: 2), + ), + ); + + final pieChartPainter = PieChartPainter(); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + pieChartPainter.drawSectionIndividualBorders( + section, + 0, + 0, + 90, + const Offset(100, 100), + 30, + mockCanvasWrapper, + ); + + verify(mockCanvasWrapper.drawArc(any, any, any, false, any)).called(1); + verifyNever(mockCanvasWrapper.drawLine(any, any, any)); + }); + + test('draws inner arc when inner border is set', () { + const viewSize = Size(200, 200); + final section = PieChartSectionData( + color: MockData.color1, + value: 1, + radius: 40, + border: const PieChartSectionBorder( + inner: BorderSide(color: MockData.color2, width: 2), + ), + ); + + final pieChartPainter = PieChartPainter(); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + pieChartPainter.drawSectionIndividualBorders( + section, + 0, + 0, + 90, + const Offset(100, 100), + 30, + mockCanvasWrapper, + ); + + verify(mockCanvasWrapper.drawArc(any, any, any, false, any)).called(1); + verifyNever(mockCanvasWrapper.drawLine(any, any, any)); + }); + + test('draws left and right lines when radial borders are set', () { + const viewSize = Size(200, 200); + final section = PieChartSectionData( + color: MockData.color1, + value: 1, + radius: 40, + border: const PieChartSectionBorder( + left: BorderSide(color: MockData.color3, width: 2), + right: BorderSide(color: MockData.color4, width: 2), + ), + ); + + final pieChartPainter = PieChartPainter(); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + pieChartPainter.drawSectionIndividualBorders( + section, + 0, + 0, + 90, + const Offset(100, 100), + 30, + mockCanvasWrapper, + ); + + verifyNever(mockCanvasWrapper.drawArc(any, any, any, any, any)); + verify(mockCanvasWrapper.drawLine(any, any, any)).called(2); + }); + + test('draws all borders when .all() is used', () { + const viewSize = Size(200, 200); + final section = PieChartSectionData( + color: MockData.color1, + value: 1, + radius: 40, + border: const PieChartSectionBorder.all( + BorderSide(color: MockData.color2, width: 2), + ), + ); + + final pieChartPainter = PieChartPainter(); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + pieChartPainter.drawSectionIndividualBorders( + section, + 0, + 0, + 90, + const Offset(100, 100), + 30, + mockCanvasWrapper, + ); + + // 2 arcs (outer + inner) + verify(mockCanvasWrapper.drawArc(any, any, any, false, any)).called(2); + // 2 lines (left + right) + verify(mockCanvasWrapper.drawLine(any, any, any)).called(2); + }); + + test('draws only arcs when .arcsOnly() is used', () { + const viewSize = Size(200, 200); + final section = PieChartSectionData( + color: MockData.color1, + value: 1, + radius: 40, + border: const PieChartSectionBorder.arcsOnly( + BorderSide(color: MockData.color2, width: 2), + ), + ); + + final pieChartPainter = PieChartPainter(); + final mockCanvasWrapper = MockCanvasWrapper(); + when(mockCanvasWrapper.size).thenAnswer((realInvocation) => viewSize); + when(mockCanvasWrapper.canvas).thenReturn(MockCanvas()); + + pieChartPainter.drawSectionIndividualBorders( + section, + 0, + 0, + 90, + const Offset(100, 100), + 30, + mockCanvasWrapper, + ); + + // 2 arcs (outer + inner) + verify(mockCanvasWrapper.drawArc(any, any, any, false, any)).called(2); + // No lines + verifyNever(mockCanvasWrapper.drawLine(any, any, any)); + }); + }); }