Flutter CustomPainter

Flutter CustomPainter 详解

CustomPainter 是 Flutter 中用于自定义绘制的核心组件,允许你通过底层的绘制 API 实现任意形状、动画和视觉效果,比如自定义图表、特殊按钮、手绘风格的 UI 等。

一、核心概念与基础用法

1. 核心类说明
  • CustomPainter:抽象类,必须实现 paint(Canvas canvas, Size size)shouldRepaint(CustomPainter oldDelegate) 两个方法。
    • paint():绘制逻辑的核心,通过 Canvas 执行绘制操作。
    • shouldRepaint():判断是否需要重绘(性能优化关键),返回 true 表示需要重绘。
  • Canvas:绘制画布,提供绘制点、线、矩形、圆形、路径等方法。
  • Paint:画笔,定义颜色、宽度、样式(填充 / 描边)、抗锯齿等属性。
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('CustomPainter 示例')),
        body: Center(
          // 使用 CustomPaint 包裹自定义绘制逻辑
          child: CustomPaint(
            size: const Size(200, 100), // 绘制区域大小
            painter: MyCustomPainter(), // 自定义的 Painter
          ),
        ),
      ),
    );
  }
}

// 自定义 CustomPainter
class MyCustomPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // 1. 创建画笔
    final Paint paint = Paint()
      ..shader = LinearGradient(
        colors: [Colors.blueAccent, Colors.purpleAccent],
        begin: Alignment.topLeft,
        end: Alignment.bottomRight,
      ).createShader(Rect.fromLTWH(0, 0, size.width, size.height))
      ..style = PaintingStyle.fill // 填充模式(stroke 为描边)
      ..isAntiAlias = true; // 抗锯齿

    // 2. 创建路径(圆角矩形)
    final path = Path()
      ..addRRect(RRect.fromRectAndRadius(
        Rect.fromLTWH(0, 0, size.width, size.height),
        const Radius.circular(20),
      ));

    // 3. 画布绘制路径
    canvas.drawPath(path, paint);

    // 额外:绘制文字
    final textPainter = TextPainter(
      text: const TextSpan(
        text: '自定义绘制',
        style: TextStyle(color: Colors.white, fontSize: 20),
      ),
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    // 文字居中
    textPainter.paint(
      canvas,
      Offset(
        (size.width - textPainter.width) / 2,
        (size.height - textPainter.height) / 2,
      ),
    );
  }

  @override
  bool shouldRepaint(covariant MyCustomPainter oldDelegate) {
    // 无动态数据时返回 false(避免不必要的重绘)
    return false;
  }
}
3. 代码关键解释
  • CustomPaint:承载自定义绘制的容器,size 定义绘制区域的宽高。
  • Paint:配置画笔属性,示例中使用了渐变着色器(shader),也可直接用 color 设置纯色。
  • Path:路径对象,用于定义复杂形状(如圆角矩形、多边形、曲线),Canvas 通过绘制路径实现自定义形状。
  • TextPainter:用于在画布上绘制文字(Canvas 本身无直接绘制文字的方法)。

二、进阶用法:动态绘制与动画

如果需要实现动态效果(比如绘制进度条),可以结合 AnimationChangeNotifier

dart

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

// 带进度的自定义绘制器(支持动画)
class ProgressPainter extends CustomPainter with ChangeNotifier {
  double progress; // 0~1 的进度值

  ProgressPainter({this.progress = 0.0}) : super(repaint: null);

  // 更新进度并通知重绘
  void updateProgress(double value) {
    progress = value.clamp(0.0, 1.0); // 限制在 0~1
    notifyListeners();
  }

