用 Flutter 实现画板

本文作者为 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 <CACHE_PATH>

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

  • 安装指定版本的 flutterfvm install version

  • 删除指定版本的 flutterfvm remove version

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

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

安装独立的 dart 环境

  1. 安装

brew tap dart-lang/dart
 brew 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 {
    @override
    void 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);
    }

    @override
    bool shouldRepaint(CustomPainter oldDelegate) {
        return true
    }
}

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

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

Paint 类参数:

属性名 类型 参考值 功能
color Colors Colors.black 画笔颜色
strokeWidth double 5.0 画笔粗细
strokeCap StrokeCap StrokeCap.round 画笔笔触类型
isAntiAlias bool true 是否启动抗锯齿

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);

  @override
  State<CanvasPage> createState() => _CanvasPageState();
}

class _CanvasPageState extends State<CanvasPage> {
  Path path = Path();

  @override
  Widget 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});

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = color!
      ..strokeWidth = width!
      ..strokeCap = StrokeCap.round
      ..style = PaintingStyle.stroke;
    canvas.drawPath(path!, paint);
  }

  // 是否需要重新绘制
  @override
  bool shouldRepaint(covariant CanvasPaint oldDelegate) {
    // return oldDelegate.path != path;
    return true;
  }
}

三、部署到 chrome 插件

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

30e6d7f7a9d7fc25a73f1bc67c4dbb81.png
  1. <body>下插入<script> 标签

<script src="main.dart.js" type="application/javascript"></script>
  1. 设置扩展程序尺寸,在 html 标签内添加宽高

<html style="height: 600px; width: 300px">
  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.png d2705605b5eabffe181b79d753491038.gif

参考资料

谷歌扩展程序文档
flutter 文档

- END -

关于奇舞团

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

e2f0ce2162dc61b92c8c410d8eb3b2f1.png

猜你喜欢

转载自blog.csdn.net/qiwoo_weekly/article/details/129543422