การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่
ใช้การทดสอบการชนแบบกำหนดเองและตรรกะ shouldRepaint เพื่อให้การวาดทำงานได้อย่างมีประสิทธิภาพ
การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่ เป็นบทเรียน Flutter Mobile Development ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Flutter Mobile Development และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Flutter Mobile Development มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
Why Hit Testing and Repaint Matter
When you draw with CustomPainter you take full control of the canvas. But two costs are easy to overlook: hit testing (deciding which pixels respond to taps) and repainting (re-running your paint code every frame).
- A naive painter that always returns
truefromshouldRepaintrepaints on every rebuild, burning GPU and battery. - By default a custom-painted region is transparent to pointer events, so taps fall through to widgets behind it.
This lesson shows how to make custom drawings both interactive and efficient.
Anatomy of a CustomPainter
A CustomPainter exposes two key overrides: paint for drawing and shouldRepaint for deciding when to re-run paint. There is also an optional hitTest for pointer logic.
paint(Canvas canvas, Size size)— your drawing commands.shouldRepaint(covariant CustomPainter old)— returntrueonly when visual inputs changed.hitTest(Offset position)— returntrue/false/nullto control pointer routing.
class CirclePainter extends CustomPainter {
final Color color;
final double radius;
CirclePainter({required this.color, required this.radius});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
canvas.drawCircle(size.center(Offset.zero), radius, paint);
}
@override
bool shouldRepaint(covariant CirclePainter old) =>
old.color != color || old.radius != radius;
}shouldRepaint: The Core Optimization
shouldRepaint is called when a new painter instance is supplied to CustomPaint. If it returns false, Flutter skips the paint phase and reuses the cached layer.
- Compare only the fields that affect pixels.
- Returning
trueunconditionally defeats the optimization. - Returning
falsewhen something did change causes stale, frozen visuals.
Compare values, not identity — two painter instances with equal fields should yield false.
@override
bool shouldRepaint(covariant CirclePainter old) {
// Repaint ONLY when a visual input differs.
return old.color != color || old.radius != radius;
}
// Anti-pattern: always repaints, no matter what.
// bool shouldRepaint(old) => true;Repaint Boundaries Isolate Work
Even a correct shouldRepaint can't help if your painter shares a layer with a frequently-rebuilding subtree. CustomPaint automatically wraps itself in a RepaintBoundary when given a child, but for a standalone painter you often add one explicitly.
- A
RepaintBoundarygives the painted region its own compositing layer. - Repaints inside the boundary don't force ancestors or siblings to repaint.
- Use the Flutter DevTools "Highlight Repaints" overlay to see which layers flash.
RepaintBoundary(
child: CustomPaint(
size: const Size(200, 200),
painter: CirclePainter(color: Colors.blue, radius: 60),
),
)Default Hit Testing Behavior
By itself a CustomPaint does not capture pointer events on the painted shapes — its hitTest defaults to returning null, meaning "defer to default box behavior." To make drawings tappable you wrap them in a gesture detector and/or override hitTest.
- Return
true: this position is a hit; consume the event. - Return
false: not a hit; let it pass to widgets behind. - Return
null: use the default (the whole bounding box is a hit if opaque).
Implementing Precise hitTest
Override hitTest to restrict taps to the actual drawn shape rather than the rectangular bounds. For a circle, test the distance from the center against the radius.
This lets the corners of the bounding box pass touches through to whatever is behind, which feels natural for non-rectangular art.
class CirclePainter extends CustomPainter {
final Color color;
final double radius;
final Offset center;
CirclePainter({required this.color, required this.radius, required this.center});
@override
void paint(Canvas canvas, Size size) {
canvas.drawCircle(center, radius, Paint()..color = color);
}
@override
bool hitTest(Offset position) {
// True only inside the circle, not the whole box.
return (position - center).distance <= radius;
}
@override
bool shouldRepaint(covariant CirclePainter old) =>
old.color != color || old.radius != radius || old.center != center;
}Modeling Distance Without Flutter
The math behind circular hit testing is just the Euclidean distance. Here is a pure-Dart model you can run on any judge to verify the logic before wiring it into hitTest.
A point is inside the circle when its distance to the center is less than or equal to the radius.
import 'dart:math';
bool insideCircle(double px, double py, double cx, double cy, double r) {
final dx = px - cx;
final dy = py - cy;
return sqrt(dx * dx + dy * dy) <= r;
}
void main() {
const cx = 100.0, cy = 100.0, r = 60.0;
print(insideCircle(120, 110, cx, cy, r)); // true (inside)
print(insideCircle(10, 10, cx, cy, r)); // false (corner)
print(insideCircle(160, 100, cx, cy, r)); // true (on edge)
}Hit Testing with Path.contains
For arbitrary shapes, build a Path in paint and reuse it in hitTest via Path.contains. Storing the path as a field avoids rebuilding it twice per frame.
path.contains(position)returnstruewhen the point lies inside the filled region.- Cache the path; rebuild it only when geometry inputs change.
class StarPainter extends CustomPainter {
final Path _path;
final Color color;
StarPainter({required this.color, required Size size})
: _path = _buildStar(size);
static Path _buildStar(Size size) {
final p = Path();
p.moveTo(size.width / 2, 0);
p.lineTo(size.width, size.height);
p.lineTo(0, size.height);
p.close();
return p;
}
@override
void paint(Canvas canvas, Size size) {
canvas.drawPath(_path, Paint()..color = color);
}
@override
bool hitTest(Offset position) => _path.contains(position);
@override
bool shouldRepaint(covariant StarPainter old) => old.color != color;
}Repainting Only on Animation Ticks
For animated painters, pass the driving Listenable (such as an AnimationController) to CustomPainter's repaint super-parameter. The painter then repaints when the animation ticks — without rebuilding the whole widget tree.
super(repaint: animation)wires the painter to listen directly.- This skips
setStateand avoids rebuilding siblings every frame.
class PulsePainter extends CustomPainter {
final Animation<double> animation;
PulsePainter(this.animation) : super(repaint: animation);
@override
void paint(Canvas canvas, Size size) {
final r = 20 + animation.value * 40;
canvas.drawCircle(
size.center(Offset.zero), r, Paint()..color = Colors.teal);
}
// No need to compare values: the repaint Listenable drives it.
@override
bool shouldRepaint(covariant PulsePainter old) => false;
}Wiring Gestures to a Painter
Hit testing on the painter decides whether the painted region is opaque to pointers, but you still attach a GestureDetector to react. Combine them: precise hitTest plus a detector gives shape-accurate taps.
- The detector's
onTapDowngives a localOffset. - You can re-run the same geometry check to identify which sub-shape was hit.
GestureDetector(
behavior: HitTestBehavior.deferToChild,
onTapUp: (details) {
final local = details.localPosition;
if ((local - center).distance <= radius) {
debugPrint('Circle tapped at $local');
}
},
child: CustomPaint(
size: const Size(200, 200),
painter: CirclePainter(
color: Colors.blue, radius: 60, center: const Offset(100, 100)),
),
)A Repaint Optimization Checklist
Bring it together with a practical checklist for performant custom painting:
- Compare values in
shouldRepaint— never blanket-returntrue. - Use
repaint:for animations instead ofsetState. - Wrap in
RepaintBoundaryto isolate the painted layer. - Cache
PathandPaintobjects; don't allocate per frame. - Override
hitTestso non-rectangular art passes through unused space. - Profile with DevTools repaint highlighting and the timeline.
Quick Check: shouldRepaint
Consider what your shouldRepaint implementation should return to keep painting performant.
Recap
You learned how to keep custom-painted Flutter UIs both interactive and fast:
- shouldRepaint should compare only visual inputs by value, returning
truejust when they change. - hitTest controls pointer routing: precise shape tests let non-rectangular art pass unused taps through.
- Path.contains and distance math give shape-accurate hit detection; cache the path.
- RepaintBoundary isolates the painted layer so repaints don't cascade.
- repaint: wires animations directly to the painter, avoiding full rebuilds.
Together these techniques eliminate wasted frames and make your canvas drawings feel native and responsive.
คำถามที่พบบ่อย
บทเรียน “การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Flutter Mobile Development ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Flutter Mobile Development มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่”
ใช้การทดสอบการชนแบบกำหนดเองและตรรกะ shouldRepaint เพื่อให้การวาดทำงานได้อย่างมีประสิทธิภาพ คุณปฏิบัติ Flutter Mobile Development ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Flutter Mobile Development หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Flutter Mobile Development บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Flutter Mobile Development นี้ได้ไหม
ได้ บทเรียน Flutter Mobile Development ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- API Canvas: เส้นทาง สี และเชดเดอร์
- การสร้างวิดเจ็ตกราฟแบบกำหนดเองที่โต้ตอบได้
- การตัด การผสม และการประกอบเลเยอร์
- การทดสอบการชนและการปรับประสิทธิภาพการวาดใหม่