CustomPainter — cos'è, metodi paint e shouldRepaint in Flutter

Autore: IT Sectr Pubblicato: 2026-07-22 Tempo di lettura: 10 min

CustomPainter è una classe astratta dell'SDK Flutter che offre agli sviluppatori il controllo completo sul rendering di grafica 2D personalizzata su Canvas. Lo sviluppatore crea una sottoclasse di CustomPainter e implementa due metodi obbligatori: paint(Canvas, Size) e shouldRepaint(covariant CustomPainter oldDelegate). Secondo la Documentazione API Flutter (2026), CustomPainter viene utilizzato insieme al widget CustomPaint, che delega il rendering all'oggetto CustomPainter fornito ad ogni fotogramma di animazione o cambiamento di stato.

Punti Chiave

  • CustomPainter — classe astratta di Flutter che definisce il rendering personalizzato su Canvas attraverso i metodi paint e shouldRepaint
  • CustomPaint — widget che accetta CustomPainter come parametro painter o foregroundPainter e visualizza il risultato sullo schermo
  • Canvas — oggetto che fornisce metodi di disegno (drawLine, drawCircle, drawPath, drawRect, drawArc e altri)
  • shouldRepaint — metodo di ottimizzazione che determina se l'oggetto deve essere ridisegnato durante l'aggiornamento
  • size — dimensione dell'area di disegno passata a paint(Canvas canvas, Size size) dal widget genitore

Cos'è CustomPainter?

CustomPainter è una classe base di Flutter per implementare grafica 2D personalizzata. Sostituisce l'approccio di sovrascrittura di onDraw in Android, fornendo un modello di rendering più flessibile e dichiarativo. Lo sviluppatore crea una sottoclasse di CustomPainter, sovrascrive il metodo paint dove riceve Canvas e Size, ed esegue tutte le operazioni di disegno su Canvas. Il widget CustomPaint gestisce la chiamata a paint e integra il risultato nell'albero dei widget.

La differenza chiave tra CustomPainter e il disegno diretto tramite Canvas è la separazione della logica di rendering (paint) e del trigger di ridisegno (shouldRepaint). Il sistema Flutter chiama shouldRepaint ogni volta che il widget genitore cambia. Se shouldRepaint restituisce false, paint non viene chiamato, risparmiando risorse. Questo meccanismo è simile a shouldRebuild in RenderObject e fa parte dell'architettura Flutter con i suoi tre alberi (Widget, Element, RenderObject).

CustomPaint è un widget che accetta i parametri painter (livello di sfondo) e foregroundPainter (livello di primo piano). Entrambi i parametri accettano oggetti CustomPainter. Il painter di sfondo viene disegnato prima del widget figlio di CustomPaint, e il foregroundPainter dopo. Questo consente di utilizzare CustomPaint come contenitore con sfondo decorativo o sovrapposizione senza perdere la possibilità di incorporare widget normali al suo interno.

Canvas e Paint in Flutter

Canvas è l'oggetto centrale di disegno in Flutter, che incapsula la superficie grafica. Fornisce circa 40 metodi di disegno: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints e altri. Canvas in Flutter corrisponde a una classe simile in Android Canvas ma è adattato all'architettura Dart e al Flutter Engine. Canvas opera in un sistema di coordinate in cui l'origine (0, 0) è l'angolo superiore sinistro.

Paint — da non confondere con il metodo paint() — è una classe che definisce lo stile di disegno: colore (color), spessore della linea (strokeWidth), stile (style: fill o stroke), opacità, modalità di fusione (blendMode) e maschera di anti-aliasing. Paint viene configurato prima del disegno e passato ai metodi di Canvas. Importante: Paint è un oggetto mutabile, ma si consiglia di crearlo una volta e riutilizzarlo, modificando solo i parametri necessari.

Metodo CanvasScopoParametri
drawCircleDisegna un cerchioOffset center, double radius, Paint paint
drawLineDisegna un segmento di lineaOffset p1, Offset p2, Paint paint
drawRectDisegna un rettangoloRect rect, Paint paint
drawPathDisegna un percorso arbitrarioPath path, Paint paint
drawArcDisegna un arco o settoreRect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint
drawRRectRettangolo con angoli arrotondatiRRect rrect, Paint paint
drawOvalDisegna un ovaleRect rect, Paint paint
drawImageDisegna un'immagine da ImageImage image, Offset p, Paint paint

Trasformazioni di Canvas

Canvas supporta le trasformazioni del sistema di coordinate: translate(double dx, double dy) sposta l'origine; rotate(double radians) ruota il sistema; scale(double sx, double sy) ridimensiona; skew(double sx, double sy) inclina. Prima di una trasformazione, si consiglia di salvare lo stato di Canvas tramite save() e di ripristinarlo dopo il completamento tramite restore(). Questo impedisce l'accumulo di trasformazioni nelle successive chiamate a paint.

I metodi paint e shouldRepaint

paint(Canvas canvas, Size size) è il metodo obbligatorio che implementa tutta la logica di disegno. Il parametro canvas è la superficie grafica per il disegno. Il parametro size è la dimensione dell'area allocata per il disegno (larghezza e altezza in pixel logici). Canvas è preconfigurato in modo che l'origine (0,0) corrisponda all'angolo superiore sinistro dell'area di disegno. Tutte le coordinate sono specificate rispetto a questa origine.

shouldRepaint(covariant CustomPainter oldDelegate) è un metodo obbligatorio che ottimizza la frequenza di ridisegno. Flutter chiama shouldRepaint ad ogni aggiornamento dell'albero dei widget che interessa CustomPainter. Se il metodo restituisce false, l'immagine già renderizzata è considerata valida e paint non viene chiamato. Un'implementazione tipica è: return oldDelegate.someField != someField — confronto dei campi del vecchio e nuovo painter per determinare la necessità di ridisegno.

Per immagini animate o in costante cambiamento (orologi, indicatori di caricamento, grafici) shouldRepaint dovrebbe restituire true ad ogni cambiamento di stato. Se l'oggetto è statico (logo, icona, sfondo), shouldRepaint restituisce false dopo il primo rendering. Flutter memorizza automaticamente nella cache il risultato di paint e ridisegna solo quando cambiano la dimensione, l'opacità o la chiamata shouldRepaint.

Esempio di disegno personalizzato con CustomPainter in Dart

Creiamo GaugePainter — un indicatore circolare personalizzato con riempimento animato. CustomPainter accetta il valore corrente (progresso da 0.0 a 1.0) e disegna un arco con gradiente. Il metodo shouldRepaint confronta il valore del progresso per l'ottimizzazione. CustomPaint viene inserito nell'albero dei widget con il parametro size per limitare l'area di disegno.

dart
class GaugePainter extends CustomPainter {
    final double progress;
    final Color startColor;
    final Color endColor;

    GaugePainter({
        required this.progress,
        this.startColor = Colors.blue,
        this.endColor = Colors.cyan,
    });

    @override
    void paint(Canvas canvas, Size size) {
        final center = Offset(size.width / 2,
            size.height / 2);
        final radius = center.dx - 12;
        final bgPaint = Paint()
            ..color = Colors.grey.withOpacity(0.3)
            ..style = PaintingStyle.stroke
            ..strokeWidth = 16
            ..strokeCap = StrokeCap.round;

        final progressPaint = Paint()
            ..shader = LinearGradient(
                colors: [startColor, endColor],
            ).createShader(
                Rect.fromCircle(
                    center, radius))
            ..style = PaintingStyle.stroke
            ..strokeWidth = 16
            ..strokeCap = StrokeCap.round;

        canvas.drawCircle(center, radius, bgPaint);
        canvas.drawArc(
            Rect.fromCircle(center, radius),
            -math.pi / 2,
            2 * math.pi * progress,
            false, progressPaint);
    }

    @override
    bool shouldRepaint(
        GaugePainter oldDelegate) {
        return oldDelegate.progress != progress;
    }
}

CustomPainter con Path per forme complesse

L'esempio disegna un grafico a onde utilizzando Path. Path consente di costruire contorni arbitrari da linee, curve di Bézier e archi. Dopo aver costruito il contorno, viene riempito con colore o delineato con una linea. Nell'esempio, il grafico è costruito da un array di punti che simulano una forma d'onda.

dart
class WavePainter extends CustomPainter {
    final List<double> values;

    WavePainter({required this.values});

    @override
    void paint(Canvas canvas, Size size) {
        final path = Path();
        final dx = size.width /
            (values.length - 1);

        path.moveTo(0, size.height);
        for (var i = 0; i < values.length; i++) {
            final x = i * dx;
            final y = size.height *
                (1 - values[i]);
            path.lineTo(x, y);
        }
        path.lineTo(size.width, size.height);
        path.close();

        final fillPaint = Paint()
            ..color = Colors.blue.withOpacity(0.3)
            ..style = PaintingStyle.fill;

        canvas.drawPath(path, fillPaint);
    }

    @override
    bool shouldRepaint(
        WavePainter oldDelegate) {
        return oldDelegate.values != values;
    }
}

ForegroundPainter e backgroundPainter: differenza

painter (sfondo) viene disegnato prima del widget figlio di CustomPaint. Se CustomPaint ha un widget figlio (parametro child), il painter di sfondo viene visualizzato sotto di esso. Questo è adatto per decorazioni: barre di progresso, motivi di sfondo, filigrane. foregroundPainter viene disegnato dopo il widget figlio, cioè sopra di esso. Viene utilizzato per sovrapposizioni: riflessi, maschere, righelli di misurazione, annotazioni.

La differenza tra painter e foregroundPainter è critica per l'ordine Z. Se hai bisogno di disegnare qualcosa dietro il contenuto — usa painter. Se sopra il contenuto — foregroundPainter. In alcuni casi, puoi utilizzare entrambi i painter contemporaneamente: sfondo tramite painter, etichetta di sovrapposizione tramite foregroundPainter. Questo consente di separare la logica di diversi livelli grafici in due classi CustomPainter separate.

Il parametro size nel metodo paint è lo stesso per entrambi i painter ed è determinato dalla dimensione dell'area allocata per CustomPaint. La dimensione può essere impostata esplicitamente tramite il parametro size in CustomPaint o implicitamente tramite il contenitore genitore. Se la dimensione è Size.zero, paint non viene chiamato — Flutter salta il rendering per un'area di dimensione zero. Questo comportamento deve essere considerato quando si anima la comparsa di elementi.

Ottimizzazione delle prestazioni di CustomPainter

La regola principale delle prestazioni di CustomPainter è non creare oggetti all'interno del metodo paint. Canvas, Paint, Path, Rect, Offset e altri oggetti di disegno dovrebbero essere creati nel costruttore di CustomPainter o memorizzati nella cache. Creare oggetti all'interno di paint porta ad allocazioni ad ogni fotogramma, che a 60 fotogrammi al secondo causa frequenti garbage collection e scatti nell'animazione (jank).

La memorizzazione nella cache di Path accelera significativamente il rendering di percorsi complessi ripetuti. Se il percorso non cambia tra i fotogrammi (ad esempio, una griglia di sfondo), crea il Path una volta e riutilizzalo più volte. RepaintBoundary è un widget wrapper che impedisce il ridisegno di CustomPainter quando l'albero dei widget superiore cambia. Se CustomPainter è statico, avvolgilo in un RepaintBoundary per isolarlo.

Per CustomPainter animati con frequenti ridisegni, usa Ticker o AnimationController con shouldRepaint che restituisce true ad ogni cambiamento del valore animato. È ottimale confrontare il campo animato in shouldRepaint piuttosto che ridisegnare ad ogni fotogramma inutilmente. Memorizzazione nella cache di Rect per gradienti complessi: se i colori del gradiente non cambiano, puoi memorizzare nella cache lo Shader tramite Paint.shader.

Domande Frequenti

Qual è la differenza tra CustomPainter e Canvas in Flutter?

CustomPainter è una classe contratto che definisce cosa e come disegnare sul Canvas. Canvas è l'oggetto di disegno stesso, che fornisce metodi (drawCircle, drawLine, ecc.). CustomPainter riceve Canvas tramite il metodo paint e controlla il suo utilizzo. Una stessa Canvas può essere utilizzata da diversi CustomPainter in momenti diversi.

Si può usare CustomPainter senza CustomPaint?

No, CustomPainter non ha senso senza CustomPaint. CustomPaint è il widget che chiama il metodo paint di CustomPainter e visualizza il risultato sullo schermo. Senza CustomPaint, Canvas non verrà creato e paint non verrà eseguito. Puoi tuttavia implementare il tuo RenderObject con Canvas, ma questo è un approccio di livello inferiore.

Come animare un CustomPainter?

Usa un AnimationController con un Ticker, passando il valore animato a CustomPainter tramite i parametri del costruttore. Quando il valore cambia, chiama setState() nel widget che contiene CustomPaint, che attiva la chiamata a shouldRepaint. Se shouldRepaint restituisce true, il sistema chiama paint per il nuovo fotogramma di animazione.

Perché CustomPainter non si aggiorna quando i dati cambiano?

La causa è quasi sempre shouldRepaint che restituisce false. Controlla se i campi di oldDelegate vengono confrontati correttamente con i nuovi valori. Se viene utilizzato un oggetto mutabile, shouldRepaint potrebbe non rilevare le modifiche perché il riferimento all'oggetto rimane lo stesso. Usa == o oggetti immutabili per un confronto corretto.

Si può usare CustomPainter per animazioni in ReorderableListView?

, CustomPainter viene utilizzato per disegnare decorazioni personalizzate in liste complesse. Tuttavia, tieni presente che durante il riordinamento degli elementi, RepaintBoundary all'interno di ListView memorizza nella cache il rendering di ciascun elemento. CustomPainter dovrebbe essere leggero e non contenere operazioni pesanti in paint per non rallentare l'animazione di trascinamento.

Riepilogo

  • CustomPainter — classe astratta di Flutter per rendering 2D personalizzato con i metodi paint e shouldRepaint
  • CustomPaint — widget che accetta painter (sfondo) e foregroundPainter (primo piano) per visualizzare grafica
  • Canvas — oggetto con metodi di disegno: drawLine, drawCircle, drawPath, drawRect, drawArc e altri
  • Paint — classe che definisce lo stile di disegno: colore, spessore, stile (fill/stroke), opacità e modalità di fusione
  • shouldRepaint — metodo di ottimizzazione che restituisce true solo quando i campi che influenzano il rendering cambiano
  • Creare oggetti all'interno di paint è vietato — tutti i Paint, Path, Rect devono essere memorizzati nella cache per 60 fps
  • RepaintBoundary — widget per isolare CustomPainter dai ridisegni dell'albero dei widget superiore

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche