Write Platform-Specific Code in Flutter Apps


Platform-Specific Code
Platform-Specific Code
Platform-Specific Code
Platform-Specific Code
While Flutter provides a rich set of cross-platform widgets, sometimes you need to tap into platform-specific APIs—for camera access, sensors, native dialogs, or system services. That’s where platform channels come in. They let you write native code in Android, iOS, Windows, macOS, or Linux and call it directly from Dart.
Learn how to call platform-specific code in Flutter using platform channels and Pigeon. This guide covers native integration for Android, iOS, Windows, macOS, and Linux with best practices for clean, maintainable architecture.
In this guide, you’ll learn how to create a custom Flutter app that calls native code across all major platforms. We’ll explore the architectural setup, supported data types, and how to use both platform channels and Pigeon for type-safe communication. You'll also understand how threading works in this setup and how to keep platform code clean and reusable—essential for building plugins or publishing packages.
While Flutter provides a rich set of cross-platform widgets, sometimes you need to tap into platform-specific APIs—for camera access, sensors, native dialogs, or system services. That’s where platform channels come in. They let you write native code in Android, iOS, Windows, macOS, or Linux and call it directly from Dart.
Learn how to call platform-specific code in Flutter using platform channels and Pigeon. This guide covers native integration for Android, iOS, Windows, macOS, and Linux with best practices for clean, maintainable architecture.
In this guide, you’ll learn how to create a custom Flutter app that calls native code across all major platforms. We’ll explore the architectural setup, supported data types, and how to use both platform channels and Pigeon for type-safe communication. You'll also understand how threading works in this setup and how to keep platform code clean and reusable—essential for building plugins or publishing packages.
While Flutter provides a rich set of cross-platform widgets, sometimes you need to tap into platform-specific APIs—for camera access, sensors, native dialogs, or system services. That’s where platform channels come in. They let you write native code in Android, iOS, Windows, macOS, or Linux and call it directly from Dart.
Learn how to call platform-specific code in Flutter using platform channels and Pigeon. This guide covers native integration for Android, iOS, Windows, macOS, and Linux with best practices for clean, maintainable architecture.
In this guide, you’ll learn how to create a custom Flutter app that calls native code across all major platforms. We’ll explore the architectural setup, supported data types, and how to use both platform channels and Pigeon for type-safe communication. You'll also understand how threading works in this setup and how to keep platform code clean and reusable—essential for building plugins or publishing packages.
While Flutter provides a rich set of cross-platform widgets, sometimes you need to tap into platform-specific APIs—for camera access, sensors, native dialogs, or system services. That’s where platform channels come in. They let you write native code in Android, iOS, Windows, macOS, or Linux and call it directly from Dart.
Learn how to call platform-specific code in Flutter using platform channels and Pigeon. This guide covers native integration for Android, iOS, Windows, macOS, and Linux with best practices for clean, maintainable architecture.
In this guide, you’ll learn how to create a custom Flutter app that calls native code across all major platforms. We’ll explore the architectural setup, supported data types, and how to use both platform channels and Pigeon for type-safe communication. You'll also understand how threading works in this setup and how to keep platform code clean and reusable—essential for building plugins or publishing packages.
Overview
Overview
Overview
Overview
Flutter allows you to write platform-specific code to access native functionality that's not available through the Flutter framework. You can do this in two primary ways:
Platform Channels: Send asynchronous messages between Dart and native code on Android, iOS, Windows, macOS, or Linux. See Call platform-specific code using platform channels.
Pigeon Package: Generate type-safe code for platform communication using a declarative Dart syntax. See the Call platform-specific code using the Pigeon package.
Flutter currently supports the following platforms and native languages:
Android: Kotlin, Java
iOS: Swift, Objective-C
Windows: C++
macOS: Objective-C
Linux: C
These tools let you integrate custom native features while maintaining Flutter’s cross-platform codebase.
Flutter allows you to write platform-specific code to access native functionality that's not available through the Flutter framework. You can do this in two primary ways:
Platform Channels: Send asynchronous messages between Dart and native code on Android, iOS, Windows, macOS, or Linux. See Call platform-specific code using platform channels.
Pigeon Package: Generate type-safe code for platform communication using a declarative Dart syntax. See the Call platform-specific code using the Pigeon package.
Flutter currently supports the following platforms and native languages:
Android: Kotlin, Java
iOS: Swift, Objective-C
Windows: C++
macOS: Objective-C
Linux: C
These tools let you integrate custom native features while maintaining Flutter’s cross-platform codebase.
Flutter allows you to write platform-specific code to access native functionality that's not available through the Flutter framework. You can do this in two primary ways:
Platform Channels: Send asynchronous messages between Dart and native code on Android, iOS, Windows, macOS, or Linux. See Call platform-specific code using platform channels.
Pigeon Package: Generate type-safe code for platform communication using a declarative Dart syntax. See the Call platform-specific code using the Pigeon package.
Flutter currently supports the following platforms and native languages:
Android: Kotlin, Java
iOS: Swift, Objective-C
Windows: C++
macOS: Objective-C
Linux: C
These tools let you integrate custom native features while maintaining Flutter’s cross-platform codebase.
Flutter allows you to write platform-specific code to access native functionality that's not available through the Flutter framework. You can do this in two primary ways:
Platform Channels: Send asynchronous messages between Dart and native code on Android, iOS, Windows, macOS, or Linux. See Call platform-specific code using platform channels.
Pigeon Package: Generate type-safe code for platform communication using a declarative Dart syntax. See the Call platform-specific code using the Pigeon package.
Flutter currently supports the following platforms and native languages:
Android: Kotlin, Java
iOS: Swift, Objective-C
Windows: C++
macOS: Objective-C
Linux: C
These tools let you integrate custom native features while maintaining Flutter’s cross-platform codebase.
Flutter’s platform channels provide a seamless way to pass messages between the Dart UI layer (client) and platform-specific native code (host). This architecture ensures communication happens asynchronously, keeping the UI responsive even during heavy operations.

On the Flutter side,
MethodChannel
is used to send method calls.On the Android side, messages are received via
MethodChannel
(Kotlin/Java).On the iOS side,
FlutterMethodChannel
(Swift/Objective-C) is used to handle method calls and return responses.
This setup minimizes boilerplate, making it easy to build plugins or access native services.
🔄 Note: All method calls must be invoked on the platform’s main thread, even though Flutter handles communication asynchronously. For details, see the threading section.
Additionally, the channel communication is bidirectional—platforms can also call Dart methods. A practical example is the quick_actions
plugin, where the native side initiates the call.
Flutter’s platform channels provide a seamless way to pass messages between the Dart UI layer (client) and platform-specific native code (host). This architecture ensures communication happens asynchronously, keeping the UI responsive even during heavy operations.

On the Flutter side,
MethodChannel
is used to send method calls.On the Android side, messages are received via
MethodChannel
(Kotlin/Java).On the iOS side,
FlutterMethodChannel
(Swift/Objective-C) is used to handle method calls and return responses.
This setup minimizes boilerplate, making it easy to build plugins or access native services.
🔄 Note: All method calls must be invoked on the platform’s main thread, even though Flutter handles communication asynchronously. For details, see the threading section.
Additionally, the channel communication is bidirectional—platforms can also call Dart methods. A practical example is the quick_actions
plugin, where the native side initiates the call.
Flutter’s platform channels provide a seamless way to pass messages between the Dart UI layer (client) and platform-specific native code (host). This architecture ensures communication happens asynchronously, keeping the UI responsive even during heavy operations.

On the Flutter side,
MethodChannel
is used to send method calls.On the Android side, messages are received via
MethodChannel
(Kotlin/Java).On the iOS side,
FlutterMethodChannel
(Swift/Objective-C) is used to handle method calls and return responses.
This setup minimizes boilerplate, making it easy to build plugins or access native services.
🔄 Note: All method calls must be invoked on the platform’s main thread, even though Flutter handles communication asynchronously. For details, see the threading section.
Additionally, the channel communication is bidirectional—platforms can also call Dart methods. A practical example is the quick_actions
plugin, where the native side initiates the call.
Flutter’s platform channels provide a seamless way to pass messages between the Dart UI layer (client) and platform-specific native code (host). This architecture ensures communication happens asynchronously, keeping the UI responsive even during heavy operations.

