CustomPainter — τι είναι, οι μέθοδοι paint και shouldRepaint στο Flutter

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-07-22 Χρόνος ανάγνωσης: 10 λεπ

CustomPainter — είναι μια αφηρημένη κλάση από το Flutter SDK που παρέχει στους προγραμματιστές πλήρη έλεγχο στην απόδοση προσαρμοσμένων 2D γραφικών στο Canvas. Ο προγραμματιστής δημιουργεί μια υποκλάση του CustomPainter και υλοποιεί δύο υποχρεωτικές μεθόδους: paint(Canvas, Size) και shouldRepaint(covariant CustomPainter oldDelegate). Σύμφωνα με το Flutter API Documentation (2026), το CustomPainter χρησιμοποιείται μαζί με το widget CustomPaint, το οποίο αναθέτει την απόδοση στο αντικείμενο CustomPainter σε κάθε καρέ κινούμενου σχεδίου ή αλλαγής κατάστασης.

Κύρια σημεία

  • CustomPainter — αφηρημένη κλάση Flutter που ορίζει προσαρμοσμένη απόδοση στο Canvas μέσω των μεθόδων paint και shouldRepaint
  • CustomPaint — widget που δέχεται CustomPainter ως παράμετρο painter ή foregroundPainter και εμφανίζει το αποτέλεσμα στην οθόνη
  • Canvas — αντικείμενο που παρέχει μεθόδους σχεδίασης (drawLine, drawCircle, drawPath, drawRect, drawArc και άλλες)
  • shouldRepaint — μέθοδος βελτιστοποίησης που καθορίζει εάν το αντικείμενο πρέπει να επανασχεδιαστεί κατά την ενημέρωση
  • size — το μέγεθος της περιοχής σχεδίασης, που μεταβιβάζεται στο paint(Canvas canvas, Size size) από το γονικό widget

Τι είναι το CustomPainter;

CustomPainter — είναι η βασική κλάση Flutter για την υλοποίηση αυθαίρετων 2D γραφικών. Αντικαθιστά την προσέγγιση με την παράκαμψη του onDraw στο Android, παρέχοντας ένα πιο ευέλικτο και δηλωτικό μοντέλο απόδοσης. Ο προγραμματιστής δημιουργεί μια υποκλάση του CustomPainter, παρακάμπτει τη μέθοδο paint, όπου λαμβάνει Canvas και Size, και εκτελεί όλες τις λειτουργίες σχεδίασης στο Canvas. Το widget CustomPaint διαχειρίζεται την κλήση του paint και ενσωματώνει το αποτέλεσμα στο δέντρο widget.

Η βασική διαφορά μεταξύ CustomPainter και άμεσης σχεδίασης μέσω Canvas — ο διαχωρισμός της λογικής απόδοσης (paint) και του ενεργοποιητή επανασχεδίασης (shouldRepaint). Το σύστημα Flutter καλεί το shouldRepaint κάθε φορά που το γονικό widget αλλάζει. Εάν το shouldRepaint επιστρέψει false, το paint δεν καλείται, εξοικονομώντας πόρους. Αυτός ο μηχανισμός είναι ανάλογος του shouldRebuild στο RenderObject και αποτελεί μέρος της αρχιτεκτονικής Flutter με τα τρία δέντρα της (Widget, Element, RenderObject).

CustomPaint — είναι ένα widget που δέχεται τις παραμέτρους painter (στρώμα φόντου) και foregroundPainter (στρώμα προσκηνίου). Και οι δύο παράμετροι δέχονται αντικείμενα CustomPainter. Ο painter φόντου σχεδιάζει πριν από το παιδί του CustomPaint, ενώ ο foregroundPainter μετά. Αυτό επιτρέπει τη χρήση του CustomPaint ως δοχείου με διακοσμητικό φόντο ή επικάλυψη, χωρίς να χάνεται η δυνατότητα ενσωμάτωσης συνηθισμένων widget στο εσωτερικό.

Canvas και Paint στο Flutter

Canvas — είναι το κεντρικό αντικείμενο για σχεδίαση στο Flutter, που ενσωματώνει τη γραφική επιφάνεια. Παρέχει περίπου 40 μεθόδους σχεδίασης: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints και άλλες. Το Canvas στο Flutter αντιστοιχεί στην ανάλογη κλάση στο Android Canvas, αλλά είναι προσαρμοσμένο στην αρχιτεκτονική Dart και Flutter Engine. Το Canvas λειτουργεί σε ένα σύστημα συντεταγμένων όπου η αρχή (0, 0) είναι η πάνω αριστερή γωνία.

