This episode opens access to native capabilities: using platform channels to communicate with Android and iOS code, integrating device APIs like camera, location, and sensors, developing custom plugins, and managing plugin compatibility and platform-specific code.

Flutter draws its own UI, but sometimes an app must use platform capabilities: the camera, GPS, fingerprint, or APIs that only exist natively. Episode 12 explains the bridge between Flutter and the platform: platform channels, device API integration through plugins, custom plugin development, and compatibility management.
A platform channel is a communication conduit between Dart code and Android (Kotlin) or iOS (Swift) code. When a plugin calls MethodChannel, the framework forwards the message to native and returns the result.
const platform = MethodChannel('com.example.myapp/battery');
Future<int> ambilBaterai() async {
final level = await platform.invokeMethod<int>('getBatteryLevel');
return level!;
}The MethodChannel on the Dart side calls the method name getBatteryLevel implemented on the native side. The channel name must be unique — follow the com.company/app pattern — and the method signatures on both sides must be identical.
On the Android side (MainActivity.kt), the channel implementation is attached in configureFlutterEngine:
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.example.myapp/battery",
).setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
result.success(42)
} else {
result.notImplemented()
}
}
}setMethodCallHandler receives call.method and answers through result.success. The channel name and method contract must match the Dart side exactly, or the call fails with MissingPluginException.
Plugins already handle the platform channel for common APIs. The most frequently used:
camera — access the camera for preview and capture.geolocator — location with permission handling.sensors_plus — accelerometer and gyroscope.flutter pub add camera geolocatorDevice plugins require platform permissions. For location on Android, add this to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />The location permission is written as <uses-permission android:name="...ACCESS_FINE_LOCATION"/> inside the manifest. Remember: static permissions are declared in the manifest, but runtime permission for location still needs to be requested from code — the geolocator plugin provides a helper for that.
Don't call GPS directly without asking for permission:
final layananAktif = await Geolocator.isLocationServiceEnabled();
if (!layananAktif) {
return;
}
LocationPermission izin = await Geolocator.checkPermission();
if (izin == LocationPermission.denied) {
izin = await Geolocator.requestPermission();
}The isLocationServiceEnabled → checkPermission → requestPermission flow is the correct pattern: check the service, check the permission, then request. Skipping these steps is the most common cause of crashes and app review rejections.
When a native feature is too specific for a public plugin, create your own:
flutter create --template=plugin my_device_plugin
cd my_device_pluginflutter create --template=plugin generates a complete plugin structure with android, ios, and example/ folders. The Dart side holds the public API; the native side holds the channel implementation.
lib/ — the Dart API consumed by apps.android/ — the Kotlin implementation.ios/ — the Swift implementation.example/ — a demo app to test the plugin.Start with one simple method channel, then expand. A good plugin documents supported platforms and handles notImplemented correctly.
Not every plugin supports every platform. Before using one, check the platform labels on its pub.dev page, and verify in your project:
flutter pub depsflutter pub deps shows the list of dependencies. For per-platform compatibility details, check the plugin documentation or its pubspec.yaml.
When you need different behavior per platform, use a conditional import or defaultTargetPlatform:
import 'package:flutter/foundation.dart';
if (defaultTargetPlatform == TargetPlatform.android) {
return const AndroidLayout();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
return const IOSLayout();
}defaultTargetPlatform tells you the platform at runtime without importing native code. This is enough for small differences; for large differences, use a conditional import — a technique we'll explore in depth in episode 18.
Key takeaways:
flutter create --template=plugin for custom plugins.In the next episode 13 we discuss testing and quality assurance — unit testing Dart logic, widget testing and golden tests, integration testing with flutter_test and flutter drive, and continuous testing and test automation. Your app's quality starts being accountable through tests.