Charts and Graphs Implementation in Mobile Apps
Charts in mobile apps are not just visualization. This is touches, zoom, animations on data appearance, correct behavior on screen orientation change and Accessibility support. UITableView doesn't lag because it's optimized over years — a custom LineChart without point caching and clip region lags immediately.
Library Choice: Requirements Not Marketing
The "which library to pick" question solves through specific project requirements:
| Criteria | fl_chart | syncfusion_flutter | MPAndroidChart | DGCharts (iOS) |
|---|---|---|---|---|
| Chart types | Line, Bar, Pie, Scatter, Radar | 30+ types | 8 main | 10+ types |
| Zoom/Pan | No (needs custom) | Yes | Yes | Yes |
| Candlestick | No | Yes | Yes | Yes |
| License | MIT | Commercial | Apache 2.0 | Apache 2.0 |
| Performance at >1000 points | Degrades | Good | Good | Good |
For financial apps with candlestick and zoom — syncfusion or native. For health/fitness with simple line charts — fl_chart sufficient. For real-time high-frequency data updates — WebView + ECharts.
LineChart with Real Data: Hidden Pitfalls
Typical mistake — pass entire 5000-point array to fl_chart. LineChart renders each point as FlSpot, and with touchData enabled — calculates hit-testing for each. On mid-range Android — FPS drops below 20.
Correct: downsampling before rendering. LTTB algorithm (Largest-Triangle-Three-Buckets) preserves visual curve shape while reducing point count:
List<FlSpot> lttbDownsample(List<FlSpot> data, int threshold) {
if (data.length <= threshold) return data;
final sampled = <FlSpot>[data.first];
final bucketSize = (data.length - 2) / (threshold - 2);
var a = 0;
for (var i = 0; i < threshold - 2; i++) {
final rangeStart = (i * bucketSize + 1).floor();
final rangeEnd = ((i + 1) * bucketSize + 1).floor().clamp(0, data.length);
final nextA = ((i + 1) * bucketSize + 1).floor().clamp(0, data.length - 1);
// Find point with maximum triangle area
double maxArea = -1;
int maxAreaPoint = rangeStart;
for (var j = rangeStart; j < rangeEnd; j++) {
final area = (data[a].x * (data[nextA].y - data[j].y) +
data[nextA].x * (data[j].y - data[a].y) +
data[j].x * (data[a].y - data[nextA].y))
.abs() / 2;
if (area > maxArea) {
maxArea = area;
maxAreaPoint = j;
}
}
sampled.add(data[maxAreaPoint]);
a = maxAreaPoint;
}
sampled.add(data.last);
return sampled;
}
500 points instead of 5000 — FPS returns to 60.
Animations on Data Appearance
Animation of line "drawn" left to right — popular UX pattern. On Flutter via AnimationController + TweenAnimationBuilder, limiting displayed points via t:
TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 800),
curve: Curves.easeOut,
builder: (context, value, _) {
final visiblePoints = (allSpots.length * value).round();
return LineChart(
LineChartData(
lineBarsData: [
LineChartBarData(spots: allSpots.take(visiblePoints).toList()),
],
),
);
},
)
For bar chart — growth animation via fromY parameter and AnimatedContainer.
Real-time Updates
Charts with second-by-second updates (metrics, exchange data) need setState only for changed data. Wrong: call setState on entire screen — whole widget tree rebuilds. Correct: ValueNotifier + ValueListenableBuilder around only chart widget:
final chartData = ValueNotifier<List<FlSpot>>([]);
// In widget:
ValueListenableBuilder<List<FlSpot>>(
valueListenable: chartData,
builder: (_, spots, __) => LineChart(LineChartData(
lineBarsData: [LineChartBarData(spots: spots)],
)),
)
// On new point:
void addDataPoint(double x, double y) {
final current = [...chartData.value, FlSpot(x, y)];
if (current.length > maxPoints) current.removeAt(0); // sliding window
chartData.value = current;
}
Accessibility
Charts are inaccessible for VoiceOver / TalkBack without extra work. Minimum: Semantics widget describing what chart shows. For financial or medical apps — table alternative data required, switchable via button.
Scope of Work
- Library choice per project requirements with justification
- Implementation of needed chart types (line, bar, pie, candlestick etc.)
- Performance optimization (downsampling, rendering)
- Data appearance animations
- Interactivity: zoom, pan, tooltip on touch
- Accessibility support
Timeframe
One chart type with basic interactivity: 2–3 days. Set of 3–5 types with animations and real-time updates: 1–2 weeks. Cost calculated individually.







