内容简介:GitHub地址:先上效果图:这次的想法是移植自项目中的一个小功能:截屏当前页面,添加涂鸦功能后,分享给第三方APP。分享功能我们暂不讨论,使用插件可以轻松完成,重点是截屏+涂鸦+图片保存。
本文涉及的知识点:截图、图片保存、根据用户手势实时绘制canvas。
GitHub地址: github.com/yumi0629/Fl…
先上效果图:
需求分析
这次的想法是移植自项目中的一个小功能:截屏当前页面,添加涂鸦功能后,分享给第三方APP。分享功能我们暂不讨论,使用插件可以轻松完成,重点是截屏+涂鸦+图片保存。
具体实现思路是:截取当前屏幕内容,保存至APP缓存目录,涂鸦页面再去读取文件,依然是使用CustomerPaint实现根据用户手势实时绘制,最后将用户涂鸦部分与原图片组合保存至本地。给图片加水印的实现其实就是截屏,因为截取当前屏幕内容实际上也是将Widget转化为byteData再转化为File的过程。
截屏并保存
Flutter提供了一个 RepaintBoundary
Widget来实现截图的功能,用 RepaintBoundary
包裹需要截取的部分, RenderRepaintBoundary
可以将 RepaintBoundary
包裹的部分截取出来;然后通过 boundary.toImage()
方法转化为 ui.Image
对象,再使用 image.toByteData()
将image转化为 byteData
;最后通过 File().writeAsBytes()
存储为文件对象:
RepaintBoundary( key: _repaintKey, child: Stack( alignment: Alignment.bottomRight, children: <Widget>[ Image.asset( 'images/food01.jpeg', fit: BoxFit.cover, ), Icon(Icons.translate,), ], ), ) 复制代码
RenderRepaintBoundary boundary = _repaintKey.currentContext.findRenderObject(); ui.Image image = await boundary.toImage(); ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); Uint8List pngBytes = byteData.buffer.asUint8List(); File(tempPath).writeAsBytes(pngBytes); 复制代码
需要注意的地方:
- 记住给
RepaintBoundary
添加一个key
,因为我们需要通过这个key
来找到当前的RenderObject
,生成一个RenderRepaintBoundary
对象; -
image.toByteData(format: format)
可以自定义存储格式,一般图片为png
格式,但是要注意,如果你的RepaintBoundary
包裹的部分没有设置背景色,那么存储出来的图片可能会有背景色缺失的问题,boundary.toImage()
并不会自动添加一个你想要的白色背景,这种情况在截取Text
的时候会尤其明显。
Flutter中获取存储路径
我们可以通过官方插件 path_provider
来获取APP的内部和外部存储路径:
-
Directory tempDir = await getTemporaryDirectory()
,等同于iOS中的NSCachesDirectory
API和Android中的getCacheDir
API; -
Directory externalDir = await getExternalStorageDirectory()
,不支持iOS(会抛出UnsupportedError),等同于Android中的getExternalStorageDirectory
API; -
Directory applicationDir = await getApplicationDocumentsDirectory()
,等同于iOS中的NSDocumentsDirectory
API和Android中的AppData
目录;
要注意的是 getExternalStorageDirectory()
方法,大多数情况下是访问SDCard路径,因此即使在Android中调用,也要注意权限问题,推荐使用 permission_handler
插件。 还有就是存储文件的时候要养成一个好习惯,先判断下父目录是否存在:
void _saveImage(Uint8List uint8List, Directory dir, String fileName) async { bool isDirExist = await Directory(dir.path).exists(); if (!isDirExist) Directory(dir.path).create(); ······ File(tempPath).writeAsBytes(uint8List); } 复制代码
涂鸦
通过 GestureDetector
包裹需要绘制的区域,收集用户的手势路径信息,通过 canvas.drawLine()
方法来绘制路径:
List<Offset> points = []; GestureDetector( onPanStart: (details) { }, onPanUpdate: (details) { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition); state(() { points.add(localPosition); }); }, onPanEnd: (details) { }, ) 复制代码
for (int i = 0; i < points.length - 1; i++) { if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], _linePaint); } 复制代码
为什么不直接用 canvas.drawPoints()
方法呢?
即使将 PointMode
设置为 PointMode.lines
,你会发现,绘制出来的点集合并不是无缝连接在一起的,看起来就像是虚线一样:
因此我们还是使用 drawLine()
强行将所有点连接起来。
RepaintBoundary
来保存图片就可以啦~~~
以上所述就是小编给大家介绍的《用Flutter实现一个涂鸦和加水印功能》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 将涂鸦变成真画
- Pulsar 在涂鸦智能的实践
- Neditor 2.1.17 发布,修复涂鸦板报错
- Neditor 2.1.17 发布,修复涂鸦板报错
- 涂鸦智能的 Istio 企业级生产环境的实践
- 涂鸦智能 dubbo-go 亿级流量的实践与探索
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Domain-Driven Design Distilled
Vaughn Vernon / Addison-Wesley Professional / 2016-6-2 / USD 36.99
Domain-Driven Design (DDD) software modeling delivers powerful results in practice, not just in theory, which is why developers worldwide are rapidly moving to adopt it. Now, for the first time, there......一起来看看 《Domain-Driven Design Distilled》 这本书的介绍吧!