Flutter Platform Integration
Goal
Integrates Flutter applications with platform-specific code and native features across Android, iOS, and Web environments. Determines the optimal interoperability strategy (FFI, Platform Channels, Platform Views, or JS Interop) and implements the necessary Dart and native code bindings while adhering to thread safety, WebAssembly (Wasm) compatibility, and modern build hook standards.
Instructions
1. Determine Integration Strategy (Decision Logic)
Evaluate the user's requirements using the following decision tree to select the correct integration path:
- Scenario A: Calling native C/C++ code.
- Action: Use
dart:ffi with the package_ffi template and build hooks.
- Exception: If accessing the Flutter Plugin API or requiring static linking on iOS, use the legacy
plugin_ffi template.
- Scenario B: Calling OS-specific APIs (Java/Kotlin for Android, Swift/Obj-C for iOS).
- Action: Use Platform Channels (
MethodChannel) or the pigeon package for type-safe code generation.
- Scenario C: Embedding native UI components into the Flutter widget tree.
- Action: Use Platform Views (
AndroidView / AndroidViewSurface for Android, UiKitView for iOS).
- Scenario D: Web integration and JavaScript APIs.
- Action: Use
package:web and dart:js_interop (Wasm-compatible). Use HtmlElementView for embedding web content.
STOP AND ASK THE USER: "Which platform(s) are you targeting, and what specific native functionality or UI component do you need to integrate?"
2. Implement C/C++ Interop (dart:ffi)
If Scenario A is selected, implement the modern FFI architecture using build hooks (Flutter 3.38+).
- Generate the package:
flutter create --template=package_ffi native_add
- Configure the build hook (
hook/build.dart) to compile the native code:import 'package:hooks/hooks.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
void main(List<String> args) async {
await build(args, (config, output) async {
final builder = CBuilder.library(
name: 'native_add',
assetId: 'native_add/src/native_add.dart',
sources: ['src/native_add.c'],
);
await builder.run(config: config, output: output);
});
}
- Bind the native function in Dart (
lib/src/native_add.dart):import 'dart:ffi';
@Native<Int32 Function(Int32, Int32)>()
external int sum(int a, int b);
3. Implement Platform Channels (MethodChannel)
If Scenario B is selected, implement asynchronous message passing.
- Dart Client Implementation:
import 'package:flutter/services.dart';
class NativeApi {
static const platform = MethodChannel('com.example.app/channel');
Future<String> getNativeData() async {
try {
final String result = await platform.invokeMethod('getData');
return result;
} on PlatformException catch (e) {
return "Error: '${e.message}'.";
}
}
}
- Android Host Implementation (Kotlin):
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "com.example.app/channel"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "getData") {
result.success("Data from Android")
} else {
result.notImplemented()
}
}
}
}
- iOS Host Implementation (Swift):
import Flutter
import UIKit
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "com.example.app/channel", binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if call.method == "getData" {
result("Data from iOS")
} else {
result(FlutterMethodNotImplemented)
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
4. Implement Platform Views
If Scenario C is selected, embed native views.
- Dart Implementation (iOS Example):
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Widget buildNativeView() {
const String viewType = '<platform-view-type>';
final Map<String, dynamic> creationParams = <String, dynamic>{};
return UiKitView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
- iOS Factory Implementation (Swift):
import Flutter
import UIKit
class FLNativeViewFactory: NSObject, FlutterPlatformViewFactory {
private var messenger: FlutterBinaryMessenger
init(messenger: FlutterBinaryMessenger) {
self.messenger = messenger
super.init()
}
func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {
return FLNativeView(frame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)
}
public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
return FlutterStandardMessageCodec.sharedInstance()
}
}
class FLNativeView: NSObject, FlutterPlatformView {
private var _view: UIView
init(frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {
_view = UIView()
super.init()
_view.backgroundColor = UIColor.blue
}
func view() -> UIView { return _view }
}
Validate-and-Fix: Ensure the factory is registered in AppDelegate.swift using registrar.register(factory, withId: "<platform-view-type>").
5. Implement Web Integration (Wasm & JS Interop)
If Scenario D is selected, implement Wasm-compatible web integrations.
- JS Interop (Dart):
import 'dart:js_interop';
import 'package:web/web.dart' as web;
@JS('console.log')
external void log(JSAny? value);
void manipulateDOM() {
final div = web.document.createElement('div') as web.HTMLDivElement;
div.text = "Hello from Wasm-compatible Dart!";
web.document.body?.append(div);
log("DOM updated".toJS);
}
- Embedding HTML Elements:
import 'package:flutter/widgets.dart';
import 'package:web/web.dart' as web;
Widget buildVideoElement() {
return HtmlElementView.fromTag('video', onElementCreated: (Object video) {
final videoElement = video as web.HTMLVideoElement;
videoElement.src = 'https://example.com/video.mp4';
videoElement.style.width = '100%';
videoElement.style.height = '100%';
});
}
Constraints
- Thread Safety: Whenever you invoke a channel method on the platform side destined for Flutter, you MUST invoke it on the platform's main/UI thread. Use
Handler(Looper.getMainLooper()).post (Android) or DispatchQueue.main.async (iOS) if jumping from a background thread.
- WebAssembly Compatibility: DO NOT use
dart:html, dart:js, or package:js. You MUST use package:web and dart:js_interop to ensure the app compiles to Wasm.
- Wasm iOS Limitation: Flutter compiled to Wasm currently CANNOT run on the iOS version of any browser due to WebKit limitations. Ensure fallback to JS compilation is maintained.
- FFI Naming: When implementing
build.dart hooks for Apple platforms, dynamic libraries MUST have consistent filenames across all target architectures (e.g., do not use lib_arm64.dylib).
- Platform View Performance: Handling
SurfaceView on Android via Platform Views is problematic and should be avoided when possible. Prefer TextureLayerHybridComposition for better Flutter rendering performance.
1---2name: flutter-native-interop3description: Interoperate with native APIs in a Flutter app on Android, iOS, and the web4---5# Flutter Platform Integration67## Goal8Integrates Flutter applications with platform-specific code and native features across Android, iOS, and Web environments. Determines the optimal interoperability strategy (FFI, Platform Channels, Platform Views, or JS Interop) and implements the necessary Dart and native code bindings while adhering to thread safety, WebAssembly (Wasm) compatibility, and modern build hook standards.910## Instructions1112### 1. Determine Integration Strategy (Decision Logic)13Evaluate the user's requirements using the following decision tree to select the correct integration path:1415* **Scenario A: Calling native C/C++ code.**16 * *Action:* Use `dart:ffi` with the `package_ffi` template and build hooks.17 * *Exception:* If accessing the Flutter Plugin API or requiring static linking on iOS, use the legacy `plugin_ffi` template.18* **Scenario B: Calling OS-specific APIs (Java/Kotlin for Android, Swift/Obj-C for iOS).**19 * *Action:* Use Platform Channels (`MethodChannel`) or the `pigeon` package for type-safe code generation.20* **Scenario C: Embedding native UI components into the Flutter widget tree.**21 * *Action:* Use Platform Views (`AndroidView` / `AndroidViewSurface` for Android, `UiKitView` for iOS).22* **Scenario D: Web integration and JavaScript APIs.**23 * *Action:* Use `package:web` and `dart:js_interop` (Wasm-compatible). Use `HtmlElementView` for embedding web content.2425**STOP AND ASK THE USER:** "Which platform(s) are you targeting, and what specific native functionality or UI component do you need to integrate?"2627### 2. Implement C/C++ Interop (`dart:ffi`)28If Scenario A is selected, implement the modern FFI architecture using build hooks (Flutter 3.38+).29301. Generate the package:31 ```bash32 flutter create --template=package_ffi native_add33 ```342. Configure the build hook (`hook/build.dart`) to compile the native code:35 ```dart36 import 'package:hooks/hooks.dart';37 import 'package:native_toolchain_c/native_toolchain_c.dart';3839 void main(List<String> args) async {40 await build(args, (config, output) async {41 final builder = CBuilder.library(42 name: 'native_add',43 assetId: 'native_add/src/native_add.dart',44 sources: ['src/native_add.c'],45 );46 await builder.run(config: config, output: output);47 });48 }49 ```503. Bind the native function in Dart (`lib/src/native_add.dart`):51 ```dart52 import 'dart:ffi';5354 @Native<Int32 Function(Int32, Int32)>()55 external int sum(int a, int b);56 ```5758### 3. Implement Platform Channels (MethodChannel)59If Scenario B is selected, implement asynchronous message passing.60611. **Dart Client Implementation:**62 ```dart63 import 'package:flutter/services.dart';6465 class NativeApi {66 static const platform = MethodChannel('com.example.app/channel');6768 Future<String> getNativeData() async {69 try {70 final String result = await platform.invokeMethod('getData');71 return result;72 } on PlatformException catch (e) {73 return "Error: '${e.message}'.";74 }75 }76 }77 ```782. **Android Host Implementation (Kotlin):**79 ```kotlin80 import androidx.annotation.NonNull81 import io.flutter.embedding.android.FlutterActivity82 import io.flutter.embedding.engine.FlutterEngine83 import io.flutter.plugin.common.MethodChannel8485 class MainActivity: FlutterActivity() {86 private val CHANNEL = "com.example.app/channel"8788 override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {89 super.configureFlutterEngine(flutterEngine)90 MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->91 if (call.method == "getData") {92 result.success("Data from Android")93 } else {94 result.notImplemented()95 }96 }97 }98 }99 ```1003. **iOS Host Implementation (Swift):**101 ```swift102 import Flutter103 import UIKit104105 @UIApplicationMain106 @objc class AppDelegate: FlutterAppDelegate {107 override func application(108 _ application: UIApplication,109 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?110 ) -> Bool {111 let controller : FlutterViewController = window?.rootViewController as! FlutterViewController112 let channel = FlutterMethodChannel(name: "com.example.app/channel", binaryMessenger: controller.binaryMessenger)113 114 channel.setMethodCallHandler({115 (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in116 if call.method == "getData" {117 result("Data from iOS")118 } else {119 result(FlutterMethodNotImplemented)120 }121 })122123 GeneratedPluginRegistrant.register(with: self)124 return super.application(application, didFinishLaunchingWithOptions: launchOptions)125 }126 }127 ```128129### 4. Implement Platform Views130If Scenario C is selected, embed native views.1311321. **Dart Implementation (iOS Example):**133 ```dart134 import 'package:flutter/material.dart';135 import 'package:flutter/services.dart';136137 Widget buildNativeView() {138 const String viewType = '<platform-view-type>';139 final Map<String, dynamic> creationParams = <String, dynamic>{};140141 return UiKitView(142 viewType: viewType,143 layoutDirection: TextDirection.ltr,144 creationParams: creationParams,145 creationParamsCodec: const StandardMessageCodec(),146 );147 }148 ```1492. **iOS Factory Implementation (Swift):**150 ```swift151 import Flutter152 import UIKit153154 class FLNativeViewFactory: NSObject, FlutterPlatformViewFactory {155 private var messenger: FlutterBinaryMessenger156157 init(messenger: FlutterBinaryMessenger) {158 self.messenger = messenger159 super.init()160 }161162 func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView {163 return FLNativeView(frame: frame, viewIdentifier: viewId, arguments: args, binaryMessenger: messenger)164 }165166 public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {167 return FlutterStandardMessageCodec.sharedInstance()168 }169 }170171 class FLNativeView: NSObject, FlutterPlatformView {172 private var _view: UIView173174 init(frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {175 _view = UIView()176 super.init()177 _view.backgroundColor = UIColor.blue178 }179180 func view() -> UIView { return _view }181 }182 ```183 *Validate-and-Fix:* Ensure the factory is registered in `AppDelegate.swift` using `registrar.register(factory, withId: "<platform-view-type>")`.184185### 5. Implement Web Integration (Wasm & JS Interop)186If Scenario D is selected, implement Wasm-compatible web integrations.1871881. **JS Interop (Dart):**189 ```dart190 import 'dart:js_interop';191 import 'package:web/web.dart' as web;192193 @JS('console.log')194 external void log(JSAny? value);195196 void manipulateDOM() {197 final div = web.document.createElement('div') as web.HTMLDivElement;198 div.text = "Hello from Wasm-compatible Dart!";199 web.document.body?.append(div);200 log("DOM updated".toJS);201 }202 ```2032. **Embedding HTML Elements:**204 ```dart205 import 'package:flutter/widgets.dart';206 import 'package:web/web.dart' as web;207208 Widget buildVideoElement() {209 return HtmlElementView.fromTag('video', onElementCreated: (Object video) {210 final videoElement = video as web.HTMLVideoElement;211 videoElement.src = 'https://example.com/video.mp4';212 videoElement.style.width = '100%';213 videoElement.style.height = '100%';214 });215 }216 ```217218## Constraints219* **Thread Safety:** Whenever you invoke a channel method on the platform side destined for Flutter, you MUST invoke it on the platform's main/UI thread. Use `Handler(Looper.getMainLooper()).post` (Android) or `DispatchQueue.main.async` (iOS) if jumping from a background thread.220* **WebAssembly Compatibility:** DO NOT use `dart:html`, `dart:js`, or `package:js`. You MUST use `package:web` and `dart:js_interop` to ensure the app compiles to Wasm.221* **Wasm iOS Limitation:** Flutter compiled to Wasm currently CANNOT run on the iOS version of any browser due to WebKit limitations. Ensure fallback to JS compilation is maintained.222* **FFI Naming:** When implementing `build.dart` hooks for Apple platforms, dynamic libraries MUST have consistent filenames across all target architectures (e.g., do not use `lib_arm64.dylib`).223* **Platform View Performance:** Handling `SurfaceView` on Android via Platform Views is problematic and should be avoided when possible. Prefer `TextureLayerHybridComposition` for better Flutter rendering performance.