Flutter绘图插件window_paint的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter绘图插件window_paint的使用

WindowPaint 允许你在任何其他小部件之上进行平移、缩放和绘制。它结合了新的 InteractiveViewerCustomPainter,为你提供了最少的必要功能来开始使用。

开始使用

对于一般的用例,你应该可以通过玩一下 example/lib/main.dart 文件来快速上手。

如果你想要添加自己的绘图工具类型,建议你查看 DrawObjectAdapterDrawObject 及其子类的参考实现。

该库不包含作为核心库一部分的UI控件,但你可以复制 example/lib/window_paint_control.dart 文件并将其作为起点。

适配器和对象

DrawObjectAdapter 负责创建、更新、(反)序列化和选择它们对应的 DrawObject。这些类的文档已经足够详细,可以让你编写自己的实现。

该库附带了四个参考实现:

DrawObjectAdapter DrawObject
PanZoomAdapter DrawNoop
DrawPencilAdapter DrawPencil
DrawRectangleAdapter DrawRectangle
DrawRectangleCrossAdapter DrawRectangleCross

此外,示例项目还附带了一个第五个:

DrawObjectAdapter DrawObject
DrawTextAdapter DrawText

状态恢复

该库通过 RestorableWindowPaintController 提供了完整的状态恢复支持:

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
      restorationScopeId: 'root', // 记住要设置这个,否则状态恢复将被禁用。
    );
  }
}

class MyHomePage extends StatefulWidget {
  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with RestorationMixin { // 添加 RestorationMixin。
  final adapters = <DrawObjectAdapter>[
    // 在这里添加你的适配器...
    //
    // 记住始终提供相同的适配器,
    // 否则不是所有对象都能成功恢复!
  ];

  // 这是你的好朋友。
  late final RestorableWindowPaintController _controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    _controller = RestorableWindowPaintController(
      adapters,
      initialColor: Colors.red,
    );
  }

  // 注册控制器以进行状态恢复。
  [@override](/user/override)
  void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
    registerForRestoration(_controller, 'controller');
  }

  // 给你的组件一个唯一的 restorationId,在包含的 RestorationScope 中。
  [@override](/user/override)
  String? get restorationId => 'main_page';

  [@override](/user/override)
  void dispose() {
    // 当然,记得释放它。
    _controller.dispose();
    super.dispose();
  }
}

路径简化

查看 DrawPencil.finalize() 来了解 simplifyPoints() 的实际应用。

此函数将数百个单独的点减少到最小限度,以在给定的 tolerance 下大致表示相同的路径。结果非常令人印象深刻。

此算法的功劳归于 Vladimir Agafonkin

碰撞检测

基于画布的路径渲染是非常动态的。因此,如果我们想进行对象选择,我们就必须自己管理碰撞检测。

幸运的是,有人已经为我们解决了数学问题!

对于简单的 AABB 碰撞检测,我们可以利用 Flutter 的 Rect.contains(Offset) 方法。

对于更复杂的碰撞检测,如 OBB,我们实现了 Line(Offset start, Offset end, double extent) 类。这允许我们对对角线进行碰撞检测(如铅笔线条或矩形内部的交叉线)。

如果有更多可能性,可以查看出色的 vector_math

命名

窗口绘画的名字来源于我的童年记忆。

我和我的兄弟姐妹曾经用“艺术”在卧室的窗户上作画,那种你能用这个库创作出的令人惊叹的艺术作品 👨‍🎨。干燥后,我们的作品覆盖了外面的世界——而这正是这个包所做的。

许可证

MIT


完整示例代码

以下是完整的示例代码,展示如何使用 window_paint 插件:

