Graham Wheeler
Graham Wheeler

Reputation: 2814

Get a PNG file from a Flutter Widget without drawing to the screen

I have a game a bit like Wordle, and I want to add a share capability much like Wordle does. I have a flag on my game grid widget which allows me to render the normal displayed version, or a special version for sharing. I'd like to use the widget to get a PNG file (or PNG byte data). I'd prefer to not actually draw this to the screen, just render it to some framebuffer and convert to PNG, although rendering to the screen is okay if the alternative is not possible. Looking through the docs, its not obvious whether what I am trying to do is possible or even makes sense (it seems I probably have to get the widget instantiated as an element and then get a render object, which seems to imply having to put it in my layout and draw it to the display), but maybe I am missing something. Is there a way to do this?

Upvotes: 4

Views: 1915

Answers (3)

Hadiuzzaman
Hadiuzzaman

Reputation: 492

Here is the complete implementation, don't need any package

import 'package:path_provider/path_provider.dart';
import 'dart:io';
class WidgetScreenshotExample extends StatefulWidget {
  const WidgetScreenshotExample({super.key});

  @override
  State<WidgetScreenshotExample> createState() =>
      _WidgetScreenshotExampleState();
}

class _WidgetScreenshotExampleState extends State<WidgetScreenshotExample> {
  final globalKey = GlobalKey();

  Future<Uint8List> takeScreenshot() async {
    //Get the render object from context.
    final boundary =
        globalKey.currentContext?.findRenderObject() as RenderRepaintBoundary;
    //Convert to the image
    final image = await boundary.toImage();
    final bytes = await image.toByteData(format: ImageByteFormat.png);
    Uint8List memoryImageData = bytes!.buffer.asUint8List();
    return memoryImageData;
  }

  Future<String> saveImage(Uint8List bytes) async {
    final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    String path = "";
    try {
      Directory root = await getTemporaryDirectory();
      String directoryPath = '${root.path}/appName';
      // Create the directory if it doesn't exist
      await Directory(directoryPath).create(recursive: true);
      String filePath = '$directoryPath/$timestamp.jpg';
      final file = await File(filePath).writeAsBytes(bytes);
      path = file.path;
    } catch (e) {
      debugPrint(e.toString());
    }
    return path;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: RepaintBoundary(
        key: globalKey,
        child: Container(
          color: Colors.red,
          padding: const EdgeInsets.all(20),
          child: const Text('Your Widget'),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.save),
        onPressed: () async {
          final imageData = await takeScreenshot();
          await saveImage(imageData);
        },
      ),
    );
  }
}

Upvotes: 0

krishnaacharyaa
krishnaacharyaa

Reputation: 25110

You might want to look at flutter's screenshot package.

A simple package to capture widgets as Images. Now you can also capture the widgets that are not rendered on the screen!

1. Create Instance of Screenshot Controller

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  Uint8List _imageFile;

  //Create an instance of ScreenshotController
  ScreenshotController screenshotController = ScreenshotController(); 

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
  }
  ...
}

2. Wrap the widget that you want to capture inside Screenshot Widget. Assign the controller to screenshotController that you have created earlier

Screenshot(
    controller: screenshotController,
    child: Text("This text will be captured as image"),
),

3. Take the screenshot by calling capture method. This will return a Uint8List

screenshotController.capture().then((Uint8List image) {
    //Capture Done
    setState(() {
        _imageFile = image;
    });
}).catchError((onError) {
    print(onError);
});

Extra: Saving images to Specific Location

final directory = (await getApplicationDocumentsDirectory ()).path; //from path_provide package
String fileName = DateTime.now().microsecondsSinceEpoch;
path = '$directory';

screenshotController.captureAndSave(
    path //set path where screenshot will be saved
    fileName:fileName 
);

Refer this example

Upvotes: 3

Aakash Kumar
Aakash Kumar

Reputation: 1187

I was once working on an app in which I needed something similar. After searching for a while I found a package named 'screenshot'

This package lets you convert both visible or invisible widgets to an image.

Here is the sample code from their document

  ScreenshotController screenshotController = ScreenshotController();

  function createScreenshot(){
     screenshotController
       .captureFromWidget(Container(
         padding: const EdgeInsets.all(30.0),
         decoration: BoxDecoration(
           border: Border.all(color: Colors.blueAccent, width: 5.0),
           color: Colors.redAccent,
         ),
         child: Text("This is an invisible widget"),
       ),
     ).then((capturedImage) {
       // Here you will get image object
     });
  }

Upvotes: 0

Related Questions