How to convert a widget with child Image widgets into an image file in Flutter?

Viewed 48

I am using the RenderRepaintBoundary/GlobalKey technique to convert a Widget into an image file. The problem is that any child Image Widgets in the Widget disappear when I convert the Widget to an image file, but everything else on the screen, besides the child Image Widgets, makes it into the image file.

Here's my code. It's basically a screen with a button and an image on it. If you click the button then an image snapshot is taken of the Widget and then the app displays this snapshot and nothing else. As a result, the child Image disappears. There is a bit more explanation below this code:

import 'package:flutter/material.dart';
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title}) : super(key: key);

  final String? title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  Uint8List? ssBytes;
  bool isShowSs = false;

  final GlobalKey genKey = GlobalKey();

  Future<void> takePicture() async {
    RenderRepaintBoundary boundary = genKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
    ui.Image image = await boundary.toImage();
    ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    ssBytes = byteData!.buffer.asUint8List();
    setState(() {
      isShowSs = true;
    });
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    if(isShowSs) {
      return Image.memory(ssBytes!);
    }
    else {
      return RepaintBoundary(
        key: genKey,
        child: Scaffold(
          appBar: AppBar(
            title: Text(widget.title!),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '$_counter',
                  style: Theme.of(context).textTheme.headline4,
                ),
                ElevatedButton(
                    onPressed: () async {
                      await takePicture();
                    },
                    child: Text(
                      "Take Picture",
                    )),
                Image.asset('assets/images/img.png'),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        ),
      );
    }
  }
}

To clarify, this is the element that disappears from the image capture:

Image.asset('assets/images/img.png')

How can I make it so that this sub-image appears in the image capture?

1 Answers

If you are on flutter web and using the html renderer instead of canvaskit, may this article explains the issue: https://github.com/flutter/flutter/issues/47721 It points out that on flutter web RenderRepaintBoundary.toImage() doesn't work for other images like the asset image due to cross-origin restrictions.

... In general on the Web it is hard to get the pixels of a web-page due to cross-origin restrictions. We can partially support it in the CanvasKit backend, but again, we won't be able to get the pixels of cross-origin images or platform views. ...

In this case the solution would be to use the canvaskit renderer if possible.

Related