Calling Native Libraries in Flutter Using Platform Channels
Introduction
Flutter, a cross-platform framework, allows you to leverage native features on both Android and iOS. Platform Channels provide a communication bridge between your Flutter code and native platforms. This enables you to access platform-specific functionalities that are not directly available in Flutter.
What are Platform Channels?
Platform Channels are a mechanism for asynchronous communication between Flutter and native code. They allow your Flutter application to interact with native libraries, services, or system functionalities.
Types of Platform Channels
There are three types of Platform Channels:
– **BasicMessageChannel:** Used for exchanging simple data types, such as strings, integers, and lists.
– **MethodChannel:** Offers more flexibility for invoking methods and retrieving results from native code.
– **EventChannel:** Enables native code to send streams of data to Flutter.
Using Platform Channels
Here’s a step-by-step guide to use Platform Channels:
1. Set Up Platform Channels
* **Flutter:** Define the channel name in your Flutter code.
* **Android:** Create a `MethodChannel` in your Activity or Service.
* **iOS:** Create a `FlutterMethodChannel` in your ViewController or AppDelegate.
2. Implement Method Calls
* **Flutter:** Use the `invokeMethod` function on your channel to call a method in native code.
* **Native:** Implement a handler for the method call in your native code, process the data, and send a result back to Flutter.
3. Receive Responses
* **Flutter:** Handle the result returned from the native method.
* **Native:** Use the `success` or `error` methods on the `MethodCallHandler` to send a response back to Flutter.
Example: Accessing the Device Battery Level
Let’s demonstrate using a Platform Channel to retrieve the device’s battery level.
Flutter Code
“`html
import 'package:flutter/services.dart'; class BatteryInfo { static const platform = MethodChannel('samples.flutter.dev/battery'); static FuturegetBatteryLevel() async { String batteryLevel; try { final result = await platform.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level: $result%'; } on PlatformException catch (e) { batteryLevel = 'Failed to get battery level: ${e.message}'; } return batteryLevel; } }
“`
Android Code (MainActivity.java)
“`html
import io.flutter.embedding.android.FlutterActivity; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import android.content.Context; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build; import android.os.Bundle; public class MainActivity extends FlutterActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MethodChannel batteryChannel = new MethodChannel(flutterView, "samples.flutter.dev/battery"); batteryChannel.setMethodCallHandler(new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("getBatteryLevel")) { int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success(batteryLevel); } else { result.error("UNAVAILABLE", "Could not get battery level.", null); } } else { result.notImplemented(); } } }); } private int getBatteryLevel() { int batteryLevel = -1; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { BatteryManager batteryManager = (BatteryManager) getSystemService(Context.BATTERY_SERVICE); batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); } else { IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatus = registerReceiver(null, ifilter); batteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); } return batteryLevel; } }
“`
iOS Code (AppDelegate.swift)
“`html
import UIKit import Flutter @UIApplicationMain class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery", binaryMessenger: self.registrar(forPlugin: "FlutterPluginRegistry")!.messenger) batteryChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) in if call.method == "getBatteryLevel" { let batteryLevel = UIDevice.current.batteryLevel * 100 result(batteryLevel) } else { result(FlutterMethodNotImplemented) } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
“`
Advantages of Using Platform Channels
– **Modular Architecture:** Keep your Flutter and native codebase separate.
– **Platform-Specific Functionality:** Access features not available in Flutter’s core libraries.
– **Reusability:** Share platform-specific code across multiple Flutter projects.
Comparison Table
| Feature | BasicMessageChannel | MethodChannel | EventChannel |
|—————|———————|————–|—————–|
| Data Exchange | Simple data types | Methods and data | Streams of data |
| Usage | Sending messages | Calling methods | Receiving updates |
| Examples | Sending messages, storing data | Invoking platform APIs | Receiving location updates |
Conclusion
Platform Channels are a powerful tool for extending your Flutter application with native features. By leveraging this mechanism, you can seamlessly integrate platform-specific functionalities and enhance the overall capabilities of your Flutter app.