Widgetを画像ファイルとしてエクスポートする


概要

Flutterで作ったアプリの画面を画像ファイルにする要件があったので調べた。
具体的には、アプリで生成したQRコードをSNSでシェアしたい、というニーズに応えるもの。
英語ではたくさん情報があるが、日本語の情報が見当たらなかったので記載する。

スクリーンショットを撮ってくれとユーザーに促す手もあるが、シェア用に画面を加工したあげた方が見栄えが良い。
SNSでシェアする部分は割愛。

実装

以下最小限の実装。

  1. Widgetへアクセスできるようにするために、GlobalKeyを生成しておく。
  2. 画像ファイル化したい範囲のウィジェットをRepaintBoundaryでラップする。
  3. RepaintBoundaryのkeyに、手順1で生成したGlobalKeyを割り当てる。
  4. どこかのイベント内にWidgetを画像ファイル化する処理を入れる。
import 'dart:convert';
import 'dart:typed_data';
import 'dart:ui' as ui;

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  GlobalKey _globalKey = GlobalKey(); // 1. GlobalKey生成

  // 4. ボタンが押された際の処理
  void _exportToImage() async {
    // 現在描画されているWidgetを取得する
    RenderRepaintBoundary boundary =
        _globalKey.currentContext.findRenderObject();

    // 取得したWidgetからイメージファイルをキャプチャする
    ui.Image image = await boundary.toImage(
      pixelRatio: 3.0,
    );

    // 以下はお好みで
    // PNG形式化
    ByteData byteData = await image.toByteData(
      format: ui.ImageByteFormat.png,
    );
    // バイトデータ化
    final _pngBytes = byteData.buffer.asUint8List();
    // BASE64形式化
    final _base64 = base64Encode(_pngBytes);
    print(_base64);
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary( // 2. 画面全体をキャプチャするならScaffoldを囲ってやる
      key: _globalKey, // 3. GlobalKeyを割り当てて子のボタンからアクセスできるようにしてやる
      child: Scaffold(
        appBar: AppBar(
          title: Text("sample"),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'Widget to Image sample.',
              ),
              RaisedButton(
                onPressed: _exportToImage,
                child: Icon(
                  Icons.image,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

参考

https://medium.com/flutter-community/export-your-widget-to-image-with-flutter-dc7ecfa6bafb
https://api.flutter.dev/flutter/widgets/BuildContext/findRenderObject.html
https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImage.html