Flutter绘图工具插件flutter_painter_v2的使用

Flutter绘图工具插件flutter_painter_v2的使用

概要

Flutter Painter 提供了一个可以用于绘画的组件。目前,它支持以下功能:

  • 自由绘画:您可以以任意宽度和颜色绘制任何内容。
  • 对象:您可以轻松且熟悉地移动、缩放和旋转的对象,如:
    • 文本:带有任意 TextStyle 的文本。
    • 形状:带有任意 Paint 的线条、箭头、椭圆和矩形。
    • 图像:可以翻转的图像。
  • 自由式橡皮擦:可以擦除任何部分的绘画或您不希望在画家上的对象。

这些称为可绘制对象

您可以为您的绘画提供颜色或图像作为背景,并导出您的绘画作为图像。

示例

您可以查看示例标签以了解如何使用该包的一个例子。

示例项目托管在这里,如果您想亲自尝试一下!

视频演示:

Flutter Painter Video Demo

使用方法

首先,您需要一个 PainterController 对象。PainterController 控制不同的可绘制对象、您正在绘制的背景并为 FlutterPainter 小部件提供所需的设置。然后,在您的 UI 中,使用分配了控制器的 FlutterPainter 小部件。

class ExampleWidget extends StatefulWidget {
  const ExampleWidget({Key? key}) : super(key: key);

  @override
  _ExampleWidgetState createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> {
  PainterController controller = PainterController();

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 300,
      height: 300,
      child: FlutterPainter(
        controller: controller,        
      ),
    );
  }
}

您还可以使用 FlutterPainter.builder 构造函数,该构造函数使用构建器方法,当控制器发生更改时会自动更新,而无需使用 setState、回调或监听器。但是,这将比 StatefulWidget 性能更差,因为它会更频繁地重建,因此如果依赖于 PainterController 的小部件树较简单,建议使用此方法。

class ExampleWidget extends StatefulWidget {
  const ExampleWidget({Key? key}) : super(key: key);

  @override
  _ExampleWidgetState createState() => _ExampleWidgetState();
}

class _ExampleWidgetState extends State<ExampleWidget> {
  PainterController controller = PainterController();

  @override
  Widget build(BuildContext context) {
    return FlutterPainter.builder(
      controller: controller,
      builder: (context, painter){
        return SizedBox(
          width: 300,
          height: 300,
          child: painter
        );
      }
    ); 
  }
}

注意FlutterPainter 不定义其自身的大小约束,因此建议使用可以为其子节点提供大小约束的小部件,例如 SizedBoxAspectRatio(更多关于约束的信息在这里)。

注意:如果您 UI 的多个部分依赖于 PainterController,您可以使用 ValueListenableBuilder,其中 valueListenable 是您的控制器,这将在控制器更新时自动重新构建。这是示例项目中使用的策略。

回调

FlutterPainter 有一些有用的回调,当内部更改发生时会被调用。

  • onDrawableCreated:当从 FlutterPainter 创建一个可绘制对象时被调用。传递可绘制对象作为参数。
  • onDrawableDeleted:当从 FlutterPainter 删除一个可绘制对象时被调用。传递可绘制对象作为参数。
  • onSelectedObjectDrawableChanged:当所选对象可绘制对象发生变化时被调用。这在您想要显示一些用于编辑对象属性的 UI 时非常有用。传递所选对象可绘制对象作为参数。
    • 如果可绘制对象被更新(例如移动),传递的可绘制对象将变为无效。确保使用 PainterController.selectedObjectDrawable 获取所选可绘制对象的最新值。
  • onPainterSettingsChanged:当从 FlutterPainter 更改 PainterController 的设置时被调用。传递新的设置作为参数。

PainterController

PainterController 是 Flutter Painter 运行的核心。它控制 FlutterPainter 的设置、背景及其所有可绘制对象和所选对象可绘制对象。

PainterController 上的所有 setter 都会直接通知您的 FlutterPainter 进行响应并重绘。如果您使用 FlutterPainter.builder,构建器会自动调用以构建小部件树。如果不这样做,请确保使用 setState 并监听回调。

注意:如果您使用多个画家,请确保每个 FlutterPainter 小部件都有自己的 PainterController不要 使用相同的控制器处理多个画家。

设置

目前有三种类型的设置:

  • freeStyleSettings:它们控制用于绘制涂鸦的参数,如宽度和颜色。它还有一个字段来启用/禁用涂鸦,以防止用户在 FlutterPainter 上进行绘制。
  • textSettings:它们主要控制绘制文本的 TextStyle。它还有一个焦点节点字段(更多关于焦点节点的信息在这里),允许您检测用户何时开始和停止编辑文本。
  • objectSettings:这些设置控制可以移动、缩放和旋转的对象。文本、形状和图像都被视为对象。它控制布局辅助,允许居中对象并以直角旋转它们,并且有关对象控件的设置,用于缩放、旋转和调整大小。
  • shapeSettings:这些控制使用的画笔和形状工厂(形状工厂用于创建形状),以及形状是否一次性绘制还是连续绘制。
  • scaleSettings:这些设置控制画家上的缩放(放大/缩小)。默认情况下,缩放是禁用的。

您可以通过 PainterController 构造函数中的设置参数提供初始设置以绘制您想要的东西。

每个设置和子设置都有扩展的 setter 和 getter,您可以使用它们读取和修改该设置的值。

例如,这是如何修改自由式绘制的笔划宽度:

void setStrokeWidth(double value){
  controller.freeStyleStrokeWidth = value;
}

注意:如果您不使用扩展库,请注意所有设置对象都是不可变的并且不能被修改,因此为了更改某些设置,您需要创建当前设置的副本并应用所需更改(这类似于如何复制 ThemeData)。

背景

