Flutter CustomPaint

Flutter CustomPaint 完全解析

CustomPaint 是 Flutter 中用于自定义绘制的核心组件,它允许你通过代码直接在画布上绘制图形、文字、路径等,实现各种自定义的 UI 效果(比如图表、自定义形状、手绘效果等)。

一、核心概念与基础用法

1. 核心组件
  • CustomPaint:承载绘制内容的容器
  • CustomPainter:绘制逻辑的核心类(需要继承并实现 paintshouldRepaint 方法)
  • 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 方法中创建对象(比如 PaintPath),建议提前初始化
  • shouldRepaint 尽量精准(避免不必要的重绘)
  • 复杂绘制可使用 RepaintBoundary 隔离重绘区域

四、前置条件

使用 CustomPaint 无需额外依赖,只需导入 Flutter 核心包:

dart

复制代码
import 'package:flutter/material.dart';

总结

  1. CustomPaint 是 Flutter 自定义绘制的核心,通过 CustomPainter 实现绘制逻辑,Canvas 提供具体绘制方法;
  2. 核心步骤:定义 CustomPainter 实现 paint(绘制逻辑)和 shouldRepaint(重绘判断),再通过 CustomPaint 组件承载;
  3. 进阶场景可通过接收动态参数实现状态更新,或使用 foregroundPainter 实现多层绘制,注意性能优化(减少不必要的对象创建和重绘)。
相关推荐
重庆兔巴哥2 小时前
Java环境变量配置不成功,会有什么症状?
java·开发语言
大黄说说2 小时前
不可变数据:函数式编程的基石与双刃剑
开发语言
炽烈小老头2 小时前
函数式编程范式(三)
前端·typescript
不只会拍照的程序猿2 小时前
《嵌入式AI筑基笔记02:Python数据类型02,从C的“硬核”到Python的“包容”》
开发语言·笔记·python
无限进步_2 小时前
深入解析list:一个完整的C++双向链表实现
开发语言·c++·git·链表·github·list·visual studio
ruoyusixian2 小时前
chrome二维码识别查插件
前端·chrome
重庆兔巴哥2 小时前
如何安装和配置Java开发环境(JDK)?
java·开发语言
biubiuibiu2 小时前
JavaScript核心概念深度解析:位运算与短路逻辑
开发语言·javascript·ecmascript
2401_849644852 小时前
C++代码重构实战
开发语言·c++·算法