Flutter绘图插件window_paint的使用
Flutter绘图插件window_paint的使用
WindowPaint
允许你在任何其他小部件之上进行平移、缩放和绘制。它结合了新的 InteractiveViewer
和 CustomPainter
,为你提供了最少的必要功能来开始使用。
开始使用
对于一般的用例,你应该可以通过玩一下 example/lib/main.dart
文件来快速上手。
如果你想要添加自己的绘图工具类型,建议你查看 DrawObjectAdapter
和 DrawObject
及其子类的参考实现。
该库不包含作为核心库一部分的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
更多关于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));
},
);
}
}
解释
-
WindowPaint
Widget: 这是window_paint
插件的核心部件。它提供了一个画布 (Canvas
),你可以在上面进行绘制。 -
onDraw
回调: 这是WindowPaint
的一个参数,它接收一个Canvas
和Size
对象。你可以在onDraw
回调中使用Canvas
对象来绘制各种图形、线条、文本等。 -
Paint
对象:Paint
对象用于定义绘制的样式,如颜色、线条宽度、填充样式等。 -
Canvas
方法:Canvas
提供了多种绘制方法,如drawRect
、drawCircle
、drawLine
等,用于绘制不同的图形。
高级用法
你还可以通过监听用户的手势来创建一个交互式的绘图界面。例如,你可以让用户通过触摸屏幕来绘制线条:
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);
}
}
},
),
);
}
}