Flutter CustomPaint 自定义绘制指南:原理、参数与动画实战
深入了解 Flutter 中的 CustomPaint 组件,掌握 Canvas 绘图机制。本文解析 painter、size 等关键参数,并通过重写 CustomPainter 与 AnimationController 结合,演示如何实现从静态绘制到动态动画的完整流程。
CustomPaint 核心机制与参数
CustomPaint 是 Flutter 中用于执行自定义绘制的组件,它提供了一个 canvas,允许开发者在专门的绘制阶段进行内容渲染。其默认绘制顺序为:先执行 painter,再绘制 child 控件,最后执行 foregroundPainter。因此,background 层(painter)位于子组件下方,而 foregroundPainter 则覆盖在子组件之上。
在使用 CustomPaint 时,必须注意绘制边界。所有绘画指令应在原点开始并包含给定大小的矩形区域内进行;超出该边界的内存可能不足以光栅化命令,导致未定义行为,必要时可配合 ClipRect 使用。
关键构造函数参数解析
CustomPaint 的构造函数包含多个影响性能和布局的参数:
painter: 核心参数,负责背景绘制,需继承自CustomPainter。foregroundPainter: 可选参数,用于在 child 之上进行叠加绘制。size: 指定 canvas 的大小。受Constrains约束,仅在 Loose 模式下有效。若为 Tight 模式或约束不足,size 将取最小值。isComplex: 性能优化标识。设为 false 时由合成器决定缓存;设为 true 时提示系统缓存该层绘画结果,以减少重复绘制开销。willChange: 仅当isComplex为 true 时生效,告知系统下一帧绘画内容可能会发生变化,防止过度缓存导致画面滞后。
实现静态绘制
要实现自定义形状,需创建一个继承自 CustomPainter 的类,并重写两个方法:绘制逻辑所在的 paint 方法和决定是否重绘的 shouldRepaint。
- 定义 Painter 类:初始化 Paint 对象(如颜色、描边宽度),并在 paint 方法中调用 canvas 指令。
- 配置 shouldRepaint:若绘制内容完全不受外界状态影响,直接返回
false即可提升性能。
class MyPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 配置画笔样式
var paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 1.0;
// 绘制一条水平线段
canvas.drawLine(
Offset(0, 10),
Offset(100, 10),
paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}将实例化的 Painter 赋值给 CustomPaint 的 painter 参数即可完成绑定:
CustomPaint(painter: MyPainter());
处理 size 限制问题
如果 CustomPaint 包裹在无约束环境中,其内部获取的 size 可能为 Size.zero。解决此问题有两种常见方式:
1. **外层添加 SizedBox**:强制设置宽高约束。
SizedBox(
height: 20,
width: 20,
child: CustomPaint(painter: MyPainter()),
)2. **直接指定 size 参数**:在 Loose 约束下,指定的 size 可作为建议尺寸传递给 painter(但最终以实际可用空间为准)。
SizedBox(
height: 20,
width: 20,
child: CustomPaint(
size: Size(30, 30),
painter: MyPainter(),
),
)结合动画实现动态效果
CustomPainter 提供了 repaint 参数(类型为 Listenable?),用于触发视图刷新以支持动画。通过与 AnimationController 配合,可以实现流畅的动态绘制。
以下示例展示了一条红色线条随时间逐渐变长的效果:
class MyAnimatedPainter extends CustomPainter {
final Animation _animation;
MyAnimatedPainter(this._animation) : super(repaint: _animation);
@override
void paint(Canvas canvas, Size size) {
var paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 1.0;
// 利用动画值动态改变终点坐标
canvas.drawLine(
Offset(0, 10),
Offset(100 * _animation.value, 10),
paint);
}
@override
bool shouldRepaint(covariant MyAnimatedPainter oldDelegate) {
return false; // 由 repaint 控制自动刷新,无需手动判断
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with SingleTickerProviderStateMixin {
late AnimationController controller;
@override
void initState() {
super.initState();
// 创建持续2秒的循环动画
controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 40,
width: 150,
child: CustomPaint(
// 传入 animation.view 作为 repaint 监听源
painter: MyAnimatedPainter(controller.view),
),
);
}
} 运行上述代码后,屏幕上会出现一条从短到长、不断循环变化的红色直线。这就是通过 CustomPaint 和 AnimationController 结合实现的基础动画案例。
以上就是Flutter CustomPaint绘制widget使用示例的详细内容,更多关于Flutter CustomPaint绘制widget的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









