bridge_native 1.0.1
bridge_native: ^1.0.1 copied to clipboard
A Flutter plugin that provides type-safe bidirectional communication between Flutter and native platforms (Android/iOS) using Pigeon. Supports request-response pattern and event streaming for seamless [...]
bridge_native #
A Flutter plugin that provides type-safe communication between Flutter and native platforms (Android/iOS) using Pigeon. This plugin contains autogenerated Pigeon code and Flutter-side utilities for seamless Flutter-to-native integration.
What is bridge_native? #
bridge_native is a foundation plugin that provides:
- Autogenerated Pigeon code for type-safe communication
- Request-Response models (Req/Res classes)
- Flutter utilities (
sendToNative()function andNativewidget) - Platform interfaces that your main plugin implements
This plugin is typically used as a dependency by other plugins that need Flutter-to-native communication.
Features #
- Type-Safe Models: Autogenerated
ReqandResclasses using Pigeon - ReqApi Interface: Type-safe API for native implementations
- Flutter Utilities:
sendToNative(): Send requests from Flutter to nativeNativewidget: Wrapper to receive native events
- Cross-Platform: Works with both Android (Java) and iOS (Objective-C)
- Zero Manual Serialization: Pigeon handles all serialization automatically
Installation #
Add this to your plugin's pubspec.yaml:
dependencies:
bridge_native: ^1.0.0
Then run:
flutter pub get
Architecture #
┌─────────────────────────────────────┐
│ Your Flutter Plugin │
│ │
│ Uses: sendToNative() │
│ Native widget │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ bridge_native │
│ │
│ Provides: │
│ • Req/Res models │
│ • ReqApi interface │
│ • Pigeon autogenerated code │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Your Native Implementation │
│ │
│ Implements: ReqApi │
│ Sets up: EventChannel (optional) │
└─────────────────────────────────────┘
Usage #
1. Flutter Side - Use Provided Utilities #
Send Request to Native
import 'package:bridge_native/bridge/native.dart';
// Send request to native and receive response
Future<void> getDeviceInfo() async {
final response = await sendToNative(
key: 'getDeviceInfo',
data: {'includeModel': true},
);
print('Response: ${response.data}');
}
Listen to Native Events
import 'package:bridge_native/bridge/native.dart';
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Native(
child: YourContent(),
doNativeDataBack: (data) {
// Handle data from native
print('Native event: $data');
if (data is Map) {
final key = data['key']?.toString() ?? '';
final payload = data['data'];
// Process event...
}
},
);
}
}
2. Native Side - Implement ReqApi #
The autogenerated code provides interfaces/protocols that you must implement in your native plugin.
Android (Java)
package com.example.yourplugin;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel;
import vn.son.bridge.Pigeon;
import java.util.HashMap;
import java.util.Map;
public class YourPlugin implements FlutterPlugin {
private EventChannel eventChannel;
private EventChannel.EventSink eventSink;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
// 1. Setup Pigeon API
Pigeon.ReqApi.setup(binding.getBinaryMessenger(), new AndroidAPI());
// 2. Setup EventChannel for sending events to Flutter
eventChannel = new EventChannel(binding.getBinaryMessenger(), "bridgeStream");
eventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
eventSink = events;
}
@Override
public void onCancel(Object arguments) {
eventSink = null;
}
});
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
eventChannel.setStreamHandler(null);
}
// Implement ReqApi interface
class AndroidAPI implements Pigeon.ReqApi {
@NonNull
@Override
public Pigeon.Res request(@NonNull Pigeon.Req request) {
// Handle request from Flutter
String key = request.getKey();
Map<Object, Object> data = request.getData();
// Process based on key
switch (key) {
case "getDeviceInfo":
return handleDeviceInfo(data);
case "openCamera":
return handleCamera(data);
default:
return createResponse("unknown", null);
}
}
private Pigeon.Res handleDeviceInfo(Map<Object, Object> data) {
Map<String, Object> result = new HashMap<>();
result.put("model", android.os.Build.MODEL);
result.put("version", android.os.Build.VERSION.RELEASE);
return createResponse("deviceInfo", result);
}
private Pigeon.Res createResponse(String key, Map<String, Object> data) {
Pigeon.Res response = new Pigeon.Res();
response.setKey(key);
response.setData((Map) data);
return response;
}
}
// Send event to Flutter
public void sendEventToFlutter(String key, Map<String, Object> data) {
if (eventSink != null) {
Map<String, Object> event = new HashMap<>();
event.put("key", key);
event.put("data", data);
eventSink.success(event);
}
}
}
iOS (Swift)
import Flutter
import bridge_native
public class YourPlugin: NSObject, FlutterPlugin, ReqApi {
private var eventChannel: FlutterEventChannel?
private var eventSink: FlutterEventSink?
public static func register(with registrar: FlutterPluginRegistrar) {
let instance = YourPlugin()
// 1. Setup EventChannel for sending events to Flutter
let channel = FlutterEventChannel(
name: "bridgeStream",
binaryMessenger: registrar.messenger()
)
channel.setStreamHandler(instance)
instance.eventChannel = channel
// 2. Setup Pigeon API
ReqApiSetup(registrar.messenger(), instance)
}
// Implement ReqApi protocol
public func requestRequest(
_ request: Req,
error: AutoreleasingUnsafeMutablePointer<FlutterError?>
) -> Res? {
let res = Res()
switch request.key {
case "getDeviceInfo":
res.key = "deviceInfo"
res.data = [
"model": UIDevice.current.model,
"version": UIDevice.current.systemVersion
]
case "openCamera":
// Handle camera
res.key = "cameraOpened"
res.data = ["success": true]
default:
res.key = "unknown"
res.data = nil
}
return res
}
// Send event to Flutter
func sendEventToFlutter(key: String, data: [String: Any]) {
let event: [String: Any] = [
"key": key,
"data": data
]
eventSink?(event)
}
}
// MARK: - FlutterStreamHandler
extension YourPlugin: FlutterStreamHandler {
public func onListen(
withArguments arguments: Any?,
eventSink events: @escaping FlutterEventSink
) -> FlutterError? {
self.eventSink = events
return nil
}
public func onCancel(withArguments arguments: Any?) -> FlutterError? {
self.eventSink = nil
return nil
}
}
API Reference #
Flutter API #
sendToNative({key, data})
Sends a request to native code and awaits response.
Parameters:
key(dynamic): Request identifierdata(dynamic): Request payload (typically Map)
Returns:
Future<Res>: Response from native
Example:
final res = await sendToNative(
key: 'action',
data: {'param': 'value'},
);
Native Widget
A widget wrapper that receives events from native code via EventChannel.
Properties:
child(Widget, required): Your widget contentdoNativeDataBack(Function(dynamic), required): Callback for native events
Example:
Native(
child: MyContent(),
doNativeDataBack: (data) {
// Handle event
},
)
Native API #
Android - Pigeon.ReqApi Interface
public interface ReqApi {
@NonNull
Res request(@NonNull Req request);
}
Setup:
Pigeon.ReqApi.setup(binaryMessenger, new YourImplementation());
iOS - ReqApi Protocol
@protocol ReqApi
- (nullable Res *)requestRequest:(Req *)request
error:(FlutterError **)error;
@end
Setup:
ReqApiSetup(binaryMessenger, yourImplementation);
Data Models #
Req - Request Model
| Field | Type | Description |
|---|---|---|
| key | String? | Request identifier |
| data | Map? | Request payload |
Res - Response Model
| Field | Type | Description |
|---|---|---|
| key | String? | Response identifier |
| data | Map? | Response payload |
Complete Example #
See the example directory for a complete working example of using bridge_native.
Key implementation points:
- Flutter: Uses
sendToNative()andNativewidget - Android: Implements
Pigeon.ReqApiinEkycPlugin.kt - iOS: Implements
ReqApiprotocol inEkycPlugin.swift - Both platforms setup EventChannel named
"bridgeStream"
Important Notes #
EventChannel Setup #
bridge_native does NOT setup EventChannel for you. You must:
- Create EventChannel in your plugin's native code
- Use the name
"bridgeStream"(required by Native widget) - Send events as Map with
keyanddatafields
Pigeon Versions #
This plugin uses Pigeon 10.1.4 for code generation. The generated code is compatible with:
- Android: Java/Kotlin
- iOS: Objective-C/Swift
Communication Pattern #
Request → Response (Synchronous-like):
- Use
sendToNative()from Flutter - Native implements
ReqApiand returnsRes
Event Streaming (Asynchronous):
- Native sends to EventChannel
- Flutter listens via
Nativewidget
Troubleshooting #
"No implementation found for method request" #
Cause: ReqApi not properly setup in native code.
Solution:
- Android: Call
Pigeon.ReqApi.setup(messenger, yourImpl)inonAttachedToEngine - iOS: Call
ReqApiSetup(messenger, yourImpl)inregister(with:)
Events not received in Flutter #
Cause: EventChannel not setup or wrong channel name.
Solution:
- Verify channel name is exactly
"bridgeStream" - Ensure
Nativewidget is mounted in widget tree - Check that native is calling
eventSink?.success(data)
Type casting errors #
Solution: Always type-check dynamic data:
if (data is Map) {
final key = data['key']?.toString() ?? '';
// Safe to use
}
How bridge_native Works #
What It Provides #
-
Autogenerated Pigeon Code:
ReqandResclasses (Android + iOS)ReqApiinterface/protocol- Codec for serialization
-
Flutter Utilities:
sendToNative()function (wraps Pigeon API)Nativewidget (wraps EventChannel listener)- Convenience functions for common patterns
What You Must Implement #
-
In Your Native Plugin:
- Implement
ReqApiinterface/protocol - Setup EventChannel if you need native → Flutter events
- Handle requests in
request()method
- Implement
-
In Your Flutter Plugin:
- Use
sendToNative()to send requests - Use
Nativewidget to receive events - Process responses and events
- Use
Development #
Regenerating Pigeon Code #
If you need to modify the API:
- Edit
pigeons/br_api.dart - Run Pigeon code generation:
flutter pub run pigeon --input pigeons/br_api.dart
Testing #
Test your implementation:
test('sendToNative returns response', () async {
final res = await sendToNative(key: 'test', data: {});
expect(res, isNotNull);
expect(res.key, isNotNull);
});
Best Practices #
-
Define Key Constants:
class BridgeKeys { static const getDeviceInfo = 'getDeviceInfo'; static const openCamera = 'openCamera'; } -
Type-Safe Data Handling:
if (data is Map<String, dynamic>) { final value = data['key'] as String?; } -
Error Handling:
try { final res = await sendToNative(key: 'action', data: {}); } catch (e) { print('Bridge error: $e'); } -
Document Your Protocol:
- List all supported keys
- Define data structures
- Document response formats
Additional Resources #
License #
This project is licensed under the MIT License. See the LICENSE file for details.
Contributing #
Contributions are welcome! Please submit issues or pull requests.
Repository: https://github.com/sonuos1501/bridge_native Maintainer: SON (Lucas)