Flutter CustomPaint 完全解析
CustomPaint 是 Flutter 中用于自定义绘制的核心组件,它允许你通过代码直接在画布上绘制图形、文字、路径等,实现各种自定义的 UI 效果(比如图表、自定义形状、手绘效果等)。
一、核心概念与基础用法
1. 核心组件
CustomPaint:承载绘制内容的容器CustomPainter:绘制逻辑的核心类(需要继承并实现paint和shouldRepaint方法)Canvas:画布,提供各种绘制方法(画圆、画线、画路径等)Size:绘制区域的尺寸
2. 基础示例(绘制一个带边框的圆形)
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('CustomPaint 示例')),
body: Center(
// 核心容器:CustomPaint
child: CustomPaint(
size: const Size(200, 200), // 绘制区域大小
painter: MyCustomPainter(), // 自定义绘制逻辑
),
),
),
);
}
}
// 自定义绘制类:继承 CustomPainter
class MyCustomPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 1. 创建画笔(设置颜色、宽度、样式等)
final paint = Paint()
..color = Colors.blue // 填充色
..style = PaintingStyle.fill; // 填充模式(stroke 为描边)
// 2. 绘制圆形(圆心在画布中心,半径为画布宽度的一半)
canvas.drawCircle(
Offset(size.width / 2, size.height / 2), // 圆心坐标
size.width / 2, // 半径
paint, // 画笔
);
// 3. 绘制边框(重新设置画笔样式)
final borderPaint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 4; // 描边宽度
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width / 2,
borderPaint,
);
}
// 决定是否需要重绘(true=重绘,false=不重绘)
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
// 简单场景直接返回 false(无状态绘制)
// 有状态时对比新旧数据,数据变化返回 true
return false;
}
}
二、常用绘制方法
Canvas 提供了丰富的绘制 API,以下是最常用的:
表格
| 方法 | 功能 | 示例 |
|---|---|---|
drawCircle |
绘制圆形 | canvas.drawCircle(Offset(100,100), 50, paint) |
drawRect |
绘制矩形 | canvas.drawRect(Rect.fromLTWH(50,50,100,80), paint) |
drawLine |
绘制直线 | canvas.drawLine(Offset(0,0), Offset(200,200), paint) |
drawPath |
绘制自定义路径 | 见下文示例 |
drawText |
绘制文字(需配合 TextPainter) | 见下文示例 |
示例 1:绘制自定义路径(三角形)
dart
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.green
..style = PaintingStyle.fill;
// 创建路径
final path = Path()
..moveTo(size.width / 2, 0) // 起点(顶部)
..lineTo(0, size.height) // 连线到左下角
..lineTo(size.width, size.height) // 连线到右下角
..close(); // 闭合路径(回到起点)
canvas.drawPath(path, paint);
}
示例 2:绘制文字
dart
@override
void paint(Canvas canvas, Size size) {
// 创建 TextPainter
final textPainter = TextPainter(
text: const TextSpan(
text: '自定义文字',
style: TextStyle(color: Colors.black, fontSize: 24),
),
textDirection: TextDirection.ltr,
);
// 布局文字(计算尺寸)
textPainter.layout();
// 绘制文字(居中显示)
textPainter.paint(
canvas,
Offset(
(size.width - textPainter.width) / 2,
(size.height - textPainter.height) / 2,
),
);
}
三、进阶技巧
1. 有状态绘制(动态更新)
如果需要根据数据动态更新绘制内容,需:
- 在
CustomPainter中接收数据参数 shouldRepaint对比新旧数据,变化时返回true
示例:
dart
class DynamicPainter extends CustomPainter {
final double progress; // 动态参数(比如进度值 0-1)
DynamicPainter({required this.progress});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.orange;
// 绘制进度条(宽度随 progress 变化)
canvas.drawRect(
Rect.fromLTWH(0, 0, size.width * progress, size.height),
paint,
);
}
@override
bool shouldRepaint(DynamicPainter oldDelegate) {
// 对比进度值,变化则重绘
return oldDelegate.progress != progress;
}
}
// 使用时通过 setState 更新
// setState(() => _progress += 0.1);
// CustomPaint(painter: DynamicPainter(progress: _progress))
2. 叠加绘制(foregroundPainter)
CustomPaint 提供 foregroundPainter 参数,可在子组件上层绘制:
dart
CustomPaint(
size: const Size(200, 200),
painter: MyBackgroundPainter(), // 底层绘制
foregroundPainter: MyForegroundPainter(), // 上层绘制
child: const Center(child: Text('子组件')), // 中间的子组件
)
3. 性能优化
- 避免在
paint方法中创建对象(比如Paint、Path),建议提前初始化 shouldRepaint尽量精准(避免不必要的重绘)- 复杂绘制可使用
RepaintBoundary隔离重绘区域
四、前置条件
使用 CustomPaint 无需额外依赖,只需导入 Flutter 核心包:
dart
import 'package:flutter/material.dart';
总结
CustomPaint是 Flutter 自定义绘制的核心,通过CustomPainter实现绘制逻辑,Canvas提供具体绘制方法;- 核心步骤:定义
CustomPainter实现paint(绘制逻辑)和shouldRepaint(重绘判断),再通过CustomPaint组件承载; - 进阶场景可通过接收动态参数实现状态更新,或使用
foregroundPainter实现多层绘制,注意性能优化(减少不必要的对象创建和重绘)。