Perché fl_chart

Visualizzare dati in modo chiaro è spesso il cuore di dashboard, app finanziarie e strumenti di monitoraggio. In Flutter la libreria di riferimento per questo scopo è fl_chart: leggera, altamente personalizzabile e costruita interamente su CustomPaint, il che garantisce animazioni fluide e rendering nativo su tutte le piattaforme.

In questa guida vedremo come creare grafici a linee, a barre e a torta, gestendo interazioni e animazioni.

Installazione

Aggiungi la dipendenza al tuo pubspec.yaml:

dependencies:
  fl_chart: ^0.69.0

Poi esegui flutter pub get. Ricorda di controllare l'ultima versione disponibile su pub.dev, dato che l'API viene aggiornata frequentemente.

Grafico a linee

Il LineChart è ideale per mostrare andamenti nel tempo. La configurazione avviene tramite LineChartData:

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';

class VenditeLineChart extends StatelessWidget {
  const VenditeLineChart({super.key});

  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 1.7,
      child: LineChart(
        LineChartData(
          gridData: const FlGridData(show: true),
          titlesData: const FlTitlesData(
            leftTitles: AxisTitles(
              sideTitles: SideTitles(showTitles: true, reservedSize: 40),
            ),
            rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
            topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
          ),
          borderData: FlBorderData(show: true),
          lineBarsData: [
            LineChartBarData(
              spots: const [
                FlSpot(0, 3),
                FlSpot(1, 1),
                FlSpot(2, 4),
                FlSpot(3, 2),
                FlSpot(4, 5),
              ],
              isCurved: true,
              color: Colors.blue,
              barWidth: 3,
              dotData: const FlDotData(show: true),
              belowBarData: BarAreaData(
                show: true,
                color: Colors.blue.withOpacity(0.2),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Ogni punto è rappresentato da un FlSpot(x, y). Con isCurved: true la linea viene interpolata in modo smussato, mentre belowBarData riempie l'area sottostante con un gradiente o un colore.

Personalizzare le etichette degli assi

Un errore comune è lasciare etichette numeriche poco leggibili. Puoi mappare i valori a testi personalizzati con getTitlesWidget:

bottomTitles: AxisTitles(
  sideTitles: SideTitles(
    showTitles: true,
    getTitlesWidget: (value, meta) {
      const giorni = ['Lun', 'Mar', 'Mer', 'Gio', 'Ven'];
      final index = value.toInt();
      if (index < 0 || index >= giorni.length) {
        return const SizedBox.shrink();
      }
      return Padding(
        padding: const EdgeInsets.only(top: 8),
        child: Text(giorni[index]),
      );
    },
  ),
),

Grafico a barre

Il BarChart usa BarChartGroupData per raggruppare le barre. Ecco un esempio con una barra per gruppo:

BarChart(
  BarChartData(
    alignment: BarChartAlignment.spaceAround,
    maxY: 10,
    barGroups: [
      for (var i = 0; i < 5; i++)
        BarChartGroupData(
          x: i,
          barRods: [
            BarChartRodData(
              toY: (i + 1) * 1.5,
              color: Colors.teal,
              width: 18,
              borderRadius: BorderRadius.circular(4),
            ),
          ],
        ),
    ],
  ),
)

Grafico a torta

Il PieChart è perfetto per mostrare proporzioni:

PieChart(
  PieChartData(
    sectionsSpace: 2,
    centerSpaceRadius: 40,
    sections: [
      PieChartSectionData(
        value: 40,
        color: Colors.blue,
        title: '40%',
        radius: 60,
      ),
      PieChartSectionData(
        value: 30,
        color: Colors.orange,
        title: '30%',
        radius: 60,
      ),
      PieChartSectionData(
        value: 30,
        color: Colors.green,
        title: '30%',
        radius: 60,
      ),
    ],
  ),
)

Con centerSpaceRadius maggiore di zero ottieni un grafico a ciambella (donut chart).

Gestire le interazioni

fl_chart supporta il tocco tramite LineTouchData, BarTouchData e PieTouchData. Ad esempio, per aggiornare lo stato al tap su una fetta:

PieChartData(
  pieTouchData: PieTouchData(
    touchCallback: (event, response) {
      if (response?.touchedSection != null) {
        final index = response!.touchedSection!.touchedSectionIndex;
        // aggiorna lo stato, ad esempio evidenziando la sezione
      }
    },
  ),
  sections: sections,
)

Combinando il callback con un StatefulWidget puoi evidenziare la sezione selezionata aumentando il radius o mostrando un tooltip personalizzato.

Animazioni

Molti grafici fl_chart animano automaticamente quando cambiano i dati. Puoi controllare durata e curva passando i parametri al costruttore:

LineChart(
  data,
  duration: const Duration(milliseconds: 400),
  curve: Curves.easeInOut,
)

Best practice

  • Incapsula la configurazione: crea metodi separati per gridData, titlesData e i dati, evitando widget build troppo lunghi.
  • Usa AspectRatio: i grafici hanno bisogno di vincoli di dimensione precisi; AspectRatio o un SizedBox evitano errori di layout.
  • Separa dati e presentazione: passa i modelli dal tuo state management (Riverpod, Bloc) e trasformali in FlSpot o PieChartSectionData in un layer dedicato.
  • Attenzione all'accessibilità: fl_chart disegna su canvas, quindi valuta di affiancare tabelle testuali o Semantics per gli screen reader.

Conclusione

fl_chart offre un ottimo equilibrio tra semplicità e potenza: partendo dai widget base puoi arrivare a dashboard complesse e interattive. Il segreto è costruire un layer che trasformi i tuoi dati di dominio nei modelli richiesti dalla libreria, mantenendo la UI pulita e testabile.