您还可以从控制器为 FlutterPainter 小部件提供背景。您可以使用颜色或图像作为背景。

为了使用颜色,您可以直接调用 backgroundDrawable 扩展 getter。

void setBackground(){
  // 设置背景为黑色
  controller.background = Colors.black.backgroundDrawable;
}

为了使用图像,您需要一个来自 dart:ui 库的 Image 对象。由于 Flutter 有一个来自 Material 包的 Image 小部件,我们将需要的图像类型称为 ui.Image

import 'dart:ui' as ui;
ui.Image? myImage;

为了从常见的图像源(文件、资产、网络)获取 ui.Image 对象,您可以使用 ImageProvider 并使用 image 扩展 getter(ImageProvider 的示例:FileImageMemoryImageNetworkImage)。此 getter 返回 Future<ui.Image>

然后,您可以使用 ui.ImagebackgroundDrawable 扩展 getter。

void setBackground() async {
  // 从网络获取图像并创建一个 [ui.Image] 对象
  final ui.Image myImage = await NetworkImage('https://picsum.photos/960/720').image;
  // 设置背景为图像
  controller.background = myImage.backgroundDrawable;
}

背景也可以直接从 PainterController 构造函数中分配。

可绘制对象

绘制在 FlutterPainter 上的所有可绘制对象都存储并由 PainterController 控制。在大多数使用场景中,您不需要直接与可绘制对象交互。然而,您可能需要从代码中添加、插入、替换或删除可绘制对象(而不让用户实际绘制它们)。

您可以从 PainterController 构造函数分配初始的 drawables 列表以初始化控制器。您还可以从控制器修改它们,但请注意,使用 PainterController 的方法,而不是直接修改 drawables 列表。

正确做法

void addMyDrawables(List<Drawable> drawables){
  controller.addDrawables(drawables);
}

错误做法

void addMyDrawables(List<Drawable> drawables){
  controller.drawables.addAll(drawables);
}

所选对象可绘制对象

PainterController 还提供了通过 PainterController.selectedObjectDrawable 获取的当前所选 ObjectDrawable。此值会根据 UI 的任何更改保持最新(例如,用户选择一个新的对象可绘制对象)。您也可以编程方式选择和取消选择一个对象可绘制对象,前提是它在控制器的可绘制对象列表中。

void selectObjectDrawable(ObjectDrawable drawable){
  controller.selectObjectDrawable(drawable);
}

void deselectObjectDrawable(){
  controller.deselectObjectDrawable();
}

所选对象可绘制对象也会在控制器中被替换或删除时自动更新。

渲染图像

PainterController,您可以将 FlutterPainter 的内容渲染为 PNG 编码的 ui.Image 对象。为此,您需要提供输出图像的大小。所有绘制都将根据该大小进行缩放。

ui.Image 对象,您可以将其转换为原始字节列表 (Uint8List) 以便使用 Image.memory 显示或保存为文件。

Uint8List? renderImage(Size size) async {
  final ui.Image renderedImage = await controller.renderImage(size);
  final Uint8List? byteData = await renderedImage.pngBytes;
  return byteData;
}

更多关于Flutter绘图工具插件flutter_painter_v2的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter绘图工具插件flutter_painter_v2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用 flutter_painter_v2 插件在 Flutter 中进行绘图的示例代码。flutter_painter_v2 是一个强大的 Flutter 插件,用于在画布上绘制图形、文本和图像。以下是一个简单的示例,展示了如何使用该插件。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_painter_v2 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_painter_v2: ^最新版本号  # 请替换为实际的最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,创建一个 Flutter 应用,并在其中使用 flutter_painter_v2。以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:flutter_painter_v2/flutter_painter_v2.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Painter V2 Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Painter V2 Demo'),
        ),
        body: PainterScaffold(
          painterController: PainterController()
            ..addDrawingCommand(
              DrawingCommand.line(
                start: Offset(50, 50),
                end: Offset(200, 200),
                paint: Paint()
                  ..color = Colors.black
                  ..strokeWidth = 4.0,
              ),
            )
            ..addDrawingCommand(
              DrawingCommand.circle(
                center: Offset(150, 150),
                radius: 50.0,
                paint: Paint()
                  ..color = Colors.red
                  ..style = PaintingStyle.fill,
              ),
            )
            ..addDrawingCommand(
              DrawingCommand.text(
                text: 'Hello Flutter Painter!',
                position: Offset(50, 250),
                textStyle: TextStyle(
                  color: Colors.blue,
                  fontSize: 24,
                ),
              ),
            ),
          backgroundColor: Colors.white,
        ),
      ),
    );
  }
}

在这个示例中,我们做了以下几件事:

  1. 引入必要的包:flutter/material.dartflutter_painter_v2/flutter_painter_v2.dart
  2. 创建一个 MyApp 类,它继承自 StatelessWidget
  3. MyAppbuild 方法中,创建一个 MaterialApp,并设置应用的主题和主页。
  4. 主页是一个 Scaffold,包含一个 AppBar 和一个 PainterScaffold
  5. PainterScaffoldflutter_painter_v2 提供的用于绘图的组件。我们传递了一个 PainterController 对象给它,该对象管理所有的绘图命令。
  6. 使用 addDrawingCommand 方法向 PainterController 添加绘图命令。在这个示例中,我们添加了一条线、一个圆和一些文本。

请注意,DrawingCommand 类提供了多种绘图方法,如 linecirclerectangletext 等,你可以根据需要选择合适的命令来绘制图形。

这个示例展示了如何使用 flutter_painter_v2 插件在 Flutter 应用中进行基本的绘图操作。如果你需要更高级的功能,如撤销/重做、保存绘图到文件等,可以参考 flutter_painter_v2 的官方文档和示例代码。

回到顶部