Custom Painter & Canvas
Di Flutter, CustomPainter memberi Anda kebebasan melukis apa saja di kanvas. Cocok untuk grafik dinamis, animasi, atau UI unik.
1. Struktur dasar
import 'package:flutter/material.dart';
class MyCanvas extends StatelessWidget {
const MyCanvas({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
size: const Size(300, 300), // lebar × tinggi
painter: ShapePainter(),
);
}
}
class ShapePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 4
..style = PaintingStyle.stroke;
// gambar lingkaran di tengah
canvas.drawCircle(Offset(size.width/2, size.height/2), 80, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
CustomPaint menempelkan ShapePainter pada kanvas berukuran 300×300. paint‑nya menggambar lingkaran biru.
2. Menggambar bentuk kompleks
Anda dapat menggabungkan path, gradien, dan gambar bitmap.
class ComplexPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final path = Path()
..moveTo(0, size.height)
..quadraticBezierTo(size.width/2, 0, size.width, size.height)
..close();
final gradient = LinearGradient(
colors: [Colors.purple, Colors.orange],
).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
final paint = Paint()
..shader = gradient
..style = PaintingStyle.fill;
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
ComplexPainter membuat kurva Bézier berbentuk gelombang dan mengisi dengan gradien.
3. Responsif dengan MediaQuery
Gunakan size yang diberikan oleh CustomPaint atau MediaQuery.of(context).size untuk menyesuaikan skala pada berbagai layar.
class ResponsivePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final radius = size.shortestSide * 0.3;
final paint = Paint()..color = Colors.green;
canvas.drawCircle(size.center(Offset.zero), radius, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
4. Animasi dengan AnimationController
Jika shouldRepaint mengembalikan true saat nilai animasi berubah, kanvas akan di‑redraw tiap frame.
class AnimatedCircle extends StatefulWidget {
const AnimatedCircle({Key? key}) : super(key: key);
@override
_AnimatedCircleState createState() => _AnimatedCircleState();
}
class _AnimatedCircleState extends State<AnimatedCircle>
with SingleTickerProviderStateMixin {
late final AnimationController _ctrl = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat(reverse: true);
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _ctrl,
builder: (_, __) => CustomPaint(
size: const Size(200, 200),
painter: _CirclePainter(_ctrl.value),
),
);
}
@override
void dispose() {
_ctrl.dispose();
super.dispose();
}
}
class _CirclePainter extends CustomPainter {
final double progress;
_CirclePainter(this.progress);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.red
..style = PaintingStyle.fill;
final radius = 30 + 70 * progress; // 30‑100 piksel
canvas.drawCircle(size.center(Offset.zero), radius, paint);
}
@override
bool shouldRepaint(covariant _CirclePainter old) => old.progress != progress;
}
Animasi mengubah radius lingkaran dari 30 ke 100 piksel secara kontinu.
5. Tips & Trik
- Cache: Gunakan
RepaintBoundarybila gambar statis besar agar tidak di‑repaint setiap frame. - Performance: Hindari operasi berat di dalam
paint; kalkulasi dulu diinitStateataushouldRepaint. - Testing:
CustomPainterdapat diuji denganpainter.testPaint(package:flutter_test).
Penutup
CustomPainter & Canvas membuka kemungkinan tak terbatas untuk UI kreatif. Mulailah dengan contoh di atas, eksplorasi path, gradien, serta animasi.
Coba sendiri! Share ke sosial media dan tag @ahsai001