Skip to content

fix: keep single-slice pie/donut charts visible after the enter animation#98

Open
dmitriy-bty wants to merge 2 commits into
rudi-q:mainfrom
dmitriy-bty:fix/single-slice-pie-vanish-on-animation-complete
Open

fix: keep single-slice pie/donut charts visible after the enter animation#98
dmitriy-bty wants to merge 2 commits into
rudi-q:mainfrom
dmitriy-bty:fix/single-slice-pie-vanish-on-animation-complete

Conversation

@dmitriy-bty

@dmitriy-bty dmitriy-bty commented Jul 5, 2026

Copy link
Copy Markdown

Summary

Fixes #97. A pie or donut chart with a single slice (one category making up
100% of the total) animates in correctly but disappears the moment the enter
animation finishes. It is only visible while the animation is still running.

Root cause

AnimatedChartPainter._drawPieAnimated in
lib/src/widgets/animated_chart_painter.dart builds each slice with
Path.arcTo (outer/inner arcs for a donut at lines ~2238 and ~2249, the pie
wedge at line ~2261). The animated sweep angle is:

final sweepAngle = (value / total) * 2 * math.pi;   // line 2169
final animatedSweepAngle = sweepAngle * sliceProgress; // line 2189

For a lone slice, value / total == 1.0, so sweepAngle == 2*pi. At the final
frame sliceProgress == 1.0, making animatedSweepAngle exactly 2*pi.

Path.arcTo draws nothing for a full 2*pi sweep: its start and stop unit
vectors coincide, so the arc collapses to a zero-length segment and the slice
path ends up empty. This is unlike Canvas.drawArc (used elsewhere in this file
for the radial gauges), which special-cases a full-circle sweep to a complete
oval. Mid-animation the sweep is still below 2*pi, which is exactly why the
slice renders during the animation and only vanishes when it completes. The same
happens for a full pie (innerRadius: 0), not just donuts.

Change

In _drawPieAnimated, when animatedSweepAngle reaches a full circle, build the
shape from ovals instead of arcTo:

  • Full pie: a single filled circle (addOval).
  • Full donut: an even-odd outer/inner oval pair, giving a proper ring.

Partial slices (sweep < 2*pi) keep the existing arcTo path untouched, so
multi-slice pies and donuts render exactly as before. The change is contained to
the slice-path construction; the existing fill, stroke, label, and explode logic
apply to the new path unchanged (fill yields the disc/ring; stroke yields the
circle outline(s), which is the correct outline for a complete pie/ring).

Validation

  • Deterministic root-cause trace against the current source (v1.17.6): the only
    code path that can produce a 2*pi arcTo sweep is a slice whose
    value / total == 1.0 at sliceProgress == 1.0; for every partial slice
    animatedSweepAngle < 2*pi, so the branch and its output are unchanged.
  • Non-regression reasoning: multi-slice pies never reach the new branch because
    each slice's sweep is strictly less than 2*pi.
  • Automated regression test (test/single_slice_pie_test.dart): renders
    AnimatedChartPainter at animationProgress: 1.0 for a one-category (100%)
    dataset and counts the pixels that differ from the background, asserting the
    slice actually fills the disc (pie) and the ring (donut). The failure mode is
    a visually empty fill — the painter still paints, just a zero-area path — so
    the existing findsOneWidget-style tests can't detect it; a pixel check can.
    Confirmed the test fails on the pre-fix arcTo path (~800 non-background
    pixels — the empty full-circle sweep) and passes with this change (~21k–32k
    non-background pixels for the ring / disc), covering the exact regression the
    existing multi-slice pie test misses.
  • Full suite green: flutter test — 299/299 passing, including the two new
    cases; no other test affected.

Reproduction

CristalyseChart()
  .data([{'label': 'a', 'value': 1}])
  .mappingPie(value: 'value', category: 'label')
  .geomPie(innerRadius: 26)
  .build();

The donut renders during the animation, then vanishes when it completes on
main; with this change it stays as a full ring. Setting innerRadius: 0
reproduces the same behavior for a full pie.

 rudi-q#97)

A pie/donut chart with a single slice (one category at 100%) rendered
during the enter animation but vanished the instant it completed.

At full progress the slice's sweep angle is exactly 2*pi. `Path.arcTo`
draws nothing for a full-circle sweep (its start and stop unit vectors
coincide), so the slice path collapses to an empty shape on the final
frame. Mid-animation the sweep is still below 2*pi, which is why the
slice only disappears once the animation finishes.

Build the shape from ovals when the animated sweep reaches a full
circle: a filled circle for a pie, and an even-odd outer/inner oval pair
for a donut ring. Partial slices (sweep < 2*pi) keep the existing arcTo
path, so multi-slice pies are unchanged.
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

@dmitriy-bty is attempting to deploy a commit to the DoublOne Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The pie slice path-generation logic in _drawPieAnimated was updated to correctly render slices when animatedSweepAngle reaches or exceeds 2π. It now constructs oval-based paths (addOval, with evenOdd fill for donut rings) instead of relying on arcTo, which previously produced empty paths for full-circle sweeps.

Changes

Full-Circle Pie Slice Rendering Fix

Layer / File(s) Summary
Full-circle slice path construction
lib/src/widgets/animated_chart_painter.dart
Adds a fullCircle constant and checks whether animatedSweepAngle reaches 2π; when it does, builds the slice using addOval (with evenOdd fill for donut inner/outer ovals) instead of arcTo, falling back to the existing donut-slice logic for non-full sweeps.

Estimated code review effort: 2 (Simple) | ~10 minutes

Related issues: Fixes reported bug where single-slice pie/donut charts disappear once the enter animation completes, since arcTo produces an empty path at a full 2π sweep.

Suggested reviewers: rudi-q

🥧 A single slice, spinning ‘round,

Vanished once the anim wound down.

Now ovals drawn with evenOdd care,

Keep the ring forever there.

No more empty, ghostly pie —

Full circles rendered, reaching sky. 🍩

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The change satisfies #97 by handling 2π full-circle slices for both pies and donuts while leaving partial slices unchanged.
Out of Scope Changes check ✅ Passed The patch stays focused on the requested slice-path fix and introduces no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly states the key fix: keeping single-slice pie/donut charts visible after enter animation.
Description check ✅ Passed The description directly explains the bug, root cause, fix, and validation for the same pie/donut animation issue.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/src/widgets/animated_chart_painter.dart`:
- Around line 2203-2223: The full-circle fix in animated_chart_painter.dart
needs regression coverage for the single-slice case. Add a widget test around
AnimatedCristalyseChartWidget that uses a one-point dataset and exercises both
PieGeometry(innerRadius: 0) and a donut inner radius, then pumpAndSettle and
verify the chart still renders/ remains visible. This should specifically cover
the path that now uses the full-circle oval logic instead of the arc path, so
the existing multi-category pie test is not sufficient.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a08971f-593d-42d5-94d1-7d243c05ad63

📥 Commits

Reviewing files that changed from the base of the PR and between 8114cf3 and 36f3e5b.

📒 Files selected for processing (1)
  • lib/src/widgets/animated_chart_painter.dart

Comment thread lib/src/widgets/animated_chart_painter.dart
…cle fix

Renders AnimatedChartPainter at animationProgress 1.0 for a one-category
(100%) dataset and counts pixels that differ from the background, asserting
the slice actually fills the disc (pie) and ring (donut). Fails against the
pre-fix arcTo path (an empty full-circle sweep) and passes with the oval-based
full-circle branch, covering the exact regression the existing multi-slice pie
test cannot catch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Single-slice pie charts disappear when the animation finishes

1 participant