Mobile Development 9 min read

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.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
Mastering Flutter 2 Multi‑Engine Integration: From Setup to Performance Comparison

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

FlutterEngineGroup

in the

Application
onCreate

method 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

FlutterEngine

from

FlutterEngineCache

or creates a new one using

DartExecutor.DartEntrypoint

and 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,

FlutterEngineGroup

allows 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).

Hybrid solution comparison
Hybrid solution comparison

6. Lightweight multi‑engine vs single‑engine advantages and disadvantages (second image).

Engine pros and cons
Engine pros and cons

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.

flutteriOSIntegrationAndroidMulti-EngineAdd-to-App
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.