原文链接:Creating a Drawing Canvas in Flutter - 原文作者 Zaki
本文采用意译的方式
在 Flutter
中创建绘图应用程序是一个有益的过程,可以将用户交互和图像渲染相结合。在本文,我们将手把手构建一个简单的绘图画布,在画布上用户可以在画布上使用手指自由绘画并选择不同颜色的画笔。
最终效果
步骤一:设置 Flutter 环境
在开始编码前,我们需要确保自己系统上安装了 Flutter
。我们可以从 Flutter 官方站点下载并安装 Flutter
。
步骤二:创建一个新的 Flutter 项目
打开我们的终端,然后跑下面的命令行来创建一个新的 Flutter
项目:
flutter create drawing_app
导航到我们项目目录:
代码语言:javascript复制cd drawing_app
步骤三:添加依赖
对于我们 drawing_app
项目,我们需要 flutter_colorpicker
包,以允许用户来挑选颜色。在 pubspec.yaml
的属性 dependencies
下添加下面内容:
dependencies:
flutter:
sdk: flutter
flutter_colorpicker: any
运行 flutter pub get
来安装新的依赖。
步骤四:主要应用入口
打开 main.dart
文件,然后设置程序的主要入口:
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart'
void main() => runApp(MyApp());
步骤五:创建 MyApp 挂件
定义 MyApp
关键,它将主页设置在 MaterialApp
中:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Drawing App',
home: DrawingPage(),
);
}
}
步骤六:设置 DrawingPage 挂件
创建有状态的挂件 DrawingPage
,在那里绘制画布:
class DrawingPage extends StatefulWidget {
const DrawingPage({super.key});
@override
_DrawingPageState createState() => _DrawingPageState();
}
步骤七:管理绘制状态
在 _DrawingPageState
中,管理绘制点,选定颜色和描边宽度的状态:
class _DrawingPageState extends State<DrawingPage> {
List<DrawingPoints> points = [];
Color selectedColor = Colors.black;
double strokeWidth = 4.0;
List<Color> colorOptions = [
Colors.black,
Colors.red,
Colors.green,
Colors.blue,
Colors.yellow,
Colors.purple,
Colors.orange,
Colors.brown,
]; // 自定义颜色列表
步骤八:构建 UI
定义一个 AppBar
来进行控制,和 GestureDetector
来处理绘制手势:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Draw on Canvas"),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.clear),
onPressed: () => setState(() => points.clear()),
),
IconButton(
icon: const Icon(Icons.color_lens),
onPressed: pickColor,
),
],
),
body: GestureDetector(
onPanUpdate: (details) {
RenderBox? renderBox = context.findRenderObject() as RenderBox?;
if (renderBox != null) {
setState(() {
points.add(
DrawingPoints(
points: renderBox.globalToLocal(details.localPosition),
paint: Paint()
..strokeCap = StrokeCap.round
..isAntiAlias = true
..color = selectedColor
..strokeWidth = strokeWidth,
isPoint: true,
),
);
});
}
},
onPanEnd: (details) {
setState(() {
points.add(DrawingPoints(
points: Offset.zero, paint: Paint(), isPoint: false));
});
},
child: CustomPaint(
painter: DrawingPainter(points: points),
child: Container(),
),
),
);
}
步骤九:处理颜色变更
实现 pickerColor
来显示颜色拾取器,以允许用户更改画笔的颜色:
void pickColor() {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Choose a color'),
content: SingleChildScrollView(
child: BlockPicker(
pickerColor: selectedColor,
availableColors: colorOptions, // 在这使用自定义颜色列表
onColorChanged: changeColor,
),
),
actions: <Widget>[
TextButton(
child: const Text('Close'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
void changeColor(Color color) {
Navigator.of(context).pop(); // 关闭对话框
setState(() {
selectedColor = color;
});
}
}
步骤十:实现绘制逻辑
使用 CustomPainter
来处理真实的绘制逻辑:
class DrawingPoints {
Paint paint;
Offset points;
bool isPoint;
DrawingPoints({
required this.points,
required this.paint,
this.isPoint = true,
});
}
class DrawingPainter extends CustomPainter {
List<DrawingPoints> points;
DrawingPainter({required this.points});
@override
void paint(Canvas canvas, Size size) {
for (int i = 0; i < points.length - 1; i ) {
if (points[i].isPoint &&
points[i 1].isPoint) {
canvas.drawLine(
points[i].points, points[i 1].points, points[i].paint);
}
}
}
@override
bool shouldRepaint(covariant DrawingPainter oldDelegate) => true;
}
类: DrawingPoints
目的:在画布中展示单个点。
字段:
Offset points
:表示点在画布上的坐标。Paint paint
:指定此点要使用的绘画风格(颜色、绘制等)。bool isPoint
:布尔值,决定是否应该将对象视为绘制的点。这可能用于根据上下文或者触摸交互类型以不同方式处理触摸事件(例如,绘制一个点而不是一条线)。Constructor
:明确需要提供的points
和paint
。除非指定,否则isPoints
默认是true
。
类: DrawingPainter
目的:自定义画家类是基于 DrawingPoints
列表在画布上绘图。
字段:
List<DrawingPoints> points
:DrawingPoints
列表定义我们想要在画布上绘制的点。Constructor
:初始化points
列表数据。- 方法:
paint(Canvas canvas, Size size)
:当挂件需要重绘时候调用。如果当前点和下一个点标记为可绘制(isPoint
为真),此方法遍历列表并从每个点到下一个点绘制一条线。- 它使用
Canvas
对象中的drawLine
方法,使用DrawingPoints
中指定的绘制样式在连续点之间进行连线。 shouldRepaint(covariant DrawingPainter oldDelegate)
:总是返回true
,表明每次都要更新绘画。如果点列表不频繁更改,这不是性能最优的选择,因为即使没有必要也会重新绘制。
paint 方法的逻辑
paint
方法的逻辑本质上是在连续的点之间绘线,这些点应该是 isPoint
为 true
的点。如果点不是连续的,即 isPoint
为 false
,则跳过绘制到下一个点。这是处理用户手指抬离屏幕然后触屏生成另一个点绘制不连续点的简单方法。
步骤十一:测试应用
在终端上运行 flutter run
来运行我们的程序,或者使用 IDE
的运行按钮。我们应该可以在屏幕上绘制并且更改画笔