Render Flutter Widgets

In some cases, you may not want to rewrite UI code in the native frameworks for your widgets. This works by generating a png file of the Flutter widget and save it to a shared container between your Flutter app and the home screen widget.

Screenshot 2023-06-07 at 12 57 28 PM

Due to a limitation in dart:ui this method does not work when the App is fully in the background. In those cases you should try to build the UI natively.

For example, say you have a chart in your Flutter app configured with CustomPaint:

class LineChart extends StatelessWidget {
  const LineChart({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: LineChartPainter(),
      child: const SizedBox(
        height: 200,
        width: 200,
      ),
    );
  }
}
Screenshot 2023-06-07 at 12 33 44 PM

Flutter

To render a Flutter widget to an image, use the renderFlutterWidget method:

var path = await HomeWidget.renderFlutterWidget(
  const LineChart(),
  key: 'lineChart',
  logicalSize: const Size(400, 400),
);
  • LineChart() is the widget that will be rendered as an image.
  • key is the key in the key/value storage on the device that stores the path of the file for easy retrieval on the native side

iOS

To retrieve the image and display it in a widget, you can use the following SwiftUI code:

  1. In your TimelineEntry struct add a property to retrieve the path:

    struct MyEntry: TimelineEntry {
    
        let lineChartPath: String
    }
    
  2. Get the path from the UserDefaults in getSnapshot:

    func getSnapshot(
        ...
        let lineChartPath = userDefaults?.string(forKey: "lineChart") ?? "No screenshot available"
    
  3. Create a View to display the chart and resize the image based on the displaySize of the widget:

    struct WidgetEntryView : View {
    
       var ChartImage: some View {
            if let uiImage = UIImage(contentsOfFile: entry.lineChartPath) {
                let image = Image(uiImage: uiImage)
                    .resizable()
                    .frame(width: entry.displaySize.height*0.5, height: entry.displaySize.height*0.5, alignment: .center)
                return AnyView(image)
            }
            print("The image file could not be loaded")
            return AnyView(EmptyView())
        }
    
    }
    
  4. Display the chart in the body of the widget's View:

    VStack {
            Text(entry.title)
            Text(entry.description)
            ChartImage
        }
    

Android

On Android use the following code in your Glance Widget to display the Screenshot of the Flutter Widget

// Access data
val data = currentState.preferences

// Get Path
val imagePath = data.getString("lineChart", null)

// Add Image to Compose Tree
imagePath?.let {
   val bitmap = BitmapFactory.decodeFile(it)
   Image(androidx.glance.ImageProvider(bitmap), null)
}