Understanding Method Channels in Flutter

Amarjit MallickAmarjit Mallick
3 min read

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?

  1. Flutter sends a request (method call) to the native platform.

  2. The native platform executes the required task.

  3. 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! 🎯

0
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.