bridge_native 1.0.1 copy "bridge_native: ^1.0.1" to clipboard
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 #

pub package platform

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 and Native widget)
  • 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 Req and Res classes using Pigeon
  • ReqApi Interface: Type-safe API for native implementations
  • Flutter Utilities:
    • sendToNative(): Send requests from Flutter to native
    • Native widget: 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 identifier
  • data (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 content
  • doNativeDataBack (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() and Native widget
  • Android: Implements Pigeon.ReqApi in EkycPlugin.kt
  • iOS: Implements ReqApi protocol in EkycPlugin.swift
  • Both platforms setup EventChannel named "bridgeStream"

Important Notes #

EventChannel Setup #

bridge_native does NOT setup EventChannel for you. You must:

  1. Create EventChannel in your plugin's native code
  2. Use the name "bridgeStream" (required by Native widget)
  3. Send events as Map with key and data fields

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 ReqApi and returns Res

Event Streaming (Asynchronous):

  • Native sends to EventChannel
  • Flutter listens via Native widget

Troubleshooting #

"No implementation found for method request" #

Cause: ReqApi not properly setup in native code.

Solution:

  • Android: Call Pigeon.ReqApi.setup(messenger, yourImpl) in onAttachedToEngine
  • iOS: Call ReqApiSetup(messenger, yourImpl) in register(with:)

Events not received in Flutter #

Cause: EventChannel not setup or wrong channel name.

Solution:

  • Verify channel name is exactly "bridgeStream"
  • Ensure Native widget 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 #

  1. Autogenerated Pigeon Code:

    • Req and Res classes (Android + iOS)
    • ReqApi interface/protocol
    • Codec for serialization
  2. Flutter Utilities:

    • sendToNative() function (wraps Pigeon API)
    • Native widget (wraps EventChannel listener)
    • Convenience functions for common patterns

What You Must Implement #

  1. In Your Native Plugin:

    • Implement ReqApi interface/protocol
    • Setup EventChannel if you need native → Flutter events
    • Handle requests in request() method
  2. In Your Flutter Plugin:

    • Use sendToNative() to send requests
    • Use Native widget to receive events
    • Process responses and events

Development #

Regenerating Pigeon Code #

If you need to modify the API:

  1. Edit pigeons/br_api.dart
  2. 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 #

  1. Define Key Constants:

    class BridgeKeys {
      static const getDeviceInfo = 'getDeviceInfo';
      static const openCamera = 'openCamera';
    }
    
  2. Type-Safe Data Handling:

    if (data is Map<String, dynamic>) {
      final value = data['key'] as String?;
    }
    
  3. Error Handling:

    try {
      final res = await sendToNative(key: 'action', data: {});
    } catch (e) {
      print('Bridge error: $e');
    }
    
  4. 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)

0
likes
120
points
93
downloads

Publisher

unverified uploader

Weekly Downloads

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 Flutter-to-native integration.

Repository (GitHub)
View/report issues

Topics

#pigeon #native-bridge #platform-channel #flutter-native #bidirectional-communication

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on bridge_native

Packages that implement bridge_native