Introduction
PlatformException is thrown when native platform code (Android Kotlin/Java or iOS Swift/Objective-C) returns an error through a Flutter method channel. This happens when the native implementation encounters an error and calls result.error() on Android or throws a FlutterError on iOS. Understanding the error code and message is key to resolving the issue. Method channels are the primary communication bridge between Flutter's Dart code and platform-specific native code, making them critical for accessing device features like camera, GPS, sensors, and platform APIs. When this bridge fails, the entire feature breaks, and the error must be traced across both the Dart and native layers.
Symptoms
- Console error:
`- PlatformException(channel-error, Unable to establish connection on channel: "com.example/plugin".
- Details: error code 12, null, null)
`- Or:
`- PlatformException(PERMISSION_DENIED, Location permission denied, null, null)
`- Stack trace points to
MethodChannel.invokeMethod
Common Causes
- Native code explicitly returns an error via
result.error(code, message, details) - Required platform permissions not granted (camera, location, storage)
- Native SDK not properly initialized in
MainActivityorAppDelegate - Method name mismatch between Dart and native implementations
- Missing dependencies in
build.gradleorPodfile
Step-by-Step Fix
- 1.Extract full error details in Dart:
- 2.```dart
- 3.try {
- 4.final result = await methodChannel.invokeMethod('getLocation');
- 5.} on PlatformException catch (e) {
- 6.print('Error code: ${e.code}');
- 7.print('Error message: ${e.message}');
- 8.print('Error details: ${e.details}');
- 9.print('Stack trace: ${e.stacktrace}');
- 10.}
- 11.
` - 12.Check Android native implementation (
MainActivity.kt): - 13.```kotlin
- 14.MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example/plugin")
- 15..setMethodCallHandler { call, result ->
- 16.when (call.method) {
- 17."getLocation" -> {
- 18.if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
- 19.!= PackageManager.PERMISSION_GRANTED) {
- 20.result.error(
- 21."PERMISSION_DENIED",
- 22."Location permission not granted",
- 23.null
- 24.)
- 25.return@setMethodCallHandler
- 26.}
- 27.val location = fusedLocationClient.lastLocation
- 28.result.success(location)
- 29.}
- 30.else -> result.notImplemented()
- 31.}
- 32.}
- 33.
` - 34.Check iOS native implementation (
AppDelegate.swift): - 35.```swift
- 36.let controller = window?.rootViewController as! FlutterViewController
- 37.let channel = FlutterMethodChannel(name: "com.example/plugin",
- 38.binaryMessenger: controller.binaryMessenger)
- 39.channel.setMethodCallHandler { (call, result) in
- 40.switch call.method {
- 41.case "getLocation":
- 42.if CLLocationManager.locationServicesEnabled() {
- 43.self.locationManager.requestLocation()
- 44.result.success(true)
- 45.} else {
- 46.result(FlutterError(code: "SERVICES_DISABLED",
- 47.message: "Location services are disabled",
- 48.details: nil))
- 49.}
- 50.default:
- 51.result(FlutterMethodNotImplemented)
- 52.}
- 53.}
- 54.
` - 55.Verify permissions in AndroidManifest.xml:
- 56.```xml
- 57.<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- 58.<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- 59.
` - 60.Verify permissions in Info.plist:
- 61.```xml
- 62.<key>NSLocationWhenInUseUsageDescription</key>
- 63.<string>This app needs access to location for mapping features.</string>
- 64.
` - 65.Handle permission request before channel call:
- 66.```dart
- 67.import 'package:permission_handler/permission_handler.dart';
Future<void> getLocation() async { final status = await Permission.location.request(); if (!status.isGranted) { throw Exception('Location permission denied'); } final result = await methodChannel.invokeMethod('getLocation'); return result; } ```
Prevention
- Always request permissions before calling native methods that require them
- Wrap all
invokeMethodcalls in try-catch withPlatformExceptionhandling - Log the full
PlatformExceptiondetails (code, message, details) for debugging - Test on both Android and iOS simulators and real devices
- Use
flutter run --verbosefor detailed channel communication logs