MediumI Built a Production-Grade 3D Pie Chart Package for Flutter — Here’s Everything I Learned | by Akanshu Jamwal | Medium
Sitemap
Akanshu Jamwal

Akanshu Jamwal

Flutter × FinTech Developer | Real-Time Market Data · Order Execution · WebSockets | Helping trading apps scale fast

I Built a Production-Grade 3D Pie Chart Package for Flutter — Here’s Everything I Learned

4 min readMar 22, 2026

--

From CustomPainter to pub.dev: a complete journey of building, designing, and publishing a Flutter package with zero dependencies.

Press enter or click to view image in full size

Most Flutter chart libraries give you flat, 2D pie charts with limited styling options.

I wanted something different — a tilted, 3D elliptical ring chart like the ones you see in premium fintech apps and portfolio dashboards. Something that feels polished, with glowing selections, smooth animations, and real depth.

So I built three_d_pie_chart — a fully customizable, production-grade Flutter package with:

  • 30+ style parameters
  • Zero external dependencies
  • Support for all 6 Flutter platforms

In this article, I’ll walk you through the full journey — from the first Canvas.drawArc() to publishing on pub.dev.

What We’re Building

At its simplest, the package looks like this:

ThreeDPieChart(
segments: [
PieChartSegment(label: 'Large Cap', value: 65),
PieChartSegment(label: 'Mid Cap', value: 25),
PieChartSegment(label: 'Small Cap', value: 10),
],
)

That’s it.

You get:

  • A perspective-tilted ring chart
  • Auto-selected colors
  • Glow effects on selection
  • Smooth animations
  • Interactive legend
  • Dark/light theme support

And everything is customizable:

ThreeDPieChart(
segments: mySegments,
style: ThreeDPieChartStyle(
strokeWidth: 14,
gapDegrees: 8,
tiltFactor: 0.55,
shadowStyle: PieChartShadowStyle(offsetY: 14, blurRadius: 6),
glowStyle: PieChartGlowStyle(outerSpread: 30),
legendPosition: LegendPosition.right,
animationDuration: Duration(milliseconds: 400),
),
onSegmentSelected: (index, segment) {
print('Tapped: ${segment.label}');
},
)

Why CustomPainter?

I had two options:

Option A: Widget Composition

Stack multiple widgets using Container and positioning.
👉 Simple, but not scalable for complex visuals.

Option B: CustomPainter

Draw directly on the canvas.
👉 Pixel-perfect control, better performance, and advanced effects.

I chose CustomPainter.

class ThreeDRingPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// Layer 1: Shadows
// Layer 2: Ring arcs + glow
// Layer 3: Indicator line + label
}
  @override
bool shouldRepaint(covariant ThreeDRingPainter old) {
return old.selectedIndex != selectedIndex ||
old.selectedProgress != selectedProgress;
}
}

👉 shouldRepaint() is critical — it directly impacts performance.

Press enter or click to view image in full size

The 3-Layer Rendering Architecture

Each frame renders in three layers:

1. Shadows (Depth)

A blurred ellipse offset below the chart creates a 3D illusion.

canvas.drawArc(
shadowRect,
startAngle,
sweepAngle,
false,
Paint()
..style = PaintingStyle.stroke
..maskFilter = MaskFilter.blur(BlurStyle.normal, 4.5),
);

2. Ring + Glow

Glow is created using multiple blurred arcs behind the main arc.

Get Akanshu Jamwal’s stories in your inbox

Join Medium for free to get updates from this writer.

👉 Key idea: Glow = blurred copies of the same arc

3. Indicator + Label

A gradient line connects the selected segment to its label.

canvas.drawLine(ringPt, lineTop, Paint()
..shader = Gradient.linear(ringPt, lineTop, [
lineColor,
lineColor.withOpacity(0.2),
]));
Press enter or click to view image in full size

The Math Behind It (Ellipse, Not Circle)

Unlike normal charts, this is an ellipse, not a circle.

Arc Calculation

final usable = 2 * pi - gapRad * segments.length;

Each segment gets proportional sweep based on its value.

Hit Testing

To detect taps:

  1. Normalize coordinates
  2. Check distance from ellipse
  3. Compute angle
  4. Match segment range

Smooth Animations

To avoid long rotations:

double _lerpAngle(double a, double b, double t) {
double diff = (b - a) % (2 * pi);
if (diff > pi) diff -= 2 * pi;
if (diff < -pi) diff += 2 * pi;
return a + diff * t;
}

👉 Always takes the shortest path

Architecture Design

The package has 4 layers:

1. Models

  • PieChartSegment
  • ThreeDPieChartStyle

2. Widgets

  • State management
  • Layout
  • Animations

3. Painter

  • Pure rendering logic

4. Utils

  • Math + helpers

👉 Clean separation = maintainable + testable

Press enter or click to view image in full size

Theme Adaptation

The chart automatically adapts:

final isDarkMode = Theme.of(context).brightness == Brightness.dark;

Publishing to pub.dev

Checklist

dart format .
flutter analyze
flutter test
dart pub publish --dry-run

Required Files

  • pubspec.yaml
  • README.md
  • CHANGELOG.md
  • LICENSE
  • example/

Lessons Learned

1. Const defaults = performance boost

2. shouldRepaint() = performance gate

3. Always test edge cases (like 1 segment)

4. Use absolute URLs in README

5. Zero dependencies = maximum compatibility

The Numbers

  • 30+ style parameters
  • 3 rendering layers
  • 20 colors
  • 5 legend positions
  • 0 dependencies
  • 6 platforms

Try It

dependencies:
three_d_pie_chart: ^1.0.0

GitHub: https://github.com/akanshujamwal/three_d_pie_chart
pub.dev: https://pub.dev/packages/three_d_pie_chart

Support This Project ☕

If this project helped you or saved your time, consider supporting:

Your support helps me build more open-source tools 🚀

Final Thoughts

Building this package taught me more about Flutter’s rendering system and real-world architecture than any tutorial.

If you’re thinking about publishing your own package — do it.

The process itself is the learning.

Tags: Flutter, Dart, CustomPainter, Open Source, UI, Data Visualization

--

--

Written by Akanshu Jamwal

Flutter × FinTech Developer | Real-Time Market Data · Order Execution · WebSockets | Helping trading apps scale fast