I Built a Production-Grade 3D Pie Chart Package for Flutter — Here’s Everything I Learned
From CustomPainter to pub.dev: a complete journey of building, designing, and publishing a Flutter package with zero dependencies.
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.
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),
]));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:
- Normalize coordinates
- Check distance from ellipse
- Compute angle
- 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
PieChartSegmentThreeDPieChartStyle
2. Widgets
- State management
- Layout
- Animations
3. Painter
- Pure rendering logic
4. Utils
- Math + helpers
👉 Clean separation = maintainable + testable
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-runRequired 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.0GitHub: 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