import 'package:example/draw/adapters/draw_text_adapter.dart';
import 'package:example/window_paint_control.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:window_paint/window_paint.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Window Paint Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Window Paint Demo Page'),
      restorationScopeId: 'root',
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  final String title;

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> with RestorationMixin {
  static const debugHitboxes = false;

  final adapters = <DrawObjectAdapter>[
    PanZoomAdapter(),
    DrawPencilAdapter(
      width: 2.0,
      debugHitboxes: debugHitboxes,
    ),
    DrawRectangleAdapter(
      width: 2.0,
      debugHitboxes: debugHitboxes,
    ),
    DrawRectangleCrossAdapter(
      width: 2.0,
      debugHitboxes: debugHitboxes,
    ),
    DrawTextAdapter(),
  ];

  late final RestorableWindowPaintController _controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    _controller = RestorableWindowPaintController(
      adapters,
      initialColor: Colors.red,
    );
  }

  [@override](/user/override)
  void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
    registerForRestoration(_controller, 'controller');
  }

  [@override](/user/override)
  String? get restorationId => 'main_page';

  [@override](/user/override)
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      resizeToAvoidBottomInset: false,
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Center(
          child: Column(
            children: [
              WindowPaintControl(
                controller: _controller.value,
              ),
              WindowPaint(
                controller: _controller.value,
                maxScale: 10.0,
                adapters: adapters,
                child: Container(
                  decoration: BoxDecoration(
                    color: Colors.amber[100],
                  ),
                  height: 400,
                ),
                onAdd: (object) {
                  print('###ADD###');
                  print(object.toJSON());
                  print('~~~END~~~');
                },
                onChange: (from, to) {
                  print('###CHANGE###');
                  print(from.toJSON());
                  print('###TO###');
                  print(to.toJSON());
                  print('~~~END~~~');
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

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

1 回复

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


window_paint 是一个用于在 Flutter 应用中实现绘图功能的插件。它允许用户在一个窗口中绘制图形、线条、文本等,类似于一个简单的绘图工具。使用这个插件,你可以创建一个自定义的绘图界面,让用户进行自由绘制。

安装 window_paint 插件

首先,你需要在 pubspec.yaml 文件中添加 window_paint 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  window_paint: ^0.0.1  # 请检查最新版本

然后运行 flutter pub get 来安装插件。

基本用法

以下是一个简单的示例,展示如何使用 window_paint 插件创建一个基本的绘图界面:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Window Paint Example'),
        ),
        body: PaintWindow(),
      ),
    );
  }
}

class PaintWindow extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return WindowPaint(
      onDraw: (Canvas canvas, Size size) {
        // 在这里进行绘制
        final paint = Paint()
          ..color = Colors.blue
          ..strokeWidth = 5.0
          ..style = PaintingStyle.stroke;

        // 绘制一个矩形
        canvas.drawRect(Rect.fromLTWH(50, 50, 200, 100), paint);

        // 绘制一个圆形
        canvas.drawCircle(Offset(150, 150), 50, paint);

        // 绘制文本
        final textStyle = TextStyle(color: Colors.black, fontSize: 24);
        final textSpan = TextSpan(
          text: 'Hello, Flutter!',
          style: textStyle,
        );
        final textPainter = TextPainter(
          text: textSpan,
          textDirection: TextDirection.ltr,
        );
        textPainter.layout(minWidth: 0, maxWidth: size.width);
        textPainter.paint(canvas, Offset(50, 200));
      },
    );
  }
}

解释

  1. WindowPaint Widget: 这是 window_paint 插件的核心部件。它提供了一个画布 (Canvas),你可以在上面进行绘制。

  2. onDraw 回调: 这是 WindowPaint 的一个参数,它接收一个 CanvasSize 对象。你可以在 onDraw 回调中使用 Canvas 对象来绘制各种图形、线条、文本等。

  3. Paint 对象: Paint 对象用于定义绘制的样式,如颜色、线条宽度、填充样式等。

  4. Canvas 方法: Canvas 提供了多种绘制方法,如 drawRectdrawCircledrawLine 等,用于绘制不同的图形。

高级用法

你还可以通过监听用户的手势来创建一个交互式的绘图界面。例如,你可以让用户通过触摸屏幕来绘制线条:

class PaintWindow extends StatefulWidget {
  [@override](/user/override)
  _PaintWindowState createState() => _PaintWindowState();
}

class _PaintWindowState extends State<PaintWindow> {
  List<Offset> points = [];

  [@override](/user/override)
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: (details) {
        setState(() {
          points.add(details.localPosition);
        });
      },
      onPanEnd: (details) {
        setState(() {
          points.add(null); // 表示线条的结束
        });
      },
      child: WindowPaint(
        onDraw: (Canvas canvas, Size size) {
          final paint = Paint()
            ..color = Colors.red
            ..strokeWidth = 4.0
            ..strokeCap = StrokeCap.round;

          for (int i = 0; i < points.length - 1; i++) {
            if (points[i] != null && points[i + 1] != null) {
              canvas.drawLine(points[i], points[i + 1], paint);
            }
          }
        },
      ),
    );
  }
}
回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!