Paint — μην συγχέεται με τη μέθοδο paint() — είναι μια κλάση που ορίζει το στυλ σχεδίασης: χρώμα (color), πάχος γραμμής (strokeWidth), στυλ (style: fill ή stroke), διαφάνεια, λειτουργία ανάμειξης (blendMode) και μάσκα εξομάλυνσης. Paint διαμορφώνεται πριν από τη σχεδίαση και μεταβιβάζεται στις μεθόδους του Canvas. Σημαντικό: Το Paint είναι ένα μεταβλητό αντικείμενο, αλλά συνιστάται η δημιουργία του μία φορά και η επαναχρησιμοποίηση, αλλάζοντας μόνο τις απαραίτητες παραμέτρους.

Μέθοδος CanvasΣκοπόςΠαράμετροι
drawCircleΣχεδίαση κύκλουOffset center, double radius, Paint paint
drawLineΣχεδίαση γραμμήςOffset p1, Offset p2, Paint paint
drawRectΣχεδίαση ορθογωνίουRect rect, Paint paint
drawPathΣχεδίαση αυθαίρετου περιγράμματοςPath path, Paint paint
drawArcΣχεδίαση τόξου ή τομέαRect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint
drawRRectΟρθογώνιο με στρογγυλεμένες γωνίεςRRect rrect, Paint paint
drawOvalΣχεδίαση οβάλRect rect, Paint paint
drawImageΣχεδίαση εικόνας από ImageImage image, Offset p, Paint paint

Μετασχηματισμοί Canvas

Canvas υποστηρίζει μετασχηματισμούς του συστήματος συντεταγμένων: translate(double dx, double dy) μετακινεί την αρχή συντεταγμένων; rotate(double radians) περιστρέφει το σύστημα; scale(double sx, double sy) κλιμακώνει; skew(double sx, double sy) λοξώνει. Πριν από το μετασχηματισμό συνιστάται η αποθήκευση της κατάστασης του Canvas μέσω save(), και μετά την ολοκλήρωση η επαναφορά μέσω restore(). Αυτό αποτρέπει τη συσσώρευση μετασχηματισμών σε επόμενες κλήσεις του paint.

Μέθοδοι paint και shouldRepaint

paint(Canvas canvas, Size size) — η υποχρεωτική μέθοδος στην οποία υλοποιείται όλη η λογική σχεδίασης. Η παράμετρος canvas — η γραφική επιφάνεια για σχεδίαση. Η παράμετρος size — το μέγεθος της περιοχής που έχει διατεθεί για σχεδίαση (πλάτος και ύψος σε λογικά pixel). Το Canvas είναι προρυθμισμένο έτσι ώστε η αρχή συντεταγμένων (0,0) να αντιστοιχεί στην πάνω αριστερή γωνία της περιοχής σχεδίασης. Όλες οι συντεταγμένες ορίζονται σε σχέση με αυτήν την αρχή.

shouldRepaint(covariant CustomPainter oldDelegate) — η υποχρεωτική μέθοδος που βελτιστοποιεί τη συχνότητα επανασχεδίασης. Το Flutter καλεί το shouldRepaint σε κάθε ενημέρωση του δέντρου widget που επηρεάζει το CustomPainter. Εάν η μέθοδος επιστρέψει false, η ήδη σχεδιασμένη εικόνα θεωρείται έγκυρη και το paint δεν καλείται. Τυπική υλοποίηση: return oldDelegate.someField != someField — σύγκριση πεδίων του παλιού και νέου painter για τον καθορισμό της ανάγκης επανασχεδίασης.

Για κινούμενα ή συνεχώς μεταβαλλόμενα σχέδια (ρολόγια, δείκτες φόρτωσης, γραφήματα) το shouldRepaint πρέπει να επιστρέφει true σε κάθε αλλαγή κατάστασης. Εάν το αντικείμενο είναι στατικό (λογότυπο, εικονίδιο, φόντο), το shouldRepaint επιστρέφει false μετά την πρώτη απόδοση. Το Flutter αποθηκεύει αυτόματα στην κρυφή μνήμη το αποτέλεσμα του paint και επανασχεδιάζει μόνο όταν αλλάξει το μέγεθος, η διαφάνεια ή κληθεί το shouldRepaint.

Παράδειγμα προσαρμοσμένης σχεδίασης με CustomPainter σε Dart

Ας δημιουργήσουμε το GaugePainter — έναν προσαρμοσμένο κυκλικό δείκτη με κινούμενο γέμισμα. Το CustomPainter λαμβάνει την τρέχουσα τιμή (progress από 0.0 έως 1.0) και σχεδιάζει ένα τόξο με κλίση. Στη μέθοδο shouldRepaint συγκρίνεται η τιμή progress για βελτιστοποίηση. Το CustomPaint τοποθετείται στο δέντρο widget με την παράμετρο size για τον περιορισμό της περιοχής σχεδίασης.

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 με Path για σύνθετα σχήματα

