Understanding Method Channels in Flutter


Flutter is an amazing framework that allows us to create apps for multiple platforms using a single codebase. However, sometimes we need to use native code (Swift for iOS, Kotlin/Java for Android) to access platform-specific features that Flutter doesn’t provide directly. This is where Method Channels come into play!
What is a Method Channel?
A Method Channel is a way for Flutter to communicate with the native platform (Android or iOS). Think of it like a bridge that lets Flutter send messages to native code and get responses back.
This is useful when you want to:
Use device sensors (like accelerometer, gyroscope)
Access native features like Bluetooth, camera, or battery info
Perform tasks that require platform-specific code
How Does a Method Channel Work?
Flutter sends a request (method call) to the native platform.
The native platform executes the required task.
The native platform sends a response back to Flutter.
Flutter and native code communicate using a common channel name, which ensures they are talking to the same method channel.
Example: Getting Battery Level Using Method Channels
Let’s create a simple example where Flutter asks the native platform for the device's battery level.
Step 1: Setup the Method Channel in Flutter
Open your Flutter project and go to main.dart
. Add the following code:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static const platform = MethodChannel('battery_channel');
String _batteryLevel = 'Unknown battery level';
Future<void> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level: $result%';
} on PlatformException catch (e) {
batteryLevel = 'Failed to get battery level: ${e.message}';
}
setState(() {
_batteryLevel = batteryLevel;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Method Channel Example')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_batteryLevel),
ElevatedButton(onPressed: _getBatteryLevel, child: Text('Get Battery Level')),
],
),
),
),
);
}
}
Step 2: Handle Native Code (Android)
Go to android/app/src/main/kotlin/com/example/yourapp/MainActivity.kt
and modify it as follows:
package com.example.method_channel_example
import android.os.Bundle
import android.os.BatteryManager
import android.content.Context
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "battery_channel"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "getBatteryLevel") {
val batteryLevel = getBatteryLevel()
result.success(batteryLevel)
} else {
result.notImplemented()
}
}
}
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
}
Step 3: Handle Native Code (iOS)
For iOS, go to ios/Runner/AppDelegate.swift
and modify it as follows:
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(name: "battery_channel",
binaryMessenger: controller.binaryMessenger)
batteryChannel.setMethodCallHandler { (call, result) in
if call.method == "getBatteryLevel" {
result(self.getBatteryLevel())
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func getBatteryLevel() -> Int {
let device = UIDevice.current
device.isBatteryMonitoringEnabled = true
return Int(device.batteryLevel * 100)
}
}
Running the App
Now, run your Flutter app on an Android or iOS device. When you click “Get Battery Level”, the app will communicate with the native platform to fetch the battery level and display it.
Summary
Method Channels help Flutter communicate with native code.
We define a MethodChannel in Flutter using
MethodChannel(channelName)
.We handle the method call in native Android (Kotlin/Java) and iOS (Swift) code.
The result is sent back to Flutter, allowing us to use native functionalities.
This is just one example! You can use Method Channels for Bluetooth, camera, notifications, file storage, and much more!
You can check the full code here.
Hope this article makes Method Channels easy to understand for you. 🚀 Happy coding! 🎯
Subscribe to my newsletter
Read articles from Amarjit Mallick directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Amarjit Mallick
Amarjit Mallick
I am a passionate Flutter developer, enthusiastic about creating delightful and performant mobile applications. My journey involves exploring the intricacies of UI/UX design and crafting seamless user experiences.