Everything in this doc and linked from this doc is experimental. These details WILL change. Do not use these instructions or APIs in production code because we will break you.

Re-use a FlutterEngine across screens

A FlutterEngine is responsible for executing Dart code, rendering a Flutter UI to a FlutterView, and connecting plugins to the core Flutter system. Each instance of FlutterEngine comes with a non-trivial “warm-up” time, which sets up a group of dedicated threads and other resources.

Due to the warm-up time of a FlutterEngine instance, developers may choose to cache one or more instances of FlutterEngines and re-use those instances across different Activitys and/or Fragments in their Android app. This document explains how to cache and re-use FlutterEngines, as well as handle nuances when re-using those FlutterEngines.

Ensure Flutter is initialized

Flutter must be initialized before instantiating a single FlutterEngine instance. To ensure that Flutter is initialized, invoke the following methods before instantiating a FlutterEngine.

FlutterMain.startInitialization(...);
FlutterMain.ensureInitializationComplete(...);

Initializing FlutterEngines

A FlutterEngine must go through 2 steps to be fully initialized. First, an instance is instantiated. Second, the FlutterEngine is instructed to execute a Dart entrypoint, e.g., main().

// Instantiate a FlutterEngine.
FlutterEngine engine = new FlutterEngine(appContext);

// Define a DartEntrypoint
DartExecutor.DartEntrypoint entrypoint = new DartExecutor.createDefault();

// Execute the DartEntrypoint within the FlutterEngine.
engine.getDartExecutor().executeDartEntrypoint(entrypoint);

To cache one or more FlutterEngines, store the initialized FlutterEngines in a central place that you can access from your desired Activitys and Fragments. You could choose to store these FlutterEngines in your Application subclass, or you could store them in a statically accessible location of your choice. This choice is up to you, and should consider your specific application architecture and development practices.

Dart entrypoint restrictions

A FlutterEngine can only execute a Dart entrypoint one time. Once a FlutterEngine has started executing Dart code, it will continue to execute that Dart code until the FlutterEngine is disposed. To re-use a FlutterEngine that needs to display different experiences at different times you will need to find an approach that accomplishes your goals without restarting Dart execution. Below are a couple options.

Using a Navigator with routes

The Navigator widget in Flutter already has the ability to switch experiences at runtime. We call these different experiences routes, and your app is probably already using them. Typically, routes are controlled with Dart code, but the Android embedding also has a navigation system channel that allows you to pushRoute(...) and popRoute(...). The NavigationChannel can be retrieved from a FlutterEngine instance.

myFlutterEngine.getNavigationChannel().pushRoute("myPage");

You'll need to ensure that pushing a route at the desired time has the desired effect in your app. This approach of pushing routes may not work for all apps.

Using method channels and a custom widget

Custom method channels offer a mechanism to implement your own solution for jumping between experiences. Method channels allow you to send any message you would like to your Flutter app, and then take whatever action you'd like.

Start by setting up a method channel with the navigation messages that you're interested in sending.

Once your method channel is setup, respond to your messages on the Flutter/Dart side by switching out your top-level widget, or by switching out any other widget in your hierarchy that makes sense for your use-case.

TODO(mattcarroll): add a code example here.

Re-using FlutterEngines across screens

Re-using the same FlutterEngine across screens that are significantly separated should work as described above. However, developers must consider the possibility that one Flutter experience might show through to another. For example, consider an Activity that displays flutterEngineA, which then launches a native Android dialog, and that dialog is a Fragment that also displays flutterEngineA. This situation won't work because both the dialog and the underlying Activity are simultaneously visible and a single FlutterEngine can only render a single UI at a given moment.

When two Flutter experiences are shown back to back, you must carefully consider the possibility that Flutter might be instructed to show two different UIs at the same time. If such a situation occurs, you will need to switch your strategy to cache two FlutterEngines instead of one, or you'll need to allow a new FlutterEngine to be instantiated for the second Flutter experience.