本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
问题:最近上线了一款你画我猜游戏,由于政策以及合规性,尺度可能会无法管控,因此需要上一个审核功能。
需求:将游戏中的画面截图,实时生成画面,交由后台审核。
实现方案:
先介绍下我们如何实现:将widget的视图转成图片格式
RepaintBoundary
-
防止重绘
在自定义绘制时,我们常常使用 RepaintBoundary 做性能优化,来避免重复绘制。RepaintBoundary 内部的 widget 拥有独立画布,可以不被干扰,官方很多组件外层也包了层 RepaintBoundary。
这个组件的作用很明显,就是
防止边界重绘。
适用场景:动态头像等widget在运动过程中,有时会导致周围widget树节点发生抖动。此时我们可以将此运动的widget包一层RepaintBoundary,就会解决这个问题。
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Obx(() => Column(
children: [
//动态头像可能会导致其他UI闪动
//此时给他加一个RepaintBoundary可以解决这个问题
Row(
children:[
RepaintBoundary(
child:avatar()),
gameUI(),
]),
otherUI(),
]);
}
Widget gameUI(){
//XXXXXXXX;
}
-
生成截图
今天,我们用到 RepaintBoundary 的第二个常用能力:生成截图。
如下图所示,当widget树渲染完后,根据渲染出的UI输出当前图片
final _boundaryKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.transparent,
body: RepaintBoundary(
key: _boundaryKey,
child: Column(
children: [
Container(
color: Colors.transparent,
),
webView(initUrl:"xxx.html")
]));
);
}
/// 通过 RenderRepaintBoundary 生成图片
void _generatePicWidget() async {
//根据Globalkey获取RenderObject对象
final boundary = _boundaryKey.currentContext?.findRenderObject();
if (boundary != null && boundary is RenderRepaintBoundary) {
final image = await boundary.toImage();
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
if (byteData != null) {
//获取当前文件目录
Directory fileDirectory = await getApplicationDocumentsDirectory();
String filePath = fileDirectory.path;
Directory directory;
directory = await new Directory(
filePath + "${Platform.pathSeparator}" + 'picuser')
.create(recursive: true);
assert(await directory.exists() == true);
//将字节流数据转Unit8编码
Uint8List imageData = byteData.buffer.asUint8List();
//将数据写入目录下的文件
File(directory.absolute.path+"/temp.png").writeAsBytes(imageData);
}
}
}