Mastering Flutter 2 Multi‑Engine Integration: From Setup to Performance Comparison
This article explains Flutter 2's new multi‑engine Add‑to‑App approach, walks through creating an Android project with a Flutter module, shows how to manage engines with FlutterEngineGroup, and compares multi‑engine and single‑engine hybrid solutions in terms of memory and performance.
Preface
Flutter 2 brings stable support for null‑safety, desktop, and web, but the most talked‑about feature is Add‑to‑App, which lets developers embed Flutter into existing iOS and Android apps while keeping native code. Prior third‑party solutions such as flutter_boost and flutter_thrio required frequent version‑specific adaptations and could become bottlenecks if not maintained.
Flutter2 Multi‑Engine Hybrid Development Basic Usage
1. Create a native Android project (details omitted).
2. Add a Flutter module via
File → New → New Module… → Flutter Module, specify a module name, fill in the required information, and wait for Gradle sync.
3. Integrate the Flutter module into the Android project.
a) Create a
FlutterEngineGroupin the
Application onCreatemethod to manage multiple engines that can share resources.
<code>package com.zalex.hybird;
import android.app.Application;
import io.flutter.embedding.engine.FlutterEngineGroup;
public class WYApplication extends Application {
public FlutterEngineGroup engineGroup;
@Override
public void onCreate() {
super.onCreate();
// Create FlutterEngineGroup object
engineGroup = new FlutterEngineGroup(this);
}
}
</code>b) Implement a cache manager (
WYFlutterEngineManager) that retrieves a
FlutterEnginefrom
FlutterEngineCacheor creates a new one using
DartExecutor.DartEntrypointand stores it in the cache.
<code>public class WYFlutterEngineManager {
public static FlutterEngine flutterEngine(Context context, String engineId, String entryPoint) {
// 1. Get FlutterEngine from cache
FlutterEngine engine = FlutterEngineCache.getInstance().get(engineId);
if (engine == null) {
// 1. Create new FlutterEngine with the specified entry point
WYApplication app = (WYApplication) context.getApplicationContext();
DartExecutor.DartEntrypoint dartEntrypoint = new DartExecutor.DartEntrypoint(
FlutterInjector.instance().flutterLoader().findAppBundlePath(), entryPoint);
engine = app.engineGroup.createAndRunEngine(context, dartEntrypoint);
// 2. Store in cache
FlutterEngineCache.getInstance().put(engineId, engine);
}
return engine;
}
}
</code>c) Bind the engine to an
Activity:
<code>public class WYFlutterActivity extends FlutterActivity implements EngineBindingsDelegate {
private WYFlutterBindings flutterBindings;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
flutterBindings = new WYFlutterBindings(this, SingleFlutterActivity.class.getSimpleName(), "main", this);
flutterBindings.attach();
}
@Override
protected void onDestroy() {
super.onDestroy();
flutterBindings.detach();
}
@Override
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
return flutterBindings.getEngine();
}
@Override
public void onNext() {
Intent flutterIntent = new Intent(this, MainActivity.class);
startActivity(flutterIntent);
}
}
</code>d) Bind the engine to a
Fragment(custom engine and fragment IDs, container setup, and cache registration).
<code>int engineId = engineId; // custom engine Id
int fragmentId = 1233444; // custom Fragment Id
FrameLayout flutterContainer = new FrameLayout(this);
root.addView(flutterContainer);
flutterContainer.setId(containerId);
flutterContainer.setLayoutParams(new LinearLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT,
1.0f));
WYFlutterBindings flutterBindings = new WYFlutterBindings(this, "WYTopFragment", "fragmentMain", this);
FlutterEngine engine = bottomBindings.getEngine();
FlutterEngineCache.getInstance().put(engineId + "", engine);
Fragment flutterFragment = FlutterFragment.withCachedEngine(engineId + "").build();
fragmentManager.beginTransaction().add(containerId, flutterFragment).commit();
</code>e) Declare additional entry points with
@pragma('vm:entry-point')annotation.
<code>void main() => runApp(MyApp(Colors.blue));
@pragma('vm:entry-point')
void fragmentMain() => runApp(CustomApp(Colors.green));
</code>Flutter2 Multi‑Engine vs Single‑Engine Hybrid Development Comparison
1. Multiple engines can be created independently, but each engine consumes roughly 40 MB; creating ten engines can use over 235 MB, which is unacceptable for most apps.
2. Prior to Flutter 2, developers reused a single engine (e.g., FlutterBoost, Thrio) by detaching/attaching the engine during page switches, reducing memory usage but requiring invasive modifications to the official Flutter framework.
3. Starting with Flutter 2,
FlutterEngineGroupallows new engines that share GPU context, glyph cache, and isolate snapshots, resulting in only about 180 KB of additional memory per engine and faster startup.
4. Engine creation differences:
Flutter 1 (Android):
<code>val engine = FlutterEngine(this)
engine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
FlutterEngineCache.getInstance().put(1, engine)
val intent = FlutterActivity.withCachedEngine(1).build(this)
</code>Flutter 1 (iOS):
<code>let engine = FlutterEngine()
engine.run()
let vc = FlutterViewController(engine: engine, nibName: nil, bundle: nil)
</code>Flutter 2 (Android):
<code>val engineGroup = FlutterEngineGroup(context)
val engine1 = engineGroup.createAndRunDefaultEngine(context)
val engine2 = engineGroup.createAndRunEngine(context, DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(), "anotherEntrypoint"))
</code>Flutter 2 (iOS):
<code>let engineGroup = FlutterEngineGroup(name: "example", project: nil)
let engine1 = engineGroup.makeEngine(withEntrypoint: nil, libraryURI: nil)
let engine2 = engineGroup.makeEngine(withEntrypoint: "anotherEntrypoint", libraryURI: nil)
</code>5. The following tables compare the hybrid solutions (images retained from the original article).
6. Lightweight multi‑engine vs single‑engine advantages and disadvantages (second image).
Afterword
This article demonstrated how to use Flutter 2's multi‑engine capabilities, compared multi‑engine and single‑engine hybrid approaches, and highlighted their trade‑offs. The next section will explore the underlying implementation principles of Flutter 2's multi‑engine architecture.
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.