用 Flutter 实现画板
迪丽瓦拉
2025-05-28 06:47:03
0

本文作者为 360 奇舞团前端开发工程师

一、前期准备

可以按照flutter 官网搭建下相应的环境,这里大概讲解下用 fvm 搭建 flutter 环境。

MacOS 使⽤ fvm 管理多个 flutter 版本

  1. 安装fvm

    # 使用 brew 安装
    brew tap leoafarias/fvm
    brew install fvm
    # 使用 pub package 安装
    dart pub global activate fvm

    安装成功后命令行里会给出 fvm 目前安装路径 $PATH":"$HOME/.pub-cache/bin"

  2. 配置

    # .bash_profile 中添加
    export PATH = "$PATH":"$HOME/.pub-cache/bin"
    # 使.bash_profile 生效
    source .bash_profile
    # 执行 fvm 命令,看是否出现相关 help
  3. fvm 相关命令

  • 配置 fvm 缓存路径fvm config --cache-path

  • 查看当前安装的版本fvm list

  • 安装指定版本的 flutterfvm install version

  • 删除指定版本的 flutterfvm remove version

  • 指定当前使用哪个版本fvm use version

ps:切换 flutter 版本后需要敲下 fvm flutter doctor。因为每个版本不一样,所以可能需要重新下载运行环境

安装独立的 dart 环境

  1. 安装

brew tap dart-lang/dartbrew install dart
  1. 更新

brew upgrade dart
  1. 重新安装

brew reinstall dart

创建初始项目

fvm flutter create flutter_painter
cd flutter_painter
fvm flutter run

输入fvm flutter run后控制台会提示想选择用哪种设备进行调试,也可以使用flutter devices直接查询设备,flutter run -d all在所有设备上运行项目,我这里选择了用 web chrome 打开项目fvm flutter run -d Chrome。或者 mac 有 Xcode 的话,使用命令open -a Simulator可以直接打开 iOS 模拟器,然后执行flutter run,在执行前确保没有其他模拟器正在运行,这样项目就可以直接运行在 ios 模拟器上。ad2023dab36a3dc9050c012e81b10b92.png

二、了解 Path 与 CustomPainter

CustomPainter, 跟canvas一样,在 flutter 里我们可以用这个类绘制各种自定义图形。在这个类里我们需要实现 paint() 绘制方法与 shouldRepaint() 判断刷新布局的时是否需要重绘。

class SimplePainter extends CustomPainter {@overridevoid paint(Canvas canvas, Size size) {// 绘制代码Paint paint  = Paint()..color = Colors.black..style = PaintingStyle.stroke..strokeWidth = 5.0;Path path = Path();// 对路径进行相关操纵...path.close();canvas.drawPath(path, paint);}@overridebool shouldRepaint(CustomPainter oldDelegate) {return true}
}

具体的绘制由 canvas 画布和 paint 画笔来实现的。
canvas 相关方法:

方法功能
drawLine()画直线
drawCircle()画圆
drawOval()画椭圆
drawRect()画矩形
drawPoints()画点
drawArc()画圆弧

Paint 类参数:

属性名类型参考值功能
colorColorsColors.black画笔颜色
strokeWidthdouble5.0画笔粗细
strokeCapStrokeCapStrokeCap.round画笔笔触类型
isAntiAliasbooltrue是否启动抗锯齿

path 对象是需要绘制的元素集合,相关方法:

方法功能
moveTo()绘制的起点移到指定位置
lineTo()从起点绘制一条直线到指定位置
quadraticBezierTo()绘制二阶贝塞尔曲线
addRect()画矩形
addOval()画椭圆

实践

import 'package:flutter/material.dart';void main() {runApp(const MaterialApp(title: '画板', home: SafeArea(child: CanvasPage())));
}class CanvasPage extends StatefulWidget {const CanvasPage({Key? key}) : super(key: key);@overrideState createState() => _CanvasPageState();
}class _CanvasPageState extends State {Path path = Path();@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('画板')),body: Listener(// 落下onPointerDown: (e) {path.moveTo(e.localPosition.dx, e.localPosition.dy);setState(() {});},// 移动onPointerMove: (e) {path.lineTo(e.localPosition.dx, e.localPosition.dy);setState(() {});},// 离开onPointerUp: (e) {path.moveTo(e.localPosition.dx, e.localPosition.dy);path.close();setState(() {});},child: CustomPaint(foregroundPainter: CanvasPaint(path: path), child: Container(color: Colors.transparent))),);}
}class CanvasPaint extends CustomPainter {Path? path;Color? color; // 画笔颜色double? width;CanvasPaint({required this.path, this.color = Colors.black, this.width = 5});@overridevoid paint(Canvas canvas, Size size) {Paint paint = Paint()..color = color!..strokeWidth = width!..strokeCap = StrokeCap.round..style = PaintingStyle.stroke;canvas.drawPath(path!, paint);}// 是否需要重新绘制@overridebool shouldRepaint(covariant CanvasPaint oldDelegate) {// return oldDelegate.path != path;return true;}
}

三、部署到 chrome 插件

  1. 找到 web/index.html 文件并删除所有 标记:

30e6d7f7a9d7fc25a73f1bc67c4dbb81.png
  1. 下插入

    1. 设置扩展程序尺寸,在 html 标签内添加宽高

    1. manifest.json中更改配置

    {"name": "flutter_painter_plugin","short_name": "flutter_painter_plugin","version": "1.0.0","content_security_policy": {"extension_pages": "script-src 'self'; object-src 'self'"},"action": {"default_popup": "index.html","default_icon": "icons/Icon-192.png"},"manifest_version": 3
    }
    1. 构建拓展程序,需要满足 csp 限制,生成的文件在build/web文件夹下

    fvm flutter build web --web-renderer html --csp
    1. 运行拓展程序

    (1)打开谷歌扩展程序页面

    chrome://extensions/

    (2) 选择开发者模式,选中加载已解压的扩展程序

    8af303c326f748c79b30b9c366111d30.png(3) 选择 build/web 文件夹,就可以看到新的扩展程序

    b7da0b895ab5b90cc70cfb3a1053fd22.pngd2705605b5eabffe181b79d753491038.gif

    参考资料

    谷歌扩展程序文档
    flutter 文档

    - END -

    关于奇舞团

    奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

    e2f0ce2162dc61b92c8c410d8eb3b2f1.png

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...