การสร้าง Custom Painter ใน Flutter: ปลดปล่อยพลังแห่งการวาด
ในโลกของการพัฒนาแอปพลิเคชัน Flutter ที่เต็มไปด้วย Widget สำเร็จรูปมากมาย บางครั้งเราก็ต้องการสร้าง UI ที่มีความเฉพาะเจาะจงและซับซ้อนเกินกว่าที่ Widget เหล่านั้นจะทำได้ นี่คือจุดที่ CustomPainter เข้ามามีบทบาทสำคัญ ช่วยให้เราสามารถวาดภาพที่กำหนดเองลงบน Canvas ได้อย่างอิสระ บทความนี้จะพาคุณไปรู้จักกับ Custom Painter และเรียนรู้วิธีการใช้งาน Canvas, Paint และ Path เพื่อสร้างสรรค์ภาพวาดที่น่าทึ่ง
CustomPainter คืออะไร?
CustomPainter คือ Class ใน Flutter ที่ช่วยให้เราสามารถควบคุมการวาดภาพบน Canvas ได้อย่างเต็มที่ Canvas เปรียบเสมือนผืนผ้าใบดิจิทัลที่เราสามารถวาดเส้น รูปร่าง ข้อความ และอื่นๆ ลงไปได้ CustomPainter จะถูกใช้ร่วมกับ Widget CustomPaint เพื่อแสดงผลภาพวาดที่เราสร้างขึ้น
ส่วนประกอบสำคัญ: Canvas, Paint และ Path
ก่อนที่จะเริ่มลงมือเขียนโค้ด เรามาทำความเข้าใจส่วนประกอบหลักที่เกี่ยวข้องกับการสร้าง Custom Painter กันก่อน:
- Canvas: คือพื้นผิวที่เราจะทำการวาดภาพลงไป Canvas มี methods มากมายสำหรับการวาดรูปทรงต่างๆ เช่น เส้น วงกลม สี่เหลี่ยม หรือแม้กระทั่งรูปภาพ
- Paint: คือเครื่องมือที่เราใช้ในการกำหนดลักษณะของภาพวาด เช่น สี ขนาด ความหนาของเส้น รูปแบบการเติมสี (fill) หรือขอบ (stroke)
- Path: คือลำดับของเส้นตรงและเส้นโค้งที่เชื่อมต่อกัน Path ถูกใช้เพื่อสร้างรูปทรงที่ซับซ้อนและไม่สามารถสร้างได้ด้วย methods การวาดรูปทรงพื้นฐานของ Canvas
ขั้นตอนการสร้าง Custom Painter
ต่อไปนี้เป็นขั้นตอนการสร้าง Custom Painter อย่างละเอียด:
- สร้าง Class ที่ extends
CustomPainter: สร้าง Class ใหม่และ extends จากCustomPainter - Override method
paint: Implement methodpaint(Canvas canvas, Size size)ซึ่งเป็น method ที่ใช้ในการวาดภาพลงบน Canvascanvasคือ object ของ Canvas ที่เราจะใช้ในการวาด และsizeคือขนาดของ Widget ที่CustomPainterถูกใช้งาน - Override method
shouldRepaint: Implement methodshouldRepaint(covariant CustomPainter oldDelegate)Method นี้จะถูกเรียกเมื่อ Widget ถูก rebuild และใช้เพื่อตัดสินใจว่าควรจะวาดภาพใหม่หรือไม่ โดยปกติแล้วเราจะเปรียบเทียบ properties ของoldDelegate(CustomPainterก่อนหน้า) กับ properties ของCustomPainterปัจจุบัน หาก properties แตกต่างกัน แสดงว่าเราจำเป็นต้องวาดภาพใหม่
ตัวอย่าง: วาดวงกลม
มาลองสร้าง Custom Painter อย่างง่ายเพื่อวาดวงกลม:
import 'package:flutter/material.dart';
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
..style = PaintingStyle.fill; // หรือ PaintingStyle.stroke
final center = Offset(size.width / 2, size.height / 2);
canvas.drawCircle(center, radius, paint);
}
@override
bool shouldRepaint(covariant CirclePainter oldDelegate) {
return color != oldDelegate.color || radius != oldDelegate.radius;
}
}
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: CirclePainter(color: Colors.blue, radius: 50),
child: Container(), // สามารถใส่ child widget อื่นๆ ได้
);
}
}
คำอธิบายโค้ด:
CirclePainterเป็น Class ที่ extends จากCustomPainter- Constructor รับค่า
colorและradiusเพื่อกำหนดสีและขนาดของวงกลม - ใน method
paintเราสร้างPaintobject และกำหนดสีและ style ให้กับpaint canvas.drawCircleใช้ในการวาดวงกลม โดยรับค่า center, radius และ paintshouldRepaintจะ returntrueหากสีหรือรัศมีของวงกลมมีการเปลี่ยนแปลงMyWidgetใช้CustomPaintwidget และส่งCirclePainterเป็นpainter
ตัวอย่าง: วาดเส้น
มาลองสร้าง Custom Painter เพื่อวาดเส้น:
import 'package:flutter/material.dart';
class LinePainter extends CustomPainter {
final Color color;
final double strokeWidth;
LinePainter({required this.color, required this.strokeWidth});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..strokeWidth = strokeWidth
..style = PaintingStyle.stroke;
final startPoint = Offset(0, size.height / 2);
final endPoint = Offset(size.width, size.height / 2);
canvas.drawLine(startPoint, endPoint, paint);
}
@override
bool shouldRepaint(covariant LinePainter oldDelegate) {
return color != oldDelegate.color || strokeWidth != oldDelegate.strokeWidth;
}
}
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: LinePainter(color: Colors.red, strokeWidth: 5),
child: Container(),
);
}
}
คำอธิบายโค้ด:
LinePainterเป็น Class ที่ extends จากCustomPainter- Constructor รับค่า
colorและstrokeWidthเพื่อกำหนดสีและความหนาของเส้น - ใน method
paintเราสร้างPaintobject และกำหนดสี ความหนา และ style ให้กับpaint canvas.drawLineใช้ในการวาดเส้น โดยรับค่า startPoint, endPoint และ paintshouldRepaintจะ returntrueหากสีหรือความหนาของเส้นมีการเปลี่ยนแปลงMyWidgetใช้CustomPaintwidget และส่งLinePainterเป็นpainter
การใช้งาน Path
Path ช่วยให้เราสามารถสร้างรูปทรงที่ซับซ้อนได้ มาลองสร้าง Custom Painter ที่วาดรูปสามเหลี่ยมโดยใช้ Path:
import 'package:flutter/material.dart';
class TrianglePainter extends CustomPainter {
final Color color;
TrianglePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;
final path = Path();
path.moveTo(size.width / 2, 0); // จุดยอดบน
path.lineTo(0, size.height); // จุดมุมซ้ายล่าง
path.lineTo(size.width, size.height); // จุดมุมขวาล่าง
path.close(); // ปิด Path เพื่อสร้างรูปทรงที่สมบูรณ์
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant TrianglePainter oldDelegate) {
return color != oldDelegate.color;
}
}
class MyWidget extends StatelessWidget {
const MyWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: TrianglePainter(color: Colors.green),
child: Container(),
);
}
}
คำอธิบายโค้ด:
TrianglePainterเป็น Class ที่ extends จากCustomPainter- Constructor รับค่า
colorเพื่อกำหนดสีของสามเหลี่ยม - ใน method
paintเราสร้างPaintobject และกำหนดสีและ style ให้กับpaint - เราสร้าง
Pathobject และใช้ methodmoveToและlineToเพื่อกำหนดจุดต่างๆ ของสามเหลี่ยม path.close()ใช้เพื่อปิด Path และสร้างรูปทรงสามเหลี่ยมที่สมบูรณ์canvas.drawPathใช้ในการวาด Path โดยรับค่า path และ paintshouldRepaintจะ returntrueหากสีของสามเหลี่ยมมีการเปลี่ยนแปลงMyWidgetใช้CustomPaintwidget และส่งTrianglePainterเป็นpainter
กรณีใช้งานจริงและข้อผิดพลาดที่พบบ่อย
- การสร้าง Custom Chart: Custom Painter สามารถใช้ในการสร้าง chart ที่มีความซับซ้อน เช่น Bar Chart, Line Chart หรือ Pie Chart
- การสร้าง Loading Indicator: Custom Painter สามารถใช้ในการสร้าง Loading Indicator ที่มีเอกลักษณ์เฉพาะตัว
- ข้อผิดพลาด: การลืม Override method
shouldRepaintอาจทำให้ Widget ไม่ rebuild เมื่อข้อมูลมีการเปลี่ยนแปลง - ข้อผิดพลาด: การไม่ปิด Path (
path.close()) อาจทำให้รูปทรงที่วาดไม่สมบูรณ์ - ข้อผิดพลาด: การใช้
Paintobject เดียวกันสำหรับหลายๆ การวาด อาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด ควรสร้างPaintobject ใหม่สำหรับแต่ละการวาดหากต้องการลักษณะที่แตกต่างกัน
สรุป
CustomPainter เป็นเครื่องมือที่มีประสิทธิภาพในการสร้าง UI ที่มีความซับซ้อนและเฉพาะเจาะจงใน Flutter ด้วยการใช้งาน Canvas, Paint และ Path อย่างเหมาะสม เราสามารถสร้างสรรค์ภาพวาดที่น่าทึ่งและเพิ่มความน่าสนใจให้กับแอปพลิเคชันของเราได้ หวังว่าบทความนี้จะเป็นประโยชน์และช่วยให้คุณเริ่มต้นใช้งาน Custom Painter ได้อย่างมั่นใจ ในบทความถัดไป เราจะไปเรียนรู้เกี่ยวกับการใช้ Slivers เพื่อสร้าง Scrollable Layouts ที่ซับซ้อนยิ่งขึ้น