CustomPainter — adalah kelas abstrak dari Flutter SDK yang memberikan pengembang kendali penuh atas rendering grafis 2D kustom di Canvas. Pengembang membuat subkelas CustomPainter dan mengimplementasikan dua metode wajib: paint(Canvas, Size) dan shouldRepaint(covariant CustomPainter oldDelegate). Menurut Flutter API Documentation (2026), CustomPainter digunakan bersama dengan widget CustomPaint, yang mendelegasikan rendering ke objek CustomPainter yang diberikan pada setiap frame animasi atau perubahan status.
Poin Utama
CustomPainter — adalah kelas dasar Flutter untuk mengimplementasikan grafis 2D sembarang. Ini menggantikan pendekatan dengan menimpa onDraw di Android, memberikan model rendering yang lebih fleksibel dan deklaratif. Pengembang membuat subkelas CustomPainter, menimpa metode paint, di mana ia menerima Canvas dan Size, dan melakukan semua operasi menggambar di Canvas. Widget CustomPaint mengelola pemanggilan paint dan mengintegrasikan hasilnya ke dalam pohon widget.
Perbedaan utama antara CustomPainter dan menggambar langsung melalui Canvas — pemisahan logika rendering (paint) dan pemicu penggambaran ulang (shouldRepaint). Sistem Flutter memanggil shouldRepaint setiap kali widget induk berubah. Jika shouldRepaint mengembalikan false, paint tidak dipanggil, yang menghemat sumber daya. Mekanisme ini mirip dengan shouldRebuild di RenderObject dan merupakan bagian dari arsitektur Flutter dengan tiga pohonnya (Widget, Element, RenderObject).
CustomPaint — adalah widget yang menerima parameter painter (lapisan latar belakang) dan foregroundPainter (lapisan depan). Kedua parameter menerima objek CustomPainter. Painter latar belakang menggambar sebelum anak CustomPaint, dan foregroundPainter setelahnya. Ini memungkinkan penggunaan CustomPaint sebagai wadah dengan latar belakang dekoratif atau overlay, tanpa kehilangan kemampuan untuk menyematkan widget biasa di dalamnya.
Canvas — adalah objek pusat untuk menggambar di Flutter, yang merangkum permukaan grafis. Ini menyediakan sekitar 40 metode menggambar: drawLine, drawCircle, drawRect, drawPath, drawArc, drawImage, drawPicture, drawPoints dan lainnya. Canvas di Flutter sesuai dengan kelas serupa di Android Canvas, tetapi disesuaikan dengan arsitektur Dart dan Flutter Engine. Canvas bekerja dalam sistem koordinat di mana titik asal (0, 0) adalah sudut kiri atas.
Paint — jangan disamakan dengan metode paint() — adalah kelas yang mendefinisikan gaya menggambar: warna (color), ketebalan garis (strokeWidth), gaya (style: fill atau stroke), transparansi, mode campuran (blendMode) dan masker anti-aliasing. Paint dikonfigurasi sebelum menggambar dan diteruskan ke metode Canvas. Penting: Paint adalah objek yang dapat diubah, tetapi disarankan untuk membuatnya sekali dan menggunakan kembali, hanya mengubah parameter yang diperlukan.
| Metode Canvas | Tujuan | Parameter |
|---|---|---|
| drawCircle | Menggambar lingkaran | Offset center, double radius, Paint paint |
| drawLine | Menggambar garis | Offset p1, Offset p2, Paint paint |
| drawRect | Menggambar persegi panjang | Rect rect, Paint paint |
| drawPath | Menggambar kontur sembarang | Path path, Paint paint |
| drawArc | Menggambar busur atau sektor | Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint |
| drawRRect | Persegi panjang dengan sudut membulat | RRect rrect, Paint paint |
| drawOval | Menggambar oval | Rect rect, Paint paint |
| drawImage | Menggambar gambar dari Image | Image image, Offset p, Paint paint |
Canvas mendukung transformasi sistem koordinat: translate(double dx, double dy) memindahkan titik asal koordinat; rotate(double radians) memutar sistem; scale(double sx, double sy) menskala; skew(double sx, double sy) memiringkan. Sebelum transformasi disarankan untuk menyimpan status Canvas melalui save(), dan setelah selesai mengembalikannya melalui restore(). Ini mencegah akumulasi transformasi pada pemanggilan paint berikutnya.
paint(Canvas canvas, Size size) — metode wajib di mana seluruh logika menggambar diimplementasikan. Parameter canvas — permukaan grafis untuk menggambar. Parameter size — ukuran area yang dialokasikan untuk menggambar (lebar dan tinggi dalam piksel logis). Canvas telah dikonfigurasi sebelumnya sehingga titik asal koordinat (0,0) sesuai dengan sudut kiri atas area gambar. Semua koordinat ditentukan relatif terhadap titik asal ini.
shouldRepaint(covariant CustomPainter oldDelegate) — metode wajib yang mengoptimalkan frekuensi penggambaran ulang. Flutter memanggil shouldRepaint setiap kali pohon widget diperbarui yang mempengaruhi CustomPainter. Jika metode mengembalikan false, gambar yang sudah digambar dianggap valid dan paint tidak dipanggil. Implementasi tipikal: return oldDelegate.someField != someField — perbandingan bidang painter lama dan baru untuk menentukan kebutuhan penggambaran ulang.
Untuk gambar beranimasi atau terus berubah (jam, indikator pemuatan, grafik) shouldRepaint harus mengembalikan true pada setiap perubahan status. Jika objek statis (logo, ikon, latar belakang), shouldRepaint mengembalikan false setelah rendering pertama. Flutter secara otomatis menyimpan cache hasil paint dan menggambar ulang hanya saat ukuran, transparansi berubah, atau shouldRepaint dipanggil.
Mari buat GaugePainter — indikator melingkar kustom dengan isian beranimasi. CustomPainter menerima nilai saat ini (progress dari 0.0 hingga 1.0) dan menggambar busur dengan gradien. Dalam metode shouldRepaint, nilai progress dibandingkan untuk optimasi. CustomPaint ditempatkan di pohon widget dengan parameter size untuk membatasi area gambar.
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;
}
}
Contoh menggambar grafik gelombang menggunakan Path. Path memungkinkan membangun kontur sembarang dari garis, kurva Bezier, dan busur. Setelah dibangun, kontur diisi dengan warna atau digaris dengan garis. Dalam contoh, grafik dibangun berdasarkan array titik yang meniru bentuk gelombang.
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;
}
}
painter (latar belakang) menggambar sebelum anak CustomPaint. Jika CustomPaint memiliki anak (parameter child), painter latar belakang ditampilkan di bawahnya. Ini cocok untuk dekorasi: bilah kemajuan, pola latar belakang, tanda air. foregroundPainter menggambar setelah anak CustomPaint, yaitu di atasnya. Digunakan untuk overlay: sorotan, topeng, penggaris ukur, anotasi.
Perbedaan antara painter dan foregroundPainter sangat penting untuk Z-order. Jika perlu menggambar sesuatu di belakang konten — gunakan painter. Jika di atas konten — foregroundPainter. Dalam beberapa kasus, Anda dapat menggunakan kedua painter secara bersamaan: latar belakang melalui painter, label-overlay melalui foregroundPainter. Ini memungkinkan pemisahan logika lapisan grafis yang berbeda menjadi dua kelas CustomPainter terpisah.
Parameter size dalam metode paint sama untuk kedua painter dan ditentukan oleh ukuran area yang dialokasikan untuk CustomPaint. Ukuran dapat diatur secara eksplisit melalui parameter size di CustomPaint atau secara implisit melalui wadah induk. Jika size sama dengan Size.zero, paint tidak dipanggil — Flutter melewatkan rendering untuk area nol. Perilaku ini harus dipertimbangkan saat elemen muncul secara beranimasi.
Aturan utama kinerja CustomPainter — jangan membuat objek di dalam metode paint. Canvas, Paint, Path, Rect, Offset dan objek gambar lainnya harus dibuat di konstruktor CustomPainter atau di-cache. Membuat objek di dalam paint menyebabkan alokasi memori pada setiap frame, yang pada 60 frame per detik menyebabkan pengumpulan sampah yang sering dan animasi tersendat (jank).
Cache Path secara signifikan mempercepat rendering kontur kompleks yang berulang. Jika kontur tidak berubah antar frame (misalnya, kisi latar belakang), buat Path sekali dan gunakan berulang kali. RepaintBoundary — widget pembungkus yang mencegah penggambaran ulang CustomPainter saat pohon widget di atasnya berubah. Jika CustomPainter statis, bungkus dalam RepaintBoundary untuk isolasi.
Untuk CustomPainter beranimasi dengan penggambaran ulang yang sering, gunakan Ticker atau AnimationController dengan shouldRepaint yang mengembalikan true saat nilai animasi berubah. Optimal untuk membandingkan bidang animasi di shouldRepaint, bukan menggambar ulang pada setiap frame tanpa perlu. Cache Rect untuk gradien kompleks: jika gradien tidak mengubah warna, Anda dapat menyimpan cache Shader melalui Paint.shader.
Pertanyaan yang Sering Diajukan
CustomPainter — adalah kelas kontrak yang menentukan apa dan bagaimana menggambar di Canvas. Canvas — adalah objek gambar itu sendiri yang menyediakan metode (drawCircle, drawLine dll.). CustomPainter menerima Canvas melalui metode paint dan mengelola penggunaannya. Satu Canvas dapat digunakan oleh CustomPainter yang berbeda pada waktu yang berbeda.
Tidak, CustomPainter tidak berguna tanpa CustomPaint. CustomPaint adalah widget yang memanggil metode paint CustomPainter dan menampilkan hasilnya di layar. Tanpa CustomPaint, Canvas tidak akan dibuat dan paint tidak akan dijalankan. Namun, Anda dapat mengimplementasikan RenderObject sendiri dengan Canvas, tetapi ini adalah pendekatan tingkat yang lebih rendah.
Gunakan AnimationController dengan Ticker, teruskan nilai animasi ke CustomPainter melalui parameter konstruktor. Saat nilai berubah, panggil setState() di widget yang berisi CustomPaint, yang memicu shouldRepaint. Jika shouldRepaint mengembalikan true, sistem memanggil paint untuk frame animasi baru.
Penyebabnya hampir selalu shouldRepaint yang mengembalikan false. Periksa apakah bidang oldDelegate dibandingkan dengan benar dengan nilai baru. Jika menggunakan objek yang dapat diubah, shouldRepaint mungkin tidak melihat perubahan karena referensi ke objek tetap sama. Gunakan == atau objek yang tidak dapat diubah untuk perbandingan yang benar.
Ya, CustomPainter digunakan untuk menggambar dekorasi kustom di daftar kompleks. Namun, perlu diingat bahwa saat mengurutkan ulang elemen, RepaintBoundary di dalam ListView menyimpan cache rendering setiap elemen. CustomPainter harus ringan dan tidak mengandung operasi berat di paint agar tidak memperlambat animasi seret.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga