flutter_embedding 0.0.1-beta.1
flutter_embedding: ^0.0.1-beta.1 copied to clipboard
This project helps embedding into a native ios or android app.
Flutter Embedding Plugin #
A Flutter plugin that enables seamless embedding of Flutter modules into native iOS and Android applications with bidirectional communication capabilities. This can also be used to embed Flutter modules into React Native applications.
Features #
- Cross-platform Support: Works with both iOS and Android native applications
- Bidirectional Communication: Send data between Flutter and native code in both directions
- Theme Management: Runtime dynamic theme switching (light/dark/system)
- Language Support: Runtime language switching capabilities
- Environment Configuration: Support for multiple environments
- React Native Compatibility: Includes workarounds for React Native layout issues
- Fragment/ViewController Management: Easy integration with native UI components
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
flutter_embedding: ^0.0.1-beta.1
Then run:
fvm flutter pub get
Platform Setup #
Android #
The plugin automatically registers itself with the Flutter engine. No additional setup is required.
iOS #
The plugin automatically registers itself with the Flutter engine. No additional setup is required.
Usage #
Flutter Side #
1. Initialize the Embedding Controller
import 'package:flutter_embedding/flutter_embedding.dart';
void main(List<String> args) {
// Initialize the embedding controller with configuration
final embeddingController = EmbeddingController.fromArgs(args);
runApp(MyApp(embeddingController: embeddingController));
}
2. Listen to Native Events
class MyApp extends StatelessWidget {
final EmbeddingController embeddingController;
const MyApp({Key? key, required this.embeddingController}) : super(key: key);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: embeddingController.themeMode,
builder: (context, themeMode, child) {
return MaterialApp(
themeMode: themeMode,
home: MyHomePage(embeddingController: embeddingController),
);
},
);
}
}
3. Handle Custom Handover Events
// Add handlers for custom events from native side
embeddingController.addHandoverHandler('resetCounter', (args, _) async {
// Handle the reset counter event
print('Counter reset with value: ${args['counter']}');
return true;
});
embeddingController.addHandoverHandler('updateUserData', (args, _) async {
// Handle user data update
final userData = args['userData'] as Map<String, dynamic>;
// Update your app state
return true;
});
4. Send Events to Native Side
// Send data to native side
await embeddingController.invokeHandover('userAction', arguments: {
'action': 'buttonPressed',
'timestamp': DateTime.now().millisecondsSinceEpoch,
});
// Exit back to native app
embeddingController.exit();
5. React Native Compatibility
If you have issues with platform widgets (like WebView, MapView, etc.) in React Native which show without width and height, wrap them with the provided wrapper to ensure the React Native LayoutManager can properly layout them. The widget itself will only do something on Android.
import 'package:flutter_embedding/rn_native_component_wrapper.dart';
Widget build(BuildContext context) {
return RnNativeComponentWrapper(
child: YourFlutterWidget(),
);
}
Native Side #
Android
import be.krispypen.plugins.flutter_embedding.FlutterEmbedding;
import be.krispypen.plugins.flutter_embedding.HandoverResponderInterface;
public class MainActivity extends FragmentActivity implements HandoverResponderInterface {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Start the Flutter engine
FlutterEmbedding.instance().startEngine(
this,
"PROD", // environment
"en", // language
"system", // theme mode
this, // handover responder
(success, error) -> {
if (success) {
// Engine started successfully
showFlutterFragment();
} else {
// Handle error
Log.e("FlutterEmbedding", "Failed to start engine", error);
}
}
);
}
private void showFlutterFragment() {
// Get or create the Flutter fragment
FlutterEmbeddingFlutterFragment fragment = FlutterEmbedding.instance()
.getOrCreateFragment(this, R.id.flutter_container);
}
// Implement HandoverResponderInterface
@Override
public void exit() {
// Handle exit from Flutter
finish();
}
@Override
public void invokeHandover(String method, Map<String, Object> data,
CompletionHandler<Object> completion) {
// Handle custom events from Flutter
switch (method) {
case "userAction":
// Handle user action
completion.onSuccess("Action processed");
break;
default:
completion.onFailure(new Exception("Unknown method: " + method));
}
}
}
iOS
import flutter_embedding
class ViewController: UIViewController, HandoverResponderProtocol {
override func viewDidLoad() {
super.viewDidLoad()
// Start the Flutter engine
FlutterEmbedding.shared.startEngine(
forEnv: "PROD",
forLanguage: "en",
forThemeMode: "system",
with: self
) { success, error in
if success == true {
// Engine started successfully
self.showFlutterView()
} else {
// Handle error
print("Failed to start Flutter engine: \(error?.localizedDescription ?? "Unknown error")")
}
}
}
private func showFlutterView() {
do {
let flutterViewController = try FlutterEmbedding.shared.getViewController()
addChild(flutterViewController)
view.addSubview(flutterViewController.view)
flutterViewController.view.frame = view.bounds
flutterViewController.didMove(toParent: self)
} catch {
print("Failed to get Flutter view controller: \(error)")
}
}
// Implement HandoverResponderProtocol
func exit() {
// Handle exit from Flutter
dismiss(animated: true)
}
func invokeHandover(withName name: String, data: [String : Any?],
completion: @escaping (Any?, FlutterEmbeddingError?) -> Void) {
// Handle custom events from Flutter
switch name {
case "userAction":
// Handle user action
completion("Action processed", nil)
default:
completion(nil, FlutterEmbeddingError.genericError(
code: "UNKNOWN_METHOD",
message: "Unknown method: \(name)"
))
}
}
}
Configuration #
Environment Support #
You can pass the environment name when initializing the engine.
Theme Modes #
You can pass the theme mode when initializing the engine but it will also be dynamic and can be changed from the native side. Supported theme modes:
light- Light themedark- Dark themesystem- Follow system theme
Language Support #
The plugin supports dynamic language switching. Pass the language code (e.g., "en", "nl", "fr") when initializing the engine.
Flutter API Reference #
EmbeddingController #
The main controller class for managing Flutter embedding functionality.
Methods
EmbeddingController.fromArgs(List<String> args)- Create controller from command line argumentsaddHandoverHandler(String method, Handler handler)- Add handler for native eventsinvokeHandover(String method, {Map<String, dynamic> arguments})- Send event to native sideexit()- Exit back to native application
Properties
environment- Current environment (String)themeMode- Current theme mode (ValueNotifierlanguage- Current language (ValueNotifier
RnNativeComponentWrapper #
A widget wrapper for React Native compatibility that handles layout issues.
Troubleshooting #
React Native Layout Issues #
If you're experiencing layout issues in React Native, ensure you wrap your Flutter widgets with RnNativeComponentWrapper. This addresses a known React Native bug with Flutter embedding.
Engine Not Starting #
Make sure you're calling the start engine methods on the main thread and that all required parameters are provided.
Communication Issues #
Verify that both Flutter and native sides are implementing the handover interfaces correctly and that method names match between both sides.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.