top of page

Using Method Channels to enable calls between native code and Flutter code


Using Method Channels to enable calls between native code and Flutter code

Flutter, a popular cross-platform development framework, allows developers to build high-performance applications with a single codebase. However, there are times when you need to integrate platform-specific functionality into your Flutter app. Method Channels provide a powerful mechanism to bridge the gap between Flutter and native code, enabling you to call native methods from Flutter and vice versa.


In this blog, we'll explore how to utilize Method Channels to invoke native code in both Android and iOS platforms from your Flutter app.


Prerequisites


To follow along with this tutorial, you should have a basic understanding of Flutter and have Flutter SDK installed on your machine.


Additionally, make sure you have the necessary tools and configurations set up for Android and iOS development, such as Android Studio and Xcode.


Implementing Method Channels in Flutter


Step 1: Create a Flutter Project Let's start by creating a new Flutter project. Open your terminal or command prompt and run the following command:

flutter create method_channel_demo
cd method_channel_demo

Step 2: Add Dependencies Open the pubspec.yaml file in your project's root directory and add the following dependencies:

dependencies:flutter:sdk: flutter
dev_dependencies:flutter_test:sdk: flutter

Save the file and run flutter pub get in your terminal to fetch the dependencies.


Step 3: Define the Native Method Channel Create a new Dart file named method_channel.dart in the lib directory. In this file, define a class called MethodChannelDemo that will encapsulate the native method channel communication. Add the following code:

import 'package:flutter/services.dart';

class MethodChannelDemo {
  static const platform = MethodChannel('method_channel_demo');

  static Future<String> getPlatformVersion() async {
    return await platform.invokeMethod('getPlatformVersion');
  }
}

In this code, we define a static platform object of type MethodChannel and associate it with the channel name 'method_channel_demo'. We also define a getPlatformVersion() method that invokes the native method 'getPlatformVersion' using the invokeMethod() function.


Step 4: Implement Native Code Next, let's implement the native code for both Android and iOS platforms.


For Android, open the MainActivity.kt file and import the necessary packages:

import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel

Inside the MainActivity class, override the configureFlutterEngine() method and register the method channel:

class MainActivity : FlutterActivity() {
    private val CHANNEL = "method_channel_demo"
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        GeneratedPluginRegistrant.registerWith(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                if (call.method == "getPlatformVersion") {
                    result.success("Android ${VERSION.RELEASE}")
                } else {
                    result.notImplemented()
                }
            }
    }
}

The code above sets up a method channel with the same name as defined in the Dart code. It handles the method call with a lambda function where we check the method name and return the Android platform version using the result.success() method.


For iOS, open the AppDelegate.swift file and import the necessary packages:

import UIKit
import Flutter
import UIKit.UIApplication
import UIKit.UIWindow

Inside the AppDelegate class, add the following code to register the method channel:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    private let CHANNEL = "method_channel_demo"
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        GeneratedPluginRegistrant.register(with: self)
        let controller = window?.rootViewController as! FlutterViewController
        let channel = FlutterMethodChannel(name: CHANNEL,
                                           binaryMessenger: controller.binaryMessenger)
        channel.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            if call.method == "getPlatformVersion" {
                result("iOS " + UIDevice.current.systemVersion)
            } else {
                result(FlutterMethodNotImplemented)
            }
        })

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

In this code, we create a method channel with the same name as defined in the Dart code. We handle the method call using a closure, check the method name, and return the iOS platform version using the result() method.


Step 5: Call Native Code from Flutter Now that we have set up the method channels and implemented the native code, let's invoke the native methods from Flutter.

Open the lib/main.dart file and replace its contents with the following code:

import 'package:flutter/material.dart';
import 'method_channel.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Method Channel Demo'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              FutureBuilder<String>(
                future: MethodChannelDemo.getPlatformVersion(),
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return Text('Platform version: ${snapshot.data}');
                  } else if (snapshot.hasError) {
                    return Text('Error: ${snapshot.error}');
                  }
                  return CircularProgressIndicator();
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

In this code, we import the method_channel.dart file and create a simple Flutter app with a centered column containing a FutureBuilder. The FutureBuilder calls the getPlatformVersion() method and displays the platform version once it's available.


Step 6: Run the App Finally, we're ready to run our app. Connect a physical device or start an emulator, then run the following command in your terminal:

flutter run

You have successfully implemented Method Channels to call native code in Android and iOS platforms from your Flutter app. You can now leverage this mechanism to access platform-specific APIs and extend the functionality of your Flutter applications.


Conclusion


In this tutorial, we explored how to utilize Method Channels to invoke native code in Android and iOS platforms from a Flutter app. We covered the steps required to set up the method channels, implemented the native code for Android and iOS, and demonstrated how to call native methods from Flutter. By leveraging Method Channels, Flutter developers can access platform-specific features and create powerful cross-platform applications. Happy coding!

Blog for Mobile App Developers, Testers and App Owners

 

This blog is from Finotes Team. Finotes is a lightweight mobile APM and bug detection tool for iOS and Android apps.

In this blog we talk about iOS and Android app development technologies, languages and frameworks like Java, Kotlin, Swift, Objective-C, Dart and Flutter that are used to build mobile apps. Read articles from Finotes team about good programming and software engineering practices, testing and QA practices, performance issues and bugs, concepts and techniques. 

bottom of page