Integrating Flutter into an Existing Native App: Challenges, Solutions, and the Use of Flutter Boost and Fish‑Redux
This article details the process of embedding Flutter into a mature native Android/iOS application, covering architectural background, thread models, the drawbacks of the default multi‑engine approach, the adoption of Flutter Boost, code‑level integration steps, network and resource sharing techniques, and the subsequent migration to the Fish‑Redux framework for better modularity and maintainability.
The article introduces the motivation for mixing Flutter with an existing native apartment‑PMS app, explaining that a full rewrite would be costly and that hybrid development allows reuse of native resources while gaining Flutter’s cross‑platform UI benefits.
It then describes the Flutter engine architecture—Framework, Engine, and Embedder—and the thread model involving Platform, UI, GPU, and IO task runners, emphasizing the need for stable thread configuration.
The official multi‑engine mixing scheme is presented, followed by a discussion of its disadvantages such as linear memory growth, redundant resources, complex page communication, and plugin registration issues, leading the team to reject this approach.
Flutter Boost’s shared‑engine solution is introduced, with diagrams (omitted) showing how all Flutter pages share a single FlutterView, reducing memory usage and simplifying resource management.
Implementation steps:
Dart side: Add Flutter Boost to pubspec.yaml and run flutter packages get .
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '0.0.410'Android native side: Include the Flutter project in settings.gradle and add dependencies in build.gradle .
setBinding(new Binding([gradle: this, mainModuleName: 'ApartmentClient']))
evaluate(new File(settingsDir.parentFile, 'flutter_apartment/.android/include_flutter.groovy')) implementation project(':flutter')
implementation project(':flutter_boost')Define a custom URL scheme for page navigation, e.g., wbapartment://jump/house/flutter?params={"container_name":"personalCenter","show_guide":true} , and handle it in native code:
private void initFlutterBoost() {
FlutterBoostPlugin.init(new IPlatform() {
@Override
public boolean startActivity(Context context, String url, int requestCode) {
return PageTransferManager.jump(context, url);
}
});
}Network header sharing is achieved via a MethodChannel on the native side and a corresponding call on the Dart side to retrieve headers for Dio requests.
// Native side
new MethodChannel(getBoostFlutterView(), METHOD_CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
if (call.method.equals("getHeader")) {
Map
headerMap = CommonHeaderUtils.getInstance(...).generateParamMap(...);
result.success(JsonUtils.hashMapToJson(headerMap));
} else {
result.notImplemented();
}
}
}); // Dart side
Map
headers;
final String headerString = await platform.invokeMethod('getHeader');
headers = jsonDecode(headerString);
Response response = await dio.get(dataUrl, options: Options(headers: headers));Image sharing between native and Dart is performed with a BasicMessageChannel that transfers drawable bytes.
// Native side
BasicMessageChannel
messageChannel = new BasicMessageChannel<>(getFlutterView(), "getPic", StandardMessageCodec.INSTANCE);
messageChannel.setMessageHandler((o, reply) -> reply.reply(drawableToByte(getResources().getDrawable(getResId(o.toString()))))); // Dart side
const _messageChannel = BasicMessageChannel
('getPic', StandardMessageCodec());
Future
getNativeImage(String name) async {
return await _messageChannel.send(name);
}Because the project grew in complexity, the team adopted Fish‑Redux, a Redux‑based Flutter framework, to modularize pages into Page → Adapter → Component layers, separating state, effects, and UI. Sample code shows a page definition, adapter registration, and component implementation.
class PersonalCenterPage extends Page
> {
PersonalCenterPage() : super(
initState: initState,
effect: buildEffect(),
view: buildView,
dependencies: Dependencies
(
adapter: NoneConn
() + PersonalCenterListAdapter()
)
);
} class PersonalCenterListAdapter extends DynamicFlowAdapter
{
PersonalCenterListAdapter() : super(
pool: {
NORMAL_ITEM: NormalItemComponent(),
USER_INFO_ITEM: UserItemComponent(),
LOGOUT_ITEM: LogoutItemComponent(),
TODO_ITEM: TodoItemComponent(),
CONTACT_ITEM: ContactItemComponent(),
},
connector: _HouseListConnector(),
reducer: buildReducer()
);
}The article concludes that using Flutter Boost solved the engine‑sharing problem, while Fish‑Redux provided a clean, component‑based architecture that improved code maintainability, facilitated reuse of native resources, and streamlined communication between native and Dart layers.
58 Tech
Official tech channel of 58, a platform for tech innovation, sharing, and communication.
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.