On the Flutter side,
MethodChannel
is used to send method calls.On the Android side, messages are received via
MethodChannel
(Kotlin/Java).On the iOS side,
FlutterMethodChannel
(Swift/Objective-C) is used to handle method calls and return responses.
This setup minimizes boilerplate, making it easy to build plugins or access native services.
🔄 Note: All method calls must be invoked on the platform’s main thread, even though Flutter handles communication asynchronously. For details, see the threading section.
Additionally, the channel communication is bidirectional—platforms can also call Dart methods. A practical example is the quick_actions
plugin, where the native side initiates the call.
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Dart Type | Kotlin Type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dart to Native Type Mapping Across Platforms
Dart Type | Java | Kotlin | Swift | Objective-C | C++ | C (GObject) |
---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Let me know if you’d like a downloadable format (CSV or Markdown table) or platform-specific examples for each data type usage.
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Dart Type | Kotlin Type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dart to Native Type Mapping Across Platforms
Dart Type | Java | Kotlin | Swift | Objective-C | C++ | C (GObject) |
---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Let me know if you’d like a downloadable format (CSV or Markdown table) or platform-specific examples for each data type usage.
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Dart Type | Kotlin Type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dart to Native Type Mapping Across Platforms
Dart Type | Java | Kotlin | Swift | Objective-C | C++ | C (GObject) |
---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Let me know if you’d like a downloadable format (CSV or Markdown table) or platform-specific examples for each data type usage.
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Flutter uses the StandardMessageCodec
to serialize and deserialize data between Dart and native code. It supports a range of JSON-like values for seamless integration.
Here's how Dart types map to Kotlin types:
Dart Type | Kotlin Type |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dart to Native Type Mapping Across Platforms
Dart Type | Java | Kotlin | Swift | Objective-C | C++ | C (GObject) |
---|---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Let me know if you’d like a downloadable format (CSV or Markdown table) or platform-specific examples for each data type usage.
Call Platform-Specific Code Using Platform Channels
Call Platform-Specific Code Using Platform Channels
Call Platform-Specific Code Using Platform Channels
Call Platform-Specific Code Using Platform Channels
This example shows how to fetch and display the current battery level across Android, iOS, Windows, macOS, and Linux using a single method call: getBatteryLevel()
.
You can place platform-specific code directly in your app, or package it into reusable plugins. The dual use of platform channels works the same in both cases.
Step 1: Create a new app project#
Start by creating a new app:
In a terminal run:
flutter create batterylevel
By default, our template supports writing Android code using Kotlin, or iOS code using Swift. To use Java or Objective-C, use the -i
and/or -a
flags:
In a terminal run:
flutter create -i objc -a java batterylevel
Step 2: Create the Flutter platform client#
The app's State
class holds the current app state. Extend that to hold the current battery state.
First, construct the channel. Use a MethodChannel
with a single platform method that returns the battery level.
The client and host sides of a channel are connected through a channel name passed in the channel constructor. All channel names used in a single app must be unique; prefix the channel name with a unique 'domain prefix', for example: samples.flutter.dev/battery
.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('samples.flutter.dev/battery');
// Get battery level.
Next, invoke a method on the method channel, specifying the concrete method to call using the String
identifier getBatteryLevel
. The call might fail—for example, if the platform doesn't support the platform API (such as when running in a simulator), so wrap the invokeMethod
call in a try-catch statement.
Use the returned result to update the user interface state in _batteryLevel
inside setState
.
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
Finally, replace the build
method from the template to contain a small user interface that displays the battery state in a string, and a button for refreshing the value.
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _getBatteryLevel,
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
Step 3: Add an Android platform-specific implementation#
Start by opening the Android host portion of your Flutter app in Android Studio:
Start Android Studio
Select the menu item File > Open...
Navigate to the directory holding your Flutter app, and select the android folder inside it. Click OK.
Open the file
MainActivity.kt
located in the kotlin folder in the Project view.
Inside the configureFlutterEngine()
method, create a MethodChannel
and call setMethodCallHandler()
. Make sure to use the same channel name as was used on the Flutter client side.
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
}
}
Add the Android Kotlin code that uses the Android battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Android app.
First, add the needed imports at the top of the file:
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
Next, add the following method in the MainActivity
class, below the configureFlutterEngine()
method:
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Android code written in the previous step, and returns a response for both the success and error cases using the result
argument. If an unknown method is called, report that instead.
Remove the following code:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
And replace with the following:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// This method is invoked on the main thread.
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
Step 4: Add an iOS platform-specific implementation#
Swift | Objective-C
Start by opening the iOS host portion of your Flutter app in Xcode:Start Xcode.Select the menu item File > Open....Navigate to the directory holding your Flutter app, and select the ios folder inside it. Click OK.Add support for Swift in the standard template setup that uses Objective-C:Expand Runner > Runner in the Project navigator.Open the file AppDelegate.swift
located under Runner > Runner in the Project navigator.Override the application:didFinishLaunchingWithOptions:
function and create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
// Handle battery messages.
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Next, add the iOS Swift code that uses the iOS battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native iOS app.
Add the following as a new method at the bottom of AppDelegate.swift
:
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery level not available.",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the iOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
})
You should now be able to run the app on iOS. If using the iOS Simulator, note that it doesn't support battery APIs, and the app displays 'Battery level not available'.
Step 5: Add a Windows platform-specific implementation#
Start by opening the Windows host portion of your Flutter app in Visual Studio:
Run
flutter build windows
in your project directory once to generate the Visual Studio solution file.Start Visual Studio.
Select Open a project or solution.
Navigate to the directory holding your Flutter app, then into the build folder, then the windows folder, then select the
batterylevel.sln
file. Click Open.
Add the C++ implementation of the platform channel method:
Expand batterylevel > Source Files in the Solution Explorer.
Open the file
flutter_window.cpp
.
First, add the necessary includes to the top of the file, just after #include "flutter_window.h"
:
#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>
#include <memory
Edit the FlutterWindow::OnCreate
method and create a flutter::MethodChannel
tied to the channel name samples.flutter.dev/battery
:
bool FlutterWindow::OnCreate() {
// ...
RegisterPlugins(flutter_controller_->engine());
flutter::MethodChannel<> channel(
flutter_controller_->engine()->messenger(), "samples.flutter.dev/battery",
&flutter::StandardMethodCodec::GetInstance());
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
Next, add the C++ code that uses the Windows battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Windows application.
Add the following as a new function at the top of flutter_window.cpp
just after the #include
section:
static int GetBatteryLevel() {
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status) == 0 || status.BatteryLifePercent == 255) {
return -1;
}
return status.BatteryLifePercent;
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method, getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Windows code written in the previous step. If an unknown method is called, report that instead.
Remove the following code:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
And replace with the following:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
if (call.method_name() == "getBatteryLevel") {
int battery_level = GetBatteryLevel();
if (battery_level != -1) {
result->Success(battery_level);
} else {
result->Error("UNAVAILABLE", "Battery level not available.");
}
} else {
result->NotImplemented()
You should now be able to run the application on Windows. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 6: Add a macOS platform-specific implementation#
Start by opening the macOS host portion of your Flutter app in Xcode:
Start Xcode.
Select the menu item File > Open....
Navigate to the directory holding your Flutter app, and select the macos folder inside it. Click OK.
Add the Swift implementation of the platform channel method:
Expand Runner > Runner in the Project navigator.
Open the file
MainFlutterWindow.swift
located under Runner > Runner in the Project navigator.
First, add the necessary import to the top of the file, just after import FlutterMacOS
:
import IOKit.ps
Create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
in the awakeFromNib
method:
override func awakeFromNib() {
// ...
self.setFrame(windowFrame, display: true)
let batteryChannel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: flutterViewController.engine.binaryMessenger)
batteryChannel.setMethodCallHandler { (call, result) in
// This method is invoked on the UI thread.
// Handle battery messages.
}
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
}
Next, add the macOS Swift code that uses the IOKit battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native macOS app.
Add the following as a new method at the bottom of MainFlutterWindow.swift
:
private func getBatteryLevel() -> Int? {
let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let sources: Array<CFTypeRef> = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array
if let source = sources.first {
let description =
IOPSGetPowerSourceDescription(info, source).takeUnretainedValue() as! [String: AnyObject]
if let level = description[kIOPSCurrentCapacityKey] as? Int {
Finally, complete the setMethodCallHandler
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the macOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler { (call, result) in
switch call.method {
case "getBatteryLevel":
guard let level = getBatteryLevel() else {
result(
FlutterError(
code: "UNAVAILABLE",
message: "Battery level not available",
details: nil))
return
}
result(level)
default:
result(FlutterMethodNotImplemented)
}
}
You should now be able to run the application on macOS. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 7: Add a Linux platform-specific implementation#
For this example you need to install the upower
developer headers. This is likely available from your distribution, for example with:
sudo apt install libupower-glib-dev
Start by opening the Linux host portion of your Flutter app in the editor of your choice. The instructions below are for Visual Studio Code with the "C/C++" and "CMake" extensions installed, but can be adjusted for other IDEs.
Launch Visual Studio Code.
Open the linux directory inside your project.
Choose Yes in the prompt asking:
Would you like to configure project "linux"?
. This enables C++ autocomplete.Open the file
runner/my_application.cc
.
First, add the necessary includes to the top of the file, just after #include <flutter_linux/flutter_linux.h>
:
#include <math.h>
#include <upower.h
Add an FlMethodChannel
to the _MyApplication
struct:
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
FlMethodChannel* battery_channel;
};
Make sure to clean it up in my_application_dispose
:
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
g_clear_object(&self->battery_channel);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
Edit the my_application_activate
method and initialize battery_channel
using the channel name samples.flutter.dev/battery
, just after the call to fl_register_plugins
:
static void my_application_activate(GApplication* application) {
// ...
fl_register_plugins(FL_PLUGIN_REGISTRY(self->view));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->battery_channel = fl_method_channel_new(
fl_engine_get_binary_messenger(fl_view_get_engine(view)),
"samples.flutter.dev/battery", FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(
self->battery_channel, battery_method_call_handler, self, nullptr);
gtk_widget_grab_focus(GTK_WIDGET(self->view));
}
Next, add the C code that uses the Linux battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Linux application.
Add the following as a new function at the top of my_application.cc
just after the G_DEFINE_TYPE
line:
static FlMethodResponse* get_battery_level() {
// Find the first available battery and report that.
g_autoptr(UpClient) up_client = up_client_new();
g_autoptr(GPtrArray) devices = up_client_get_devices2(up_client);
if (devices->len == 0) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
"UNAVAILABLE", "Device does not have a battery.", nullptr));
}
UpDevice* device = UP_DEVICE(g_ptr_array_index(devices, 0));
double percentage = 0;
g_object_get(device, "percentage", &percentage, nullptr);
g_autoptr(FlValue) result =
fl_value_new_int(static_cast<int64_t>(round(percentage)));
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
Finally, add the battery_method_call_handler
function referenced in the earlier call to fl_method_channel_set_method_call_handler
. You need to handle a single platform method, getBatteryLevel
, so test for that in the method_call
argument. The implementation of this function calls the Linux code written in the previous step. If an unknown method is called, report that instead.
Add the following code after the get_battery_level
function:
static void battery_method_call_handler(FlMethodChannel* channel,
FlMethodCall* method_call,
gpointer user_data) {
g_autoptr(FlMethodResponse) response = nullptr;
if (strcmp(fl_method_call_get_name(method_call), "getBatteryLevel") == 0) {
response = get_battery_level();
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
g_autoptr(GError) error = nullptr;
if (!fl_method_call_respond(method_call, response, &error)) {
g_warning("Failed to send response: %s", error->message);
}
}
You should now be able to run the application on Linux. If your device doesn't have a battery, it displays 'Battery level not available'.
This example shows how to fetch and display the current battery level across Android, iOS, Windows, macOS, and Linux using a single method call: getBatteryLevel()
.
You can place platform-specific code directly in your app, or package it into reusable plugins. The dual use of platform channels works the same in both cases.
Step 1: Create a new app project#
Start by creating a new app:
In a terminal run:
flutter create batterylevel
By default, our template supports writing Android code using Kotlin, or iOS code using Swift. To use Java or Objective-C, use the -i
and/or -a
flags:
In a terminal run:
flutter create -i objc -a java batterylevel
Step 2: Create the Flutter platform client#
The app's State
class holds the current app state. Extend that to hold the current battery state.
First, construct the channel. Use a MethodChannel
with a single platform method that returns the battery level.
The client and host sides of a channel are connected through a channel name passed in the channel constructor. All channel names used in a single app must be unique; prefix the channel name with a unique 'domain prefix', for example: samples.flutter.dev/battery
.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('samples.flutter.dev/battery');
// Get battery level.
Next, invoke a method on the method channel, specifying the concrete method to call using the String
identifier getBatteryLevel
. The call might fail—for example, if the platform doesn't support the platform API (such as when running in a simulator), so wrap the invokeMethod
call in a try-catch statement.
Use the returned result to update the user interface state in _batteryLevel
inside setState
.
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
Finally, replace the build
method from the template to contain a small user interface that displays the battery state in a string, and a button for refreshing the value.
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _getBatteryLevel,
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
Step 3: Add an Android platform-specific implementation#
Start by opening the Android host portion of your Flutter app in Android Studio:
Start Android Studio
Select the menu item File > Open...
Navigate to the directory holding your Flutter app, and select the android folder inside it. Click OK.
Open the file
MainActivity.kt
located in the kotlin folder in the Project view.
Inside the configureFlutterEngine()
method, create a MethodChannel
and call setMethodCallHandler()
. Make sure to use the same channel name as was used on the Flutter client side.
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
}
}
Add the Android Kotlin code that uses the Android battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Android app.
First, add the needed imports at the top of the file:
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
Next, add the following method in the MainActivity
class, below the configureFlutterEngine()
method:
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Android code written in the previous step, and returns a response for both the success and error cases using the result
argument. If an unknown method is called, report that instead.
Remove the following code:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
And replace with the following:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// This method is invoked on the main thread.
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
Step 4: Add an iOS platform-specific implementation#
Swift | Objective-C
Start by opening the iOS host portion of your Flutter app in Xcode:Start Xcode.Select the menu item File > Open....Navigate to the directory holding your Flutter app, and select the ios folder inside it. Click OK.Add support for Swift in the standard template setup that uses Objective-C:Expand Runner > Runner in the Project navigator.Open the file AppDelegate.swift
located under Runner > Runner in the Project navigator.Override the application:didFinishLaunchingWithOptions:
function and create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
// Handle battery messages.
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Next, add the iOS Swift code that uses the iOS battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native iOS app.
Add the following as a new method at the bottom of AppDelegate.swift
:
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery level not available.",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the iOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
})
You should now be able to run the app on iOS. If using the iOS Simulator, note that it doesn't support battery APIs, and the app displays 'Battery level not available'.
Step 5: Add a Windows platform-specific implementation#
Start by opening the Windows host portion of your Flutter app in Visual Studio:
Run
flutter build windows
in your project directory once to generate the Visual Studio solution file.Start Visual Studio.
Select Open a project or solution.
Navigate to the directory holding your Flutter app, then into the build folder, then the windows folder, then select the
batterylevel.sln
file. Click Open.
Add the C++ implementation of the platform channel method:
Expand batterylevel > Source Files in the Solution Explorer.
Open the file
flutter_window.cpp
.
First, add the necessary includes to the top of the file, just after #include "flutter_window.h"
:
#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>
#include <memory
Edit the FlutterWindow::OnCreate
method and create a flutter::MethodChannel
tied to the channel name samples.flutter.dev/battery
:
bool FlutterWindow::OnCreate() {
// ...
RegisterPlugins(flutter_controller_->engine());
flutter::MethodChannel<> channel(
flutter_controller_->engine()->messenger(), "samples.flutter.dev/battery",
&flutter::StandardMethodCodec::GetInstance());
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
Next, add the C++ code that uses the Windows battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Windows application.
Add the following as a new function at the top of flutter_window.cpp
just after the #include
section:
static int GetBatteryLevel() {
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status) == 0 || status.BatteryLifePercent == 255) {
return -1;
}
return status.BatteryLifePercent;
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method, getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Windows code written in the previous step. If an unknown method is called, report that instead.
Remove the following code:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
And replace with the following:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
if (call.method_name() == "getBatteryLevel") {
int battery_level = GetBatteryLevel();
if (battery_level != -1) {
result->Success(battery_level);
} else {
result->Error("UNAVAILABLE", "Battery level not available.");
}
} else {
result->NotImplemented()
You should now be able to run the application on Windows. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 6: Add a macOS platform-specific implementation#
Start by opening the macOS host portion of your Flutter app in Xcode:
Start Xcode.
Select the menu item File > Open....
Navigate to the directory holding your Flutter app, and select the macos folder inside it. Click OK.
Add the Swift implementation of the platform channel method:
Expand Runner > Runner in the Project navigator.
Open the file
MainFlutterWindow.swift
located under Runner > Runner in the Project navigator.
First, add the necessary import to the top of the file, just after import FlutterMacOS
:
import IOKit.ps
Create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
in the awakeFromNib
method:
override func awakeFromNib() {
// ...
self.setFrame(windowFrame, display: true)
let batteryChannel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: flutterViewController.engine.binaryMessenger)
batteryChannel.setMethodCallHandler { (call, result) in
// This method is invoked on the UI thread.
// Handle battery messages.
}
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
}
Next, add the macOS Swift code that uses the IOKit battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native macOS app.
Add the following as a new method at the bottom of MainFlutterWindow.swift
:
private func getBatteryLevel() -> Int? {
let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let sources: Array<CFTypeRef> = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array
if let source = sources.first {
let description =
IOPSGetPowerSourceDescription(info, source).takeUnretainedValue() as! [String: AnyObject]
if let level = description[kIOPSCurrentCapacityKey] as? Int {
Finally, complete the setMethodCallHandler
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the macOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler { (call, result) in
switch call.method {
case "getBatteryLevel":
guard let level = getBatteryLevel() else {
result(
FlutterError(
code: "UNAVAILABLE",
message: "Battery level not available",
details: nil))
return
}
result(level)
default:
result(FlutterMethodNotImplemented)
}
}
You should now be able to run the application on macOS. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 7: Add a Linux platform-specific implementation#
For this example you need to install the upower
developer headers. This is likely available from your distribution, for example with:
sudo apt install libupower-glib-dev
Start by opening the Linux host portion of your Flutter app in the editor of your choice. The instructions below are for Visual Studio Code with the "C/C++" and "CMake" extensions installed, but can be adjusted for other IDEs.
Launch Visual Studio Code.
Open the linux directory inside your project.
Choose Yes in the prompt asking:
Would you like to configure project "linux"?
. This enables C++ autocomplete.Open the file
runner/my_application.cc
.
First, add the necessary includes to the top of the file, just after #include <flutter_linux/flutter_linux.h>
:
#include <math.h>
#include <upower.h
Add an FlMethodChannel
to the _MyApplication
struct:
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
FlMethodChannel* battery_channel;
};
Make sure to clean it up in my_application_dispose
:
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
g_clear_object(&self->battery_channel);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
Edit the my_application_activate
method and initialize battery_channel
using the channel name samples.flutter.dev/battery
, just after the call to fl_register_plugins
:
static void my_application_activate(GApplication* application) {
// ...
fl_register_plugins(FL_PLUGIN_REGISTRY(self->view));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->battery_channel = fl_method_channel_new(
fl_engine_get_binary_messenger(fl_view_get_engine(view)),
"samples.flutter.dev/battery", FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(
self->battery_channel, battery_method_call_handler, self, nullptr);
gtk_widget_grab_focus(GTK_WIDGET(self->view));
}
Next, add the C code that uses the Linux battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Linux application.
Add the following as a new function at the top of my_application.cc
just after the G_DEFINE_TYPE
line:
static FlMethodResponse* get_battery_level() {
// Find the first available battery and report that.
g_autoptr(UpClient) up_client = up_client_new();
g_autoptr(GPtrArray) devices = up_client_get_devices2(up_client);
if (devices->len == 0) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
"UNAVAILABLE", "Device does not have a battery.", nullptr));
}
UpDevice* device = UP_DEVICE(g_ptr_array_index(devices, 0));
double percentage = 0;
g_object_get(device, "percentage", &percentage, nullptr);
g_autoptr(FlValue) result =
fl_value_new_int(static_cast<int64_t>(round(percentage)));
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
Finally, add the battery_method_call_handler
function referenced in the earlier call to fl_method_channel_set_method_call_handler
. You need to handle a single platform method, getBatteryLevel
, so test for that in the method_call
argument. The implementation of this function calls the Linux code written in the previous step. If an unknown method is called, report that instead.
Add the following code after the get_battery_level
function:
static void battery_method_call_handler(FlMethodChannel* channel,
FlMethodCall* method_call,
gpointer user_data) {
g_autoptr(FlMethodResponse) response = nullptr;
if (strcmp(fl_method_call_get_name(method_call), "getBatteryLevel") == 0) {
response = get_battery_level();
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
g_autoptr(GError) error = nullptr;
if (!fl_method_call_respond(method_call, response, &error)) {
g_warning("Failed to send response: %s", error->message);
}
}
You should now be able to run the application on Linux. If your device doesn't have a battery, it displays 'Battery level not available'.
This example shows how to fetch and display the current battery level across Android, iOS, Windows, macOS, and Linux using a single method call: getBatteryLevel()
.
You can place platform-specific code directly in your app, or package it into reusable plugins. The dual use of platform channels works the same in both cases.
Step 1: Create a new app project#
Start by creating a new app:
In a terminal run:
flutter create batterylevel
By default, our template supports writing Android code using Kotlin, or iOS code using Swift. To use Java or Objective-C, use the -i
and/or -a
flags:
In a terminal run:
flutter create -i objc -a java batterylevel
Step 2: Create the Flutter platform client#
The app's State
class holds the current app state. Extend that to hold the current battery state.
First, construct the channel. Use a MethodChannel
with a single platform method that returns the battery level.
The client and host sides of a channel are connected through a channel name passed in the channel constructor. All channel names used in a single app must be unique; prefix the channel name with a unique 'domain prefix', for example: samples.flutter.dev/battery
.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('samples.flutter.dev/battery');
// Get battery level.
Next, invoke a method on the method channel, specifying the concrete method to call using the String
identifier getBatteryLevel
. The call might fail—for example, if the platform doesn't support the platform API (such as when running in a simulator), so wrap the invokeMethod
call in a try-catch statement.
Use the returned result to update the user interface state in _batteryLevel
inside setState
.
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
Finally, replace the build
method from the template to contain a small user interface that displays the battery state in a string, and a button for refreshing the value.
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _getBatteryLevel,
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
Step 3: Add an Android platform-specific implementation#
Start by opening the Android host portion of your Flutter app in Android Studio:
Start Android Studio
Select the menu item File > Open...
Navigate to the directory holding your Flutter app, and select the android folder inside it. Click OK.
Open the file
MainActivity.kt
located in the kotlin folder in the Project view.
Inside the configureFlutterEngine()
method, create a MethodChannel
and call setMethodCallHandler()
. Make sure to use the same channel name as was used on the Flutter client side.
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
}
}
Add the Android Kotlin code that uses the Android battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Android app.
First, add the needed imports at the top of the file:
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
Next, add the following method in the MainActivity
class, below the configureFlutterEngine()
method:
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Android code written in the previous step, and returns a response for both the success and error cases using the result
argument. If an unknown method is called, report that instead.
Remove the following code:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
And replace with the following:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// This method is invoked on the main thread.
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
Step 4: Add an iOS platform-specific implementation#
Swift | Objective-C
Start by opening the iOS host portion of your Flutter app in Xcode:Start Xcode.Select the menu item File > Open....Navigate to the directory holding your Flutter app, and select the ios folder inside it. Click OK.Add support for Swift in the standard template setup that uses Objective-C:Expand Runner > Runner in the Project navigator.Open the file AppDelegate.swift
located under Runner > Runner in the Project navigator.Override the application:didFinishLaunchingWithOptions:
function and create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
// Handle battery messages.
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Next, add the iOS Swift code that uses the iOS battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native iOS app.
Add the following as a new method at the bottom of AppDelegate.swift
:
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery level not available.",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the iOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
})
You should now be able to run the app on iOS. If using the iOS Simulator, note that it doesn't support battery APIs, and the app displays 'Battery level not available'.
Step 5: Add a Windows platform-specific implementation#
Start by opening the Windows host portion of your Flutter app in Visual Studio:
Run
flutter build windows
in your project directory once to generate the Visual Studio solution file.Start Visual Studio.
Select Open a project or solution.
Navigate to the directory holding your Flutter app, then into the build folder, then the windows folder, then select the
batterylevel.sln
file. Click Open.
Add the C++ implementation of the platform channel method:
Expand batterylevel > Source Files in the Solution Explorer.
Open the file
flutter_window.cpp
.
First, add the necessary includes to the top of the file, just after #include "flutter_window.h"
:
#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>
#include <memory
Edit the FlutterWindow::OnCreate
method and create a flutter::MethodChannel
tied to the channel name samples.flutter.dev/battery
:
bool FlutterWindow::OnCreate() {
// ...
RegisterPlugins(flutter_controller_->engine());
flutter::MethodChannel<> channel(
flutter_controller_->engine()->messenger(), "samples.flutter.dev/battery",
&flutter::StandardMethodCodec::GetInstance());
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
Next, add the C++ code that uses the Windows battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Windows application.
Add the following as a new function at the top of flutter_window.cpp
just after the #include
section:
static int GetBatteryLevel() {
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status) == 0 || status.BatteryLifePercent == 255) {
return -1;
}
return status.BatteryLifePercent;
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method, getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Windows code written in the previous step. If an unknown method is called, report that instead.
Remove the following code:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
And replace with the following:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
if (call.method_name() == "getBatteryLevel") {
int battery_level = GetBatteryLevel();
if (battery_level != -1) {
result->Success(battery_level);
} else {
result->Error("UNAVAILABLE", "Battery level not available.");
}
} else {
result->NotImplemented()
You should now be able to run the application on Windows. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 6: Add a macOS platform-specific implementation#
Start by opening the macOS host portion of your Flutter app in Xcode:
Start Xcode.
Select the menu item File > Open....
Navigate to the directory holding your Flutter app, and select the macos folder inside it. Click OK.
Add the Swift implementation of the platform channel method:
Expand Runner > Runner in the Project navigator.
Open the file
MainFlutterWindow.swift
located under Runner > Runner in the Project navigator.
First, add the necessary import to the top of the file, just after import FlutterMacOS
:
import IOKit.ps
Create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
in the awakeFromNib
method:
override func awakeFromNib() {
// ...
self.setFrame(windowFrame, display: true)
let batteryChannel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: flutterViewController.engine.binaryMessenger)
batteryChannel.setMethodCallHandler { (call, result) in
// This method is invoked on the UI thread.
// Handle battery messages.
}
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
}
Next, add the macOS Swift code that uses the IOKit battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native macOS app.
Add the following as a new method at the bottom of MainFlutterWindow.swift
:
private func getBatteryLevel() -> Int? {
let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let sources: Array<CFTypeRef> = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array
if let source = sources.first {
let description =
IOPSGetPowerSourceDescription(info, source).takeUnretainedValue() as! [String: AnyObject]
if let level = description[kIOPSCurrentCapacityKey] as? Int {
Finally, complete the setMethodCallHandler
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the macOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler { (call, result) in
switch call.method {
case "getBatteryLevel":
guard let level = getBatteryLevel() else {
result(
FlutterError(
code: "UNAVAILABLE",
message: "Battery level not available",
details: nil))
return
}
result(level)
default:
result(FlutterMethodNotImplemented)
}
}
You should now be able to run the application on macOS. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 7: Add a Linux platform-specific implementation#
For this example you need to install the upower
developer headers. This is likely available from your distribution, for example with:
sudo apt install libupower-glib-dev
Start by opening the Linux host portion of your Flutter app in the editor of your choice. The instructions below are for Visual Studio Code with the "C/C++" and "CMake" extensions installed, but can be adjusted for other IDEs.
Launch Visual Studio Code.
Open the linux directory inside your project.
Choose Yes in the prompt asking:
Would you like to configure project "linux"?
. This enables C++ autocomplete.Open the file
runner/my_application.cc
.
First, add the necessary includes to the top of the file, just after #include <flutter_linux/flutter_linux.h>
:
#include <math.h>
#include <upower.h
Add an FlMethodChannel
to the _MyApplication
struct:
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
FlMethodChannel* battery_channel;
};
Make sure to clean it up in my_application_dispose
:
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
g_clear_object(&self->battery_channel);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
Edit the my_application_activate
method and initialize battery_channel
using the channel name samples.flutter.dev/battery
, just after the call to fl_register_plugins
:
static void my_application_activate(GApplication* application) {
// ...
fl_register_plugins(FL_PLUGIN_REGISTRY(self->view));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->battery_channel = fl_method_channel_new(
fl_engine_get_binary_messenger(fl_view_get_engine(view)),
"samples.flutter.dev/battery", FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(
self->battery_channel, battery_method_call_handler, self, nullptr);
gtk_widget_grab_focus(GTK_WIDGET(self->view));
}
Next, add the C code that uses the Linux battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Linux application.
Add the following as a new function at the top of my_application.cc
just after the G_DEFINE_TYPE
line:
static FlMethodResponse* get_battery_level() {
// Find the first available battery and report that.
g_autoptr(UpClient) up_client = up_client_new();
g_autoptr(GPtrArray) devices = up_client_get_devices2(up_client);
if (devices->len == 0) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
"UNAVAILABLE", "Device does not have a battery.", nullptr));
}
UpDevice* device = UP_DEVICE(g_ptr_array_index(devices, 0));
double percentage = 0;
g_object_get(device, "percentage", &percentage, nullptr);
g_autoptr(FlValue) result =
fl_value_new_int(static_cast<int64_t>(round(percentage)));
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
Finally, add the battery_method_call_handler
function referenced in the earlier call to fl_method_channel_set_method_call_handler
. You need to handle a single platform method, getBatteryLevel
, so test for that in the method_call
argument. The implementation of this function calls the Linux code written in the previous step. If an unknown method is called, report that instead.
Add the following code after the get_battery_level
function:
static void battery_method_call_handler(FlMethodChannel* channel,
FlMethodCall* method_call,
gpointer user_data) {
g_autoptr(FlMethodResponse) response = nullptr;
if (strcmp(fl_method_call_get_name(method_call), "getBatteryLevel") == 0) {
response = get_battery_level();
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
g_autoptr(GError) error = nullptr;
if (!fl_method_call_respond(method_call, response, &error)) {
g_warning("Failed to send response: %s", error->message);
}
}
You should now be able to run the application on Linux. If your device doesn't have a battery, it displays 'Battery level not available'.
This example shows how to fetch and display the current battery level across Android, iOS, Windows, macOS, and Linux using a single method call: getBatteryLevel()
.
You can place platform-specific code directly in your app, or package it into reusable plugins. The dual use of platform channels works the same in both cases.
Step 1: Create a new app project#
Start by creating a new app:
In a terminal run:
flutter create batterylevel
By default, our template supports writing Android code using Kotlin, or iOS code using Swift. To use Java or Objective-C, use the -i
and/or -a
flags:
In a terminal run:
flutter create -i objc -a java batterylevel
Step 2: Create the Flutter platform client#
The app's State
class holds the current app state. Extend that to hold the current battery state.
First, construct the channel. Use a MethodChannel
with a single platform method that returns the battery level.
The client and host sides of a channel are connected through a channel name passed in the channel constructor. All channel names used in a single app must be unique; prefix the channel name with a unique 'domain prefix', for example: samples.flutter.dev/battery
.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class _MyHomePageState extends State<MyHomePage> {
static const platform = MethodChannel('samples.flutter.dev/battery');
// Get battery level.
Next, invoke a method on the method channel, specifying the concrete method to call using the String
identifier getBatteryLevel
. The call might fail—for example, if the platform doesn't support the platform API (such as when running in a simulator), so wrap the invokeMethod
call in a try-catch statement.
Use the returned result to update the user interface state in _batteryLevel
inside setState
.
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final result = await platform.invokeMethod<int>('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
Finally, replace the build
method from the template to contain a small user interface that displays the battery state in a string, and a button for refreshing the value.
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: _getBatteryLevel,
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
Step 3: Add an Android platform-specific implementation#
Start by opening the Android host portion of your Flutter app in Android Studio:
Start Android Studio
Select the menu item File > Open...
Navigate to the directory holding your Flutter app, and select the android folder inside it. Click OK.
Open the file
MainActivity.kt
located in the kotlin folder in the Project view.
Inside the configureFlutterEngine()
method, create a MethodChannel
and call setMethodCallHandler()
. Make sure to use the same channel name as was used on the Flutter client side.
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
}
}
Add the Android Kotlin code that uses the Android battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Android app.
First, add the needed imports at the top of the file:
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
Next, add the following method in the MainActivity
class, below the configureFlutterEngine()
method:
private fun getBatteryLevel(): Int {
val batteryLevel: Int
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
} else {
val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
}
return batteryLevel
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Android code written in the previous step, and returns a response for both the success and error cases using the result
argument. If an unknown method is called, report that instead.
Remove the following code:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
// This method is invoked on the main thread.
// TODO
}
And replace with the following:
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
// This method is invoked on the main thread.
call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
if (batteryLevel != -1) {
result.success(batteryLevel)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
} else {
result.notImplemented()
}
}
Step 4: Add an iOS platform-specific implementation#
Swift | Objective-C
Start by opening the iOS host portion of your Flutter app in Xcode:Start Xcode.Select the menu item File > Open....Navigate to the directory holding your Flutter app, and select the ios folder inside it. Click OK.Add support for Swift in the standard template setup that uses Objective-C:Expand Runner > Runner in the Project navigator.Open the file AppDelegate.swift
located under Runner > Runner in the Project navigator.Override the application:didFinishLaunchingWithOptions:
function and create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
:
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
// Handle battery messages.
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Next, add the iOS Swift code that uses the iOS battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native iOS app.
Add the following as a new method at the bottom of AppDelegate.swift
:
private func receiveBatteryLevel(result: FlutterResult) {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
if device.batteryState == UIDevice.BatteryState.unknown {
result(FlutterError(code: "UNAVAILABLE",
message: "Battery level not available.",
details: nil))
} else {
result(Int(device.batteryLevel * 100))
}
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the iOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
// This method is invoked on the UI thread.
guard call.method == "getBatteryLevel" else {
result(FlutterMethodNotImplemented)
return
}
self?.receiveBatteryLevel(result: result)
})
You should now be able to run the app on iOS. If using the iOS Simulator, note that it doesn't support battery APIs, and the app displays 'Battery level not available'.
Step 5: Add a Windows platform-specific implementation#
Start by opening the Windows host portion of your Flutter app in Visual Studio:
Run
flutter build windows
in your project directory once to generate the Visual Studio solution file.Start Visual Studio.
Select Open a project or solution.
Navigate to the directory holding your Flutter app, then into the build folder, then the windows folder, then select the
batterylevel.sln
file. Click Open.
Add the C++ implementation of the platform channel method:
Expand batterylevel > Source Files in the Solution Explorer.
Open the file
flutter_window.cpp
.
First, add the necessary includes to the top of the file, just after #include "flutter_window.h"
:
#include <flutter/event_channel.h>
#include <flutter/event_sink.h>
#include <flutter/event_stream_handler_functions.h>
#include <flutter/method_channel.h>
#include <flutter/standard_method_codec.h>
#include <windows.h>
#include <memory
Edit the FlutterWindow::OnCreate
method and create a flutter::MethodChannel
tied to the channel name samples.flutter.dev/battery
:
bool FlutterWindow::OnCreate() {
// ...
RegisterPlugins(flutter_controller_->engine());
flutter::MethodChannel<> channel(
flutter_controller_->engine()->messenger(), "samples.flutter.dev/battery",
&flutter::StandardMethodCodec::GetInstance());
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
Next, add the C++ code that uses the Windows battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Windows application.
Add the following as a new function at the top of flutter_window.cpp
just after the #include
section:
static int GetBatteryLevel() {
SYSTEM_POWER_STATUS status;
if (GetSystemPowerStatus(&status) == 0 || status.BatteryLifePercent == 255) {
return -1;
}
return status.BatteryLifePercent;
}
Finally, complete the setMethodCallHandler()
method added earlier. You need to handle a single platform method, getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the Windows code written in the previous step. If an unknown method is called, report that instead.
Remove the following code:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
// TODO
And replace with the following:
channel.SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<>> result) {
if (call.method_name() == "getBatteryLevel") {
int battery_level = GetBatteryLevel();
if (battery_level != -1) {
result->Success(battery_level);
} else {
result->Error("UNAVAILABLE", "Battery level not available.");
}
} else {
result->NotImplemented()
You should now be able to run the application on Windows. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 6: Add a macOS platform-specific implementation#
Start by opening the macOS host portion of your Flutter app in Xcode:
Start Xcode.
Select the menu item File > Open....
Navigate to the directory holding your Flutter app, and select the macos folder inside it. Click OK.
Add the Swift implementation of the platform channel method:
Expand Runner > Runner in the Project navigator.
Open the file
MainFlutterWindow.swift
located under Runner > Runner in the Project navigator.
First, add the necessary import to the top of the file, just after import FlutterMacOS
:
import IOKit.ps
Create a FlutterMethodChannel
tied to the channel name samples.flutter.dev/battery
in the awakeFromNib
method:
override func awakeFromNib() {
// ...
self.setFrame(windowFrame, display: true)
let batteryChannel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: flutterViewController.engine.binaryMessenger)
batteryChannel.setMethodCallHandler { (call, result) in
// This method is invoked on the UI thread.
// Handle battery messages.
}
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
}
Next, add the macOS Swift code that uses the IOKit battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native macOS app.
Add the following as a new method at the bottom of MainFlutterWindow.swift
:
private func getBatteryLevel() -> Int? {
let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()
let sources: Array<CFTypeRef> = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array
if let source = sources.first {
let description =
IOPSGetPowerSourceDescription(info, source).takeUnretainedValue() as! [String: AnyObject]
if let level = description[kIOPSCurrentCapacityKey] as? Int {
Finally, complete the setMethodCallHandler
method added earlier. You need to handle a single platform method getBatteryLevel()
, so test for that in the call
argument. The implementation of this platform method calls the macOS code written in the previous step. If an unknown method is called, report that instead.
batteryChannel.setMethodCallHandler { (call, result) in
switch call.method {
case "getBatteryLevel":
guard let level = getBatteryLevel() else {
result(
FlutterError(
code: "UNAVAILABLE",
message: "Battery level not available",
details: nil))
return
}
result(level)
default:
result(FlutterMethodNotImplemented)
}
}
You should now be able to run the application on macOS. If your device doesn't have a battery, it displays 'Battery level not available'.
Step 7: Add a Linux platform-specific implementation#
For this example you need to install the upower
developer headers. This is likely available from your distribution, for example with:
sudo apt install libupower-glib-dev
Start by opening the Linux host portion of your Flutter app in the editor of your choice. The instructions below are for Visual Studio Code with the "C/C++" and "CMake" extensions installed, but can be adjusted for other IDEs.
Launch Visual Studio Code.
Open the linux directory inside your project.
Choose Yes in the prompt asking:
Would you like to configure project "linux"?
. This enables C++ autocomplete.Open the file
runner/my_application.cc
.
First, add the necessary includes to the top of the file, just after #include <flutter_linux/flutter_linux.h>
:
#include <math.h>
#include <upower.h
Add an FlMethodChannel
to the _MyApplication
struct:
struct _MyApplication {
GtkApplication parent_instance;
char** dart_entrypoint_arguments;
FlMethodChannel* battery_channel;
};
Make sure to clean it up in my_application_dispose
:
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
g_clear_object(&self->battery_channel);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
}
Edit the my_application_activate
method and initialize battery_channel
using the channel name samples.flutter.dev/battery
, just after the call to fl_register_plugins
:
static void my_application_activate(GApplication* application) {
// ...
fl_register_plugins(FL_PLUGIN_REGISTRY(self->view));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
self->battery_channel = fl_method_channel_new(
fl_engine_get_binary_messenger(fl_view_get_engine(view)),
"samples.flutter.dev/battery", FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(
self->battery_channel, battery_method_call_handler, self, nullptr);
gtk_widget_grab_focus(GTK_WIDGET(self->view));
}
Next, add the C code that uses the Linux battery APIs to retrieve the battery level. This code is exactly the same as you would write in a native Linux application.
Add the following as a new function at the top of my_application.cc
just after the G_DEFINE_TYPE
line:
static FlMethodResponse* get_battery_level() {
// Find the first available battery and report that.
g_autoptr(UpClient) up_client = up_client_new();
g_autoptr(GPtrArray) devices = up_client_get_devices2(up_client);
if (devices->len == 0) {
return FL_METHOD_RESPONSE(fl_method_error_response_new(
"UNAVAILABLE", "Device does not have a battery.", nullptr));
}
UpDevice* device = UP_DEVICE(g_ptr_array_index(devices, 0));
double percentage = 0;
g_object_get(device, "percentage", &percentage, nullptr);
g_autoptr(FlValue) result =
fl_value_new_int(static_cast<int64_t>(round(percentage)));
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
}
Finally, add the battery_method_call_handler
function referenced in the earlier call to fl_method_channel_set_method_call_handler
. You need to handle a single platform method, getBatteryLevel
, so test for that in the method_call
argument. The implementation of this function calls the Linux code written in the previous step. If an unknown method is called, report that instead.
Add the following code after the get_battery_level
function:
static void battery_method_call_handler(FlMethodChannel* channel,
FlMethodCall* method_call,
gpointer user_data) {
g_autoptr(FlMethodResponse) response = nullptr;
if (strcmp(fl_method_call_get_name(method_call), "getBatteryLevel") == 0) {
response = get_battery_level();
} else {
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
}
g_autoptr(GError) error = nullptr;
if (!fl_method_call_respond(method_call, response, &error)) {
g_warning("Failed to send response: %s", error->message);
}
}
You should now be able to run the application on Linux. If your device doesn't have a battery, it displays 'Battery level not available'.
Call platform-specific code using the Pigeon package
Call platform-specific code using the Pigeon package
Call platform-specific code using the Pigeon package
Call platform-specific code using the Pigeon package
You can use the Pigeon
package as an alternative to Flutter's platform channel APIs to generate code that sends messages in a structured, type-safe manner. The workflow for Pigeon looks like this:
The Flutter app sends structured type-safe messages to its host, the non-Dart portion of the app, over a platform channel.
The host listens on the platform channel, and receives the message. It then calls into any number of platform-specific APIs using the native programming language and sends a response back to the client, the Flutter portion of the app.
Using this package eliminates the need to match strings between host and client for the names and data types of messages. It supports nested classes, grouping messages into APIs, generation of asynchronous wrapper code, and sending messages in either direction. The generated code is readable and guarantees there are no conflicts between multiple clients of different versions.
With Pigeon, the messaging protocol is defined in a subset of Dart that then generates messaging code for Android, iOS, macOS, or Windows. For example:
import 'package:pigeon/pigeon.dart';
class SearchRequest {
final String query;
SearchRequest({required this.query});
}
class SearchReply {
final String result;
SearchReply({required this.result});
}
@HostApi()
abstract class Api {
@async
SearchReply search(SearchRequest request);
}
import 'generated_pigeon.dart';
Future<void> onClick() async {
SearchRequest request = SearchRequest(query: 'test');
Api api = SomeApi();
SearchReply reply = await api.search(request);
print('reply: ${reply.result}');
}
You can find a complete example and more information on the pigeon
page on pub.dev.
You can use the Pigeon
package as an alternative to Flutter's platform channel APIs to generate code that sends messages in a structured, type-safe manner. The workflow for Pigeon looks like this:
The Flutter app sends structured type-safe messages to its host, the non-Dart portion of the app, over a platform channel.
The host listens on the platform channel, and receives the message. It then calls into any number of platform-specific APIs using the native programming language and sends a response back to the client, the Flutter portion of the app.
Using this package eliminates the need to match strings between host and client for the names and data types of messages. It supports nested classes, grouping messages into APIs, generation of asynchronous wrapper code, and sending messages in either direction. The generated code is readable and guarantees there are no conflicts between multiple clients of different versions.
With Pigeon, the messaging protocol is defined in a subset of Dart that then generates messaging code for Android, iOS, macOS, or Windows. For example:
import 'package:pigeon/pigeon.dart';
class SearchRequest {
final String query;
SearchRequest({required this.query});
}
class SearchReply {
final String result;
SearchReply({required this.result});
}
@HostApi()
abstract class Api {
@async
SearchReply search(SearchRequest request);
}
import 'generated_pigeon.dart';
Future<void> onClick() async {
SearchRequest request = SearchRequest(query: 'test');
Api api = SomeApi();
SearchReply reply = await api.search(request);
print('reply: ${reply.result}');
}
You can find a complete example and more information on the pigeon
page on pub.dev.
You can use the Pigeon
package as an alternative to Flutter's platform channel APIs to generate code that sends messages in a structured, type-safe manner. The workflow for Pigeon looks like this:
The Flutter app sends structured type-safe messages to its host, the non-Dart portion of the app, over a platform channel.
The host listens on the platform channel, and receives the message. It then calls into any number of platform-specific APIs using the native programming language and sends a response back to the client, the Flutter portion of the app.
Using this package eliminates the need to match strings between host and client for the names and data types of messages. It supports nested classes, grouping messages into APIs, generation of asynchronous wrapper code, and sending messages in either direction. The generated code is readable and guarantees there are no conflicts between multiple clients of different versions.
With Pigeon, the messaging protocol is defined in a subset of Dart that then generates messaging code for Android, iOS, macOS, or Windows. For example:
import 'package:pigeon/pigeon.dart';
class SearchRequest {
final String query;
SearchRequest({required this.query});
}
class SearchReply {
final String result;
SearchReply({required this.result});
}
@HostApi()
abstract class Api {
@async
SearchReply search(SearchRequest request);
}
import 'generated_pigeon.dart';
Future<void> onClick() async {
SearchRequest request = SearchRequest(query: 'test');
Api api = SomeApi();
SearchReply reply = await api.search(request);
print('reply: ${reply.result}');
}
You can find a complete example and more information on the pigeon
page on pub.dev.
You can use the Pigeon
package as an alternative to Flutter's platform channel APIs to generate code that sends messages in a structured, type-safe manner. The workflow for Pigeon looks like this:
The Flutter app sends structured type-safe messages to its host, the non-Dart portion of the app, over a platform channel.
The host listens on the platform channel, and receives the message. It then calls into any number of platform-specific APIs using the native programming language and sends a response back to the client, the Flutter portion of the app.
Using this package eliminates the need to match strings between host and client for the names and data types of messages. It supports nested classes, grouping messages into APIs, generation of asynchronous wrapper code, and sending messages in either direction. The generated code is readable and guarantees there are no conflicts between multiple clients of different versions.
With Pigeon, the messaging protocol is defined in a subset of Dart that then generates messaging code for Android, iOS, macOS, or Windows. For example:
import 'package:pigeon/pigeon.dart';
class SearchRequest {
final String query;
SearchRequest({required this.query});
}
class SearchReply {
final String result;
SearchReply({required this.result});
}
@HostApi()
abstract class Api {
@async
SearchReply search(SearchRequest request);
}
import 'generated_pigeon.dart';
Future<void> onClick() async {
SearchRequest request = SearchRequest(query: 'test');
Api api = SomeApi();
SearchReply reply = await api.search(request);
print('reply: ${reply.result}');
}
You can find a complete example and more information on the pigeon
page on pub.dev.
Channels and platform threading
Channels and platform threading
Channels and platform threading
Channels and platform threading
When invoking channels on the platform side destined for Flutter, invoke them on the platform's main thread. When invoking channels in Flutter destined for the platform side, either invoke them from any Isolate
that is the root Isolate
, or that is registered as a background Isolate
. The handlers for the platform side can execute on the platform's main thread or they can execute on a background thread if using a Task Queue. You can invoke the platform side handlers asynchronously and on any thread.
infoNote
On Android, the platform's main thread is sometimes called the "main thread", but it is technically defined as the UI thread. Annotate methods that need to be run on the UI thread with @UiThread
. On iOS, this thread is officially referred to as the main thread.
Use plugins and channels from a background isolate#
Plugins and channels can be used by any Isolate
, but that Isolate
has to be a root Isolate
(the one created by Flutter) or registered as a background Isolate
for a root Isolate
.
The following example shows how to register a background Isolate
in order to use a plugin from a background Isolate
.
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
void _isolateMain(RootIsolateToken rootIsolateToken) async {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
print(sharedPreferences.getBool('isDebug'));
}
void main() {
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
Execute channel handlers on a background thread (Android)#
In order for a channel's platform side handler to execute on a background thread on an Android app, you must use the Task Queue API.
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val taskQueue =
flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue()
channel = MethodChannel(flutterPluginBinding.binaryMessenger,
"com.example.foo",
StandardMethodCodec.INSTANCE,
taskQueue)
channel.setMethodCallHandler(this)
}
When invoking channels on the platform side destined for Flutter, invoke them on the platform's main thread. When invoking channels in Flutter destined for the platform side, either invoke them from any Isolate
that is the root Isolate
, or that is registered as a background Isolate
. The handlers for the platform side can execute on the platform's main thread or they can execute on a background thread if using a Task Queue. You can invoke the platform side handlers asynchronously and on any thread.
infoNote
On Android, the platform's main thread is sometimes called the "main thread", but it is technically defined as the UI thread. Annotate methods that need to be run on the UI thread with @UiThread
. On iOS, this thread is officially referred to as the main thread.
Use plugins and channels from a background isolate#
Plugins and channels can be used by any Isolate
, but that Isolate
has to be a root Isolate
(the one created by Flutter) or registered as a background Isolate
for a root Isolate
.
The following example shows how to register a background Isolate
in order to use a plugin from a background Isolate
.
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
void _isolateMain(RootIsolateToken rootIsolateToken) async {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
print(sharedPreferences.getBool('isDebug'));
}
void main() {
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
Execute channel handlers on a background thread (Android)#
In order for a channel's platform side handler to execute on a background thread on an Android app, you must use the Task Queue API.
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val taskQueue =
flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue()
channel = MethodChannel(flutterPluginBinding.binaryMessenger,
"com.example.foo",
StandardMethodCodec.INSTANCE,
taskQueue)
channel.setMethodCallHandler(this)
}
When invoking channels on the platform side destined for Flutter, invoke them on the platform's main thread. When invoking channels in Flutter destined for the platform side, either invoke them from any Isolate
that is the root Isolate
, or that is registered as a background Isolate
. The handlers for the platform side can execute on the platform's main thread or they can execute on a background thread if using a Task Queue. You can invoke the platform side handlers asynchronously and on any thread.
infoNote
On Android, the platform's main thread is sometimes called the "main thread", but it is technically defined as the UI thread. Annotate methods that need to be run on the UI thread with @UiThread
. On iOS, this thread is officially referred to as the main thread.
Use plugins and channels from a background isolate#
Plugins and channels can be used by any Isolate
, but that Isolate
has to be a root Isolate
(the one created by Flutter) or registered as a background Isolate
for a root Isolate
.
The following example shows how to register a background Isolate
in order to use a plugin from a background Isolate
.
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
void _isolateMain(RootIsolateToken rootIsolateToken) async {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
print(sharedPreferences.getBool('isDebug'));
}
void main() {
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
Execute channel handlers on a background thread (Android)#
In order for a channel's platform side handler to execute on a background thread on an Android app, you must use the Task Queue API.
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val taskQueue =
flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue()
channel = MethodChannel(flutterPluginBinding.binaryMessenger,
"com.example.foo",
StandardMethodCodec.INSTANCE,
taskQueue)
channel.setMethodCallHandler(this)
}
When invoking channels on the platform side destined for Flutter, invoke them on the platform's main thread. When invoking channels in Flutter destined for the platform side, either invoke them from any Isolate
that is the root Isolate
, or that is registered as a background Isolate
. The handlers for the platform side can execute on the platform's main thread or they can execute on a background thread if using a Task Queue. You can invoke the platform side handlers asynchronously and on any thread.
infoNote
On Android, the platform's main thread is sometimes called the "main thread", but it is technically defined as the UI thread. Annotate methods that need to be run on the UI thread with @UiThread
. On iOS, this thread is officially referred to as the main thread.
Use plugins and channels from a background isolate#
Plugins and channels can be used by any Isolate
, but that Isolate
has to be a root Isolate
(the one created by Flutter) or registered as a background Isolate
for a root Isolate
.
The following example shows how to register a background Isolate
in order to use a plugin from a background Isolate
.
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
void _isolateMain(RootIsolateToken rootIsolateToken) async {
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
print(sharedPreferences.getBool('isDebug'));
}
void main() {
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
Execute channel handlers on a background thread (Android)#
In order for a channel's platform side handler to execute on a background thread on an Android app, you must use the Task Queue API.
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
val taskQueue =
flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue()
channel = MethodChannel(flutterPluginBinding.binaryMessenger,
"com.example.foo",
StandardMethodCodec.INSTANCE,
taskQueue)
channel.setMethodCallHandler(this)
}
Execute channel handlers
Execute channel handlers
Execute channel handlers
Execute channel handlers
Execute channel handlers on a background thread (iOS)#
In order for a channel's platform side handler to execute on a background thread on an iOS app, you must use the Task Queue API.
SwiftObjective-C swift
public static func register(with registrar: FlutterPluginRegistrar) {
let taskQueue = registrar.messenger().makeBackgroundTaskQueue?()
let channel = FlutterMethodChannel(name: "com.example.foo",
binaryMessenger: registrar.messenger(),
codec: FlutterStandardMethodCodec.sharedInstance(),
taskQueue: taskQueue)
let instance = MyPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
Jump to the UI thread (Android)#
To comply with channels' UI thread requirement, you might need to jump from a background thread to Android's UI thread to execute a channel method. In Android, you can accomplish this by post()
ing a Runnable
to Android's UI thread Looper
, which causes the Runnable
to execute on the main thread at the next opportunity.
Handler(Looper.getMainLooper()).post {
// Call the desired channel message here.
}
Execute channel handlers on a background thread (iOS)#
In order for a channel's platform side handler to execute on a background thread on an iOS app, you must use the Task Queue API.
SwiftObjective-C swift
public static func register(with registrar: FlutterPluginRegistrar) {
let taskQueue = registrar.messenger().makeBackgroundTaskQueue?()
let channel = FlutterMethodChannel(name: "com.example.foo",
binaryMessenger: registrar.messenger(),
codec: FlutterStandardMethodCodec.sharedInstance(),
taskQueue: taskQueue)
let instance = MyPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
Jump to the UI thread (Android)#
To comply with channels' UI thread requirement, you might need to jump from a background thread to Android's UI thread to execute a channel method. In Android, you can accomplish this by post()
ing a Runnable
to Android's UI thread Looper
, which causes the Runnable
to execute on the main thread at the next opportunity.
Handler(Looper.getMainLooper()).post {
// Call the desired channel message here.
}
Execute channel handlers on a background thread (iOS)#
In order for a channel's platform side handler to execute on a background thread on an iOS app, you must use the Task Queue API.
SwiftObjective-C swift
public static func register(with registrar: FlutterPluginRegistrar) {
let taskQueue = registrar.messenger().makeBackgroundTaskQueue?()
let channel = FlutterMethodChannel(name: "com.example.foo",
binaryMessenger: registrar.messenger(),
codec: FlutterStandardMethodCodec.sharedInstance(),
taskQueue: taskQueue)
let instance = MyPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
Jump to the UI thread (Android)#
To comply with channels' UI thread requirement, you might need to jump from a background thread to Android's UI thread to execute a channel method. In Android, you can accomplish this by post()
ing a Runnable
to Android's UI thread Looper
, which causes the Runnable
to execute on the main thread at the next opportunity.
Handler(Looper.getMainLooper()).post {
// Call the desired channel message here.
}
Execute channel handlers on a background thread (iOS)#
In order for a channel's platform side handler to execute on a background thread on an iOS app, you must use the Task Queue API.
SwiftObjective-C swift
public static func register(with registrar: FlutterPluginRegistrar) {
let taskQueue = registrar.messenger().makeBackgroundTaskQueue?()
let channel = FlutterMethodChannel(name: "com.example.foo",
binaryMessenger: registrar.messenger(),
codec: FlutterStandardMethodCodec.sharedInstance(),
taskQueue: taskQueue)
let instance = MyPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
Jump to the UI thread (Android)#
To comply with channels' UI thread requirement, you might need to jump from a background thread to Android's UI thread to execute a channel method. In Android, you can accomplish this by post()
ing a Runnable
to Android's UI thread Looper
, which causes the Runnable
to execute on the main thread at the next opportunity.
Handler(Looper.getMainLooper()).post {
// Call the desired channel message here.
}
Supplementals
Supplementals
Supplementals
Supplementals
Common channels and codecs#
The following is a list of some common platform channel APIs that you can use to write platform-specific code:
MethodChannel
for Flutter: A named channel that you can use to communicate with platform plugins using asynchronous method calls. By default this channel uses theStandardMessageCodec
codec. This channel is not type safe, which means calling and receiving messages depends on the host and client declaring the same arguments and data types in order for messages to work.BasicMessageChannel
for Flutter: A named channel that supports basic, asynchronous message passing, using a supported message codec. Not type safe.Engine Embedder APIs for Platforms: These platform-specific APIs contain platform-specific channel APIs.
You can create your own codec or use an existing one. The following is a list of some existing codecs that you can use with platform-specific code:
StandardMessageCodec
: A commonly used message codec that encodes and decodes a wide range of data types into a platform-agnostic binary format for transmission across platform channels. The serialization and deserialization of values to and from messages happens automatically when you send and receive values. For a list of supported data types, see Platform channel data types support.BinaryCodec
: A message codec that passes raw binary data between the Dart side of your Flutter app and the native platform side. It does not perform any higher-level encoding or decoding of data structures.StringCodec
: A message codec that encodes and decodes strings, using UTF-8 encoding.JSONMessageCodec
: A message codec that encodes and decodes JSON-formatted data, using UTF-8 encoding.FirestoreMessageCodec
: A message codec that handles the exchange of messages sent across the platform channel between your Flutter app and the native Firebase Firestore SDKs (on Android and iOS).
Separate platform-specific code from UI code#
If you expect to use your platform-specific code in multiple Flutter apps, you might consider separating the code into a platform plugin located in a directory outside your main application. See developing packages for details.
Common channels and codecs#
The following is a list of some common platform channel APIs that you can use to write platform-specific code:
MethodChannel
for Flutter: A named channel that you can use to communicate with platform plugins using asynchronous method calls. By default this channel uses theStandardMessageCodec
codec. This channel is not type safe, which means calling and receiving messages depends on the host and client declaring the same arguments and data types in order for messages to work.BasicMessageChannel
for Flutter: A named channel that supports basic, asynchronous message passing, using a supported message codec. Not type safe.Engine Embedder APIs for Platforms: These platform-specific APIs contain platform-specific channel APIs.
You can create your own codec or use an existing one. The following is a list of some existing codecs that you can use with platform-specific code:
StandardMessageCodec
: A commonly used message codec that encodes and decodes a wide range of data types into a platform-agnostic binary format for transmission across platform channels. The serialization and deserialization of values to and from messages happens automatically when you send and receive values. For a list of supported data types, see Platform channel data types support.BinaryCodec
: A message codec that passes raw binary data between the Dart side of your Flutter app and the native platform side. It does not perform any higher-level encoding or decoding of data structures.StringCodec
: A message codec that encodes and decodes strings, using UTF-8 encoding.JSONMessageCodec
: A message codec that encodes and decodes JSON-formatted data, using UTF-8 encoding.FirestoreMessageCodec
: A message codec that handles the exchange of messages sent across the platform channel between your Flutter app and the native Firebase Firestore SDKs (on Android and iOS).
Separate platform-specific code from UI code#
If you expect to use your platform-specific code in multiple Flutter apps, you might consider separating the code into a platform plugin located in a directory outside your main application. See developing packages for details.
Common channels and codecs#
The following is a list of some common platform channel APIs that you can use to write platform-specific code:
MethodChannel
for Flutter: A named channel that you can use to communicate with platform plugins using asynchronous method calls. By default this channel uses theStandardMessageCodec
codec. This channel is not type safe, which means calling and receiving messages depends on the host and client declaring the same arguments and data types in order for messages to work.BasicMessageChannel
for Flutter: A named channel that supports basic, asynchronous message passing, using a supported message codec. Not type safe.Engine Embedder APIs for Platforms: These platform-specific APIs contain platform-specific channel APIs.
You can create your own codec or use an existing one. The following is a list of some existing codecs that you can use with platform-specific code:
StandardMessageCodec
: A commonly used message codec that encodes and decodes a wide range of data types into a platform-agnostic binary format for transmission across platform channels. The serialization and deserialization of values to and from messages happens automatically when you send and receive values. For a list of supported data types, see Platform channel data types support.BinaryCodec
: A message codec that passes raw binary data between the Dart side of your Flutter app and the native platform side. It does not perform any higher-level encoding or decoding of data structures.StringCodec
: A message codec that encodes and decodes strings, using UTF-8 encoding.JSONMessageCodec
: A message codec that encodes and decodes JSON-formatted data, using UTF-8 encoding.FirestoreMessageCodec
: A message codec that handles the exchange of messages sent across the platform channel between your Flutter app and the native Firebase Firestore SDKs (on Android and iOS).
Separate platform-specific code from UI code#
If you expect to use your platform-specific code in multiple Flutter apps, you might consider separating the code into a platform plugin located in a directory outside your main application. See developing packages for details.
Common channels and codecs#
The following is a list of some common platform channel APIs that you can use to write platform-specific code:
MethodChannel
for Flutter: A named channel that you can use to communicate with platform plugins using asynchronous method calls. By default this channel uses theStandardMessageCodec
codec. This channel is not type safe, which means calling and receiving messages depends on the host and client declaring the same arguments and data types in order for messages to work.BasicMessageChannel
for Flutter: A named channel that supports basic, asynchronous message passing, using a supported message codec. Not type safe.Engine Embedder APIs for Platforms: These platform-specific APIs contain platform-specific channel APIs.
You can create your own codec or use an existing one. The following is a list of some existing codecs that you can use with platform-specific code:
StandardMessageCodec
: A commonly used message codec that encodes and decodes a wide range of data types into a platform-agnostic binary format for transmission across platform channels. The serialization and deserialization of values to and from messages happens automatically when you send and receive values. For a list of supported data types, see Platform channel data types support.BinaryCodec
: A message codec that passes raw binary data between the Dart side of your Flutter app and the native platform side. It does not perform any higher-level encoding or decoding of data structures.StringCodec
: A message codec that encodes and decodes strings, using UTF-8 encoding.JSONMessageCodec
: A message codec that encodes and decodes JSON-formatted data, using UTF-8 encoding.FirestoreMessageCodec
: A message codec that handles the exchange of messages sent across the platform channel between your Flutter app and the native Firebase Firestore SDKs (on Android and iOS).
Separate platform-specific code from UI code#
If you expect to use your platform-specific code in multiple Flutter apps, you might consider separating the code into a platform plugin located in a directory outside your main application. See developing packages for details.
Table of content
© 2021-25 Blupx Private Limited.
All rights reserved.
© 2021-25 Blupx Private Limited.
All rights reserved.
© 2021-25 Blupx Private Limited.
All rights reserved.