Flutter is great—until you need to talk to native code.
If you’ve ever used MethodChannel, you’ve probably faced:
- Runtime crashes
Map<dynamic, dynamic>casting errors- Hard-to-debug contracts between Flutter and native
Pigeon solves this problem by introducing type-safe, generated APIs between Flutter and native platforms.
This article explains Pigeon methods in detail:
- What methods exist
- How they work on native side
- Who calls whom
- Sync vs async
- Real-world patterns
What is Pigeon?
Pigeon is a code generation tool from the Flutter team that creates:
- Dart APIs
- Kotlin / Java APIs (Android)
- Swift / Objective-C APIs (iOS)
All from one shared Dart file.
Pigeon does not render UI. It enables safe, structured communication between Flutter and native code.
The Core Concept: Only Two Types of Pigeon Methods
Pigeon has exactly two method categories:
- HostApi → Flutter calls Native
- FlutterApi → Native calls Flutter
That’s it. No third type.
1️⃣ HostApi Methods (Flutter → Native)
What are HostApi methods?
- Defined in Dart
- Implemented in native
- Called by Flutter
Think of them as commands.
Use cases
- Start payment
- Fetch device info
- Open native screens
- Trigger SDK actions
Dart Definition
@HostApi()
abstract class PaymentHostApi {
void startPayment(String orderId);
void cancelPayment();
}
Generated Native Code (Android – Kotlin)
interface PaymentHostApi {
fun startPayment(orderId: String)
fun cancelPayment()
}
Native Implementation (Your Code)
class PaymentHostApiImpl(
private val activity: Activity
) : PaymentHostApi {
override fun startPayment(orderId: String) {
// Call native SDK (Juspay, Razorpay, etc.)
}
override fun cancelPayment() {
// Handle cancellation
}
}
Key Characteristics
| Property | HostApi |
|---|---|
| Call direction | Flutter → Native |
| Implemented by | Native |
| Used for | Commands |
| Type safety | ✅ Compile-time |
| JSON parsing | ❌ None |
2️⃣ FlutterApi Methods (Native → Flutter)
What are FlutterApi methods?
- Defined in Dart
- Implemented in Flutter
- Called by native
Think of them as events or callbacks.
Use cases
- Payment status updates
- SDK lifecycle events
- Sensor updates
- Progress callbacks
Dart Definition
@FlutterApi()
abstract class PaymentFlutterApi {
void onPaymentSuccess(String txnId);
void onPaymentFailure(String reason);
}
Generated Native Class (Android – Kotlin)
class PaymentFlutterApi(
private val binaryMessenger: BinaryMessenger
) {
fun onPaymentSuccess(txnId: String) {}
fun onPaymentFailure(reason: String) {}
}
Calling from Native
val flutterApi = PaymentFlutterApi(binaryMessenger)
flutterApi.onPaymentSuccess("TXN12345")
flutterApi.onPaymentFailure("Insufficient balance")
Flutter Implementation
class PaymentCallbacks extends PaymentFlutterApi {
@override
void onPaymentSuccess(String txnId) {
// Update Bloc / Cubit / State
}
@override
void onPaymentFailure(String reason) {
// Show error UI
}
}
Key Characteristics
| Property | FlutterApi |
|---|---|
| Call direction | Native → Flutter |
| Implemented by | Flutter |
| Used for | Events |
| Real-time updates | ✅ |
| State friendly | ✅ |
Visual Call Flow

Flutter UI
│
│ HostApi (command)
▼
Native SDK
│
│ FlutterApi (event)
▼
Flutter State → UI rebuild
Async Methods in Pigeon
Pigeon supports async operations using Future in Dart.
Dart
@HostApi()
abstract class DeviceApi {
Future<String> getDeviceId();
}
Android (Kotlin)
override fun getDeviceId(result: Result<String>) {
try {
result.success("ANDROID-DEVICE-001")
} catch (e: Exception) {
result.error(e)
}
}
iOS (Swift)
func getDeviceId(completion: @escaping (Result<String, Error>) -> Void) {
completion(.success("IOS-DEVICE-001"))
}
Common Real-World Pattern (Payments Example)
HostApi (Flutter → Native)
void initiatePayment(Map<String, String> payload);
void exitSdk();
FlutterApi (Native → Flutter)
void onTransactionSuccess(String txnId);
void onTransactionFailure(String error);
void onSdkClosed();
This pattern is used heavily in:
- Juspay
- Razorpay
- Paytm
- PhonePe
Why Pigeon is Better Than MethodChannel
| Feature | MethodChannel | Pigeon |
|---|---|---|
| Type safety | ❌ | ✅ |
| Compile-time errors | ❌ | ✅ |
| JSON parsing | Required | None |
| Refactoring safety | ❌ | ✅ |
| Large SDK support | Painful | Clean |
Mental Model to Remember
@HostApi = Commands (Flutter → Native)
@FlutterApi = Events (Native → Flutter)
If you understand this, you understand Pigeon.
What Pigeon Does NOT Do
- ❌ Render UI
- ❌ Manage state
- ❌ Replace Bloc / Cubit / Streams
- ❌ Act as a real-time engine
It only guarantees safe communication.
Conclusion
Pigeon is not magic—but it removes an entire class of bugs from Flutter-native integration.
If your app:
- Uses native SDKs
- Handles real-time callbacks
- Needs stability at scale
👉 Pigeon is the correct solution.