  @override
  void paint(Canvas canvas, Size size) {
    // 1. 绘制背景圆
    final bgPaint = Paint()
      ..color = Colors.grey[200]!
      ..style = PaintingStyle.stroke
      ..strokeWidth = 10;
    canvas.drawCircle(
      Offset(size.width / 2, size.height / 2),
      size.width / 2 - 5,
      bgPaint,
    );

    // 2. 绘制进度圆弧
    final progressPaint = Paint()
      ..color = Colors.blueAccent
      ..style = PaintingStyle.stroke
      ..strokeWidth = 10
      ..strokeCap = StrokeCap.round; // 圆角端点
    canvas.drawArc(
      Rect.fromCircle(
        center: Offset(size.width / 2, size.height / 2),
        radius: size.width / 2 - 5,
      ),
      -90 * (3.14159 / 180), // 起始角度(-90度对应12点钟方向)
      360 * progress * (3.14159 / 180), // 扫过的角度(弧度)
      false, // 是否闭合路径
      progressPaint,
    );

    // 3. 绘制进度文字
    final textPainter = TextPainter(
      text: TextSpan(
        text: '${(progress * 100).toInt()}%',
        style: const 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,
      ),
    );
  }

  @override
  bool shouldRepaint(covariant ProgressPainter oldDelegate) {
    // 进度变化时重绘
    return oldDelegate.progress != progress;
  }
}

// 使用示例
class ProgressPage extends StatefulWidget {
  const ProgressPage({super.key});

  @override
  State<ProgressPage> createState() => _ProgressPageState();
}

class _ProgressPageState extends State<ProgressPage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late ProgressPainter _painter;

  @override
  void initState() {
    super.initState();
    _painter = ProgressPainter();
    // 创建动画控制器(3秒从0到1)
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 3),
    )..addListener(() {
        _painter.updateProgress(_controller.value);
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动态绘制进度条')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CustomPaint(
              size: const Size(200, 200),
              painter: _painter,
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                _controller.reset();
                _controller.forward();
              },
              child: const Text('开始动画'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

三、关键优化点

  1. shouldRepaint 优化 :仅在绘制数据变化时返回 true,避免无意义的重绘。
  2. 抗锯齿 :设置 Paint.isAntiAlias = true,避免绘制边缘出现锯齿。
  3. 复用对象 :避免在 paint() 方法内重复创建 PaintPath 等对象(可提为成员变量),减少内存开销。
  4. 裁剪区域 :使用 canvas.clipRect() 限制绘制区域,避免绘制超出可视范围的内容。

总结

  1. CustomPainter 是 Flutter 自定义绘制的核心,通过 Canvas(画布)+ Paint(画笔)实现任意视觉效果,需实现 paint()shouldRepaint() 方法。
  2. 基础用法可绘制静态形状(如矩形、圆形、路径),结合动画 / 状态管理可实现动态效果(如进度条、动效图标)。
  3. 性能优化关键:合理实现 shouldRepaint()、复用绘制对象、开启抗锯齿,避免不必要的重绘。
相关推荐
蜡台2 小时前
Flutter 安装配置
android·java·flutter·环境变量
加农炮手Jinx2 小时前
Flutter 组件 ubuntu_service 适配鸿蒙 HarmonyOS 实战:底层系统服务治理,构建鸿蒙 Linux 子系统与守护进程交互架构
flutter·harmonyos·鸿蒙·openharmony·ubuntu_service
里欧跑得慢2 小时前
Flutter 三方库 mobx_codegen — 自动化驱动的高性能响应式状态管理(适配鸿蒙 HarmonyOS Next ohos)
flutter·自动化·harmonyos
王码码20352 小时前
Flutter 三方库 login_client 的鸿蒙化适配指南 - 打造工业级安全登录、OAuth2 自动化鉴权、鸿蒙级身份守门员
flutter·harmonyos·鸿蒙·openharmony·login_client
加农炮手Jinx2 小时前
Flutter 三方库 cloudflare 鸿蒙云边协同分发流适配精讲:直连全球高速存储网关阵列无缝吞吐海量动静态画像资源,构筑大吞吐业务级网络负载安全分流-适配鸿蒙 HarmonyOS ohos
网络·flutter·harmonyos
木子雨廷7 小时前
Flutter InkWell与GestureDetector
flutter
不爱吃糖的程序媛8 小时前
Flutter 3.32.4-ohos-0.0.2 版本发布
flutter
追梦的鱼儿9 小时前
Flutter 生命周期详解:Stateless 与 Stateful 完全对比
flutter
tangweiguo030519879 小时前
Flutter 页面生命周期超全总结(附 addPostFrameCallback 详解)
flutter