Το παράδειγμα σχεδιάζει ένα κυματοειδές γράφημα χρησιμοποιώντας Path. Το Path επιτρέπει την κατασκευή αυθαίρετων περιγραμμάτων από γραμμές, καμπύλες Bezier και τόξα. Μετά την κατασκευή, το περίγραμμα γεμίζεται με χρώμα ή περιγράφεται με γραμμή. Στο παράδειγμα, το γράφημα κατασκευάζεται βάσει μιας συστοιχίας σημείων που μιμούνται ένα κυματοειδές σχήμα.

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 και backgroundPainter: διαφορά

painter (φόντου) σχεδιάζει πριν από το παιδί του CustomPaint. Εάν το CustomPaint έχει παιδί (παράμετρος child), ο painter φόντου εμφανίζεται κάτω από αυτό. Αυτό είναι κατάλληλο για διακόσμηση: γραμμές προόδου, μοτίβα φόντου, υδατογραφήματα. foregroundPainter σχεδιάζει μετά το παιδί του CustomPaint, δηλαδή πάνω από αυτό. Χρησιμοποιείται για επικαλύψεις: ανταύγειες, μάσκες, χάρακες μέτρησης, σχολιασμούς.

Η διαφορά μεταξύ painter και foregroundPainter είναι κρίσιμη για τη Z-order. Εάν πρέπει να σχεδιάσετε κάτι πίσω από το περιεχόμενο — χρησιμοποιήστε painter. Εάν πάνω από το περιεχόμενο — foregroundPainter. Σε ορισμένες περιπτώσεις, μπορείτε να χρησιμοποιήσετε και τους δύο painter ταυτόχρονα: φόντο μέσω painter, ετικέτα-επικάλυψη μέσω foregroundPainter. Αυτό επιτρέπει τον διαχωρισμό της λογικής διαφορετικών γραφικών στρωμάτων σε δύο ξεχωριστές κλάσεις CustomPainter.

Η παράμετρος size στη μέθοδο paint είναι ίδια και για τους δύο painter και καθορίζεται από το μέγεθος της περιοχής που έχει διατεθεί για το CustomPaint. Το μέγεθος μπορεί να οριστεί ρητά μέσω της παραμέτρου size στο CustomPaint ή σιωπηρά μέσω του γονικού δοχείου. Εάν το size είναι ίσο με Size.zero, το paint δεν καλείται — το Flutter παραλείπει την απόδοση για μηδενική περιοχή. Αυτή η συμπεριφορά πρέπει να λαμβάνεται υπόψη κατά την κινούμενη εμφάνιση ενός στοιχείου.

Βελτιστοποίηση απόδοσης CustomPainter

Ο κύριος κανόνας απόδοσης CustomPainter — μην δημιουργείτε αντικείμενα μέσα στη μέθοδο paint. Τα Canvas, Paint, Path, Rect, Offset και άλλα αντικείμενα σχεδίασης πρέπει να δημιουργούνται στον κατασκευαστή του CustomPainter ή να αποθηκεύονται στην κρυφή μνήμη. Η δημιουργία αντικειμένων μέσα στο paint οδηγεί σε δεσμεύσεις μνήμης σε κάθε καρέ, που στα 60 καρέ ανά δευτερόλεπτο προκαλεί συχνές συλλογές σκουπιδιών και τραυλισμό κινούμενων σχεδίων (jank).

Η προσωρινή αποθήκευση Path επιταχύνει σημαντικά την απόδοση επαναλαμβανόμενων σύνθετων περιγραμμάτων. Εάν το περίγραμμα δεν αλλάζει μεταξύ των καρέ (π.χ. πλέγμα φόντου), δημιουργήστε το Path μία φορά και χρησιμοποιήστε το πολλές φορές. RepaintBoundary — ένα widget-περιτύλιγμα που αποτρέπει την επανασχεδίαση του CustomPainter όταν αλλάζει το ανώτερο δέντρο widget. Εάν το CustomPainter είναι στατικό, τυλίξτε το σε RepaintBoundary για απομόνωση.

Για κινούμενα CustomPainter με συχνή επανασχεδίαση, χρησιμοποιήστε Ticker ή AnimationController με shouldRepaint που επιστρέφει true όταν αλλάζει η κινούμενη τιμή. Βέλτιστο είναι να συγκρίνετε το κινούμενο πεδίο στο shouldRepaint, όχι να επανασχεδιάζετε σε κάθε καρέ χωρίς λόγο. Προσωρινή αποθήκευση Rect για σύνθετες κλίσεις: εάν η κλίση δεν αλλάζει χρώματα, μπορείτε να αποθηκεύσετε προσωρινά το Shader μέσω του Paint.shader.

