Using Pigeon to Manage Flutter Plugin Interfaces Across Platforms
Pigeon lets Flutter developers define a single Dart API that automatically generates matching, type‑safe interface code for Android and iOS, eliminating manual method‑channel boilerplate, preventing naming mismatches, and keeping native and Dart implementations synchronized across platforms.
In cross‑platform development, plugins often suffer from naming and parameter inconsistencies when multiple native sides are involved. This article explains how Flutter’s Pigeon tool can generate unified interface code, reducing manual method‑channel handling and keeping Dart and native implementations in sync.
Why Pigeon is needed – Maintaining a separate protocol file for Dart, Android, and iOS can easily become out‑of‑date, leading to mismatched APIs. Pigeon generates the boiler‑plate for both sides from a single Dart definition, ensuring type safety and consistent naming.
Pigeon’s role – By providing a Dart entry point, Pigeon creates template code for Android (Java/Kotlin) and iOS (Objective‑C/Swift). Developers only need to implement the generated abstract methods; the method‑channel plumbing is handled automatically.
Getting started
1. Create a Flutter plugin package (skip if you already have one):
flutter create --org com.example --template plugin flutterPigeonDemo2. Add Pigeon as a dev dependency in pubspec.yaml :
dev_dependencies:
flutter_test:
sdk: flutter
pigeon:
version: 0.1.73. Create a pigeons directory and add a Dart file (e.g., pigeons/pigeonDemoMessage.dart ) that defines request/response classes and an @HostApi interface:
import 'package:pigeon/pigeon.dart';
class DemoReply {
String result;
}
class DemoRequest {
String methodName;
}
@HostApi()
abstract class PigeonDemoApi {
DemoReply getMessage(DemoRequest params);
}
void configurePigeon(PigeonOptions opts) {
opts.dartOut = './lib/PigeonDemoMessage.dart';
opts.objcHeaderOut = 'ios/Classes/PigeonDemoMessage.h';
opts.objcSourceOut = 'ios/Classes/PigeonDemoMessage.m';
opts.objcOptions.prefix = 'FLT';
opts.javaOut = 'android/src/main/kotlin/com/example/flutter_pigeon_demo/PigeonDemoMessage.java';
opts.javaOptions.package = 'package com.example.flutter_pigeon_demo';
}4. Run the generator:
flutter pub run pigeon --input pigeons/pigeonDemoMessage.dartThe command produces Dart, Java/Kotlin, and Objective‑C files placed in the paths defined above.
Android integration
Import the generated Java file ( PigeonDemoMessage.java ) and let your plugin class implement PigeonDemoApi . Register the API in onAttachedToEngine :
// ... other imports
import com.example.flutter_pigeon_demo.PigeonDemoMessage.*;
public class FlutterPigeonDemoPlugin implements FlutterPlugin, MethodCallHandler, PigeonDemoApi {
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
MethodChannel channel = new MethodChannel(binding.getBinaryMessenger(), "flutter_pigeon_demo");
channel.setMethodCallHandler(this);
// Register the generated API
PigeonDemoApi.setup(binding.getBinaryMessenger(), this);
}
@Override
public DemoReply getMessage(DemoRequest arg) {
DemoReply reply = new DemoReply();
reply.result = "pigeon demo result";
return reply;
}
}iOS integration
Import the generated header ( PigeonDemoMessage.h ) and register the API in registerWithRegistrar :
#import "FlutterPigeonDemoPlugin.h"
#import "PigeonDemoMessage.h"
@implementation FlutterPigeonDemoPlugin
+ (void)registerWithRegistrar:(NSObject
*)registrar {
FlutterPigeonDemoPlugin* instance = [[FlutterPigeonDemoPlugin alloc] init];
// Register the generated API
FLTPigeonDemoApiSetup(registrar.messenger, instance);
}
- (FLTDemoReply*)getMessage:(FLTDemoRequest*)input error:(FlutterError**)error {
FLTDemoReply* reply = [[FLTDemoReply alloc] init];
reply.result = @"pigeon demo result";
return reply;
}
@endDart side usage
After generation, the Dart file ( PigeonDemoMessage.dart ) contains the data classes and the API stub. Call the API like any normal Dart method:
import 'dart:async';
import 'package:flutter/services.dart';
import 'PigeonDemoMessage.dart';
class FlutterPigeonDemo {
static const MethodChannel _channel = MethodChannel('flutter_pigeon_demo');
static Future
get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
static Future
testPigeon() async {
DemoRequest request = DemoRequest()..methodName = 'requestMessage';
PigeonDemoApi api = PigeonDemoApi();
DemoReply reply = await api.getMessage(request);
return reply;
}
}Effect of using Pigeon
Before Pigeon, plugin code often mixes many MethodChannel calls with string‑based method names, leading to spaghetti code. After integration, each method has a strongly‑typed Dart signature, and the native side only implements the generated abstract methods. This improves readability, type safety, and reduces boilerplate.
Conclusion
Pigeon enables a single source of truth for Flutter plugin interfaces, automatically generating synchronized code for Dart, Android, and iOS. By maintaining only the Dart protocol file, developers can avoid mismatched APIs, reduce repetitive code, and focus on business logic.
Tencent Music Tech Team
Public account of Tencent Music's development team, focusing on technology 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.