如何用Flutter将widget生成图片

2,273 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

问题:最近上线了一款你画我猜游戏,由于政策以及合规性,尺度可能会无法管控,因此需要上一个审核功能。

需求:将游戏中的画面截图,实时生成画面,交由后台审核

实现方案:

先介绍下我们如何实现:将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输出当前图片

图片.png

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);
    }
  }
}