Συχνές Ερωτήσεις

Ποια είναι η διαφορά μεταξύ CustomPainter και Canvas στο Flutter;

CustomPainter — είναι μια κλάση-σύμβαση που καθορίζει τι και πώς να σχεδιαστεί στο Canvas. Canvas — το ίδιο το αντικείμενο σχεδίασης που παρέχει μεθόδους (drawCircle, drawLine κ.λπ.). Το CustomPainter λαμβάνει το Canvas μέσω της μεθόδου paint και διαχειρίζεται τη χρήση του. Ένα Canvas μπορεί να χρησιμοποιηθεί από διαφορετικά CustomPainter σε διαφορετικές χρονικές στιγμές.

Μπορεί να χρησιμοποιηθεί το CustomPainter χωρίς CustomPaint;

Όχι, το CustomPainter δεν έχει νόημα χωρίς CustomPaint. Το CustomPaint είναι το widget που καλεί τη μέθοδο paint του CustomPainter και εμφανίζει το αποτέλεσμα στην οθόνη. Χωρίς CustomPaint, το Canvas δεν θα δημιουργηθεί και το paint δεν θα εκτελεστεί. Μπορείτε, ωστόσο, να υλοποιήσετε το δικό σας RenderObject με Canvas, αλλά αυτή είναι μια προσέγγιση χαμηλότερου επιπέδου.

Πώς να κάνετε κίνηση στο CustomPainter;

Χρησιμοποιήστε AnimationController με Ticker, μεταβιβάζοντας την κινούμενη τιμή στο CustomPainter μέσω των παραμέτρων του κατασκευαστή. Όταν η τιμή αλλάζει, καλέστε το setState() στο widget που περιέχει το CustomPaint, το οποίο ενεργοποιεί το shouldRepaint. Εάν το shouldRepaint επιστρέψει true, το σύστημα καλεί το paint για το νέο καρέ κινούμενου σχεδίου.

Γιατί το CustomPainter δεν ενημερώνεται όταν αλλάζουν τα δεδομένα;

Η αιτία είναι σχεδόν πάντα το shouldRepaint που επιστρέφει false. Ελέγξτε εάν τα πεδία του oldDelegate συγκρίνονται σωστά με τις νέες τιμές. Εάν χρησιμοποιείται μεταβλητό αντικείμενο, το shouldRepaint μπορεί να μην παρατηρήσει τις αλλαγές επειδή η αναφορά στο αντικείμενο παραμένει ίδια. Χρησιμοποιήστε == ή αμετάβλητα αντικείμενα για σωστή σύγκριση.

Μπορεί να χρησιμοποιηθεί το CustomPainter για κίνηση σε ReorderableListView;

Ναι, το CustomPainter χρησιμοποιείται για τη σχεδίαση προσαρμοσμένων διακοσμήσεων σε σύνθετες λίστες. Ωστόσο, λάβετε υπόψη ότι κατά την αναδιάταξη στοιχείων, το RepaintBoundary μέσα στο ListView αποθηκεύει προσωρινά την απόδοση κάθε στοιχείου. Το CustomPainter πρέπει να είναι ελαφρύ και να μην περιέχει βαριές λειτουργίες στο paint για να μην επιβραδύνει την κίνηση μεταφοράς.

Σύνοψη

  • CustomPainter — αφηρημένη κλάση Flutter για προσαρμοσμένη 2D απόδοση με μεθόδους paint και shouldRepaint
  • CustomPaint — widget που δέχεται painter (φόντο) και foregroundPainter (προσκήνιο) για εμφάνιση γραφικών
  • Canvas — αντικείμενο με μεθόδους σχεδίασης: drawLine, drawCircle, drawPath, drawRect, drawArc και άλλες
  • Paint — κλάση που ορίζει το στυλ σχεδίασης: χρώμα, πάχος, στυλ (fill/stroke), διαφάνεια και λειτουργία ανάμειξης
  • shouldRepaint — μέθοδος βελτιστοποίησης που επιστρέφει true μόνο όταν αλλάζουν πεδία που επηρεάζουν την απόδοση
  • Η δημιουργία αντικειμένων μέσα στο paint απαγορεύεται — όλα τα Paint, Path, Rect πρέπει να αποθηκεύονται προσωρινά για 60 fps
  • RepaintBoundary — widget για απομόνωση του CustomPainter από επανασχεδιάσεις του ανώτερου δέντρου widget

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης