Table of contents
1.
Introduction
2.
Architectural Overview
3.
Platform channel data types support and codecs
4.
Calling platform-specific Android code using platform channels
4.1.
Step 1: Create a new project for your app.
4.2.
Step 2: Create a client for the Flutter platform.
4.3.
Step 3: Add a platform-specific implementation for Android.
4.4.
Output
5.
Typesafe platform channels using Pigeon
5.1.
Example: Pigeon file
5.2.
Example: Dart Usage
6.
Frequently Asked Questions
6.1.
Is there a framework included with Flutter?
6.2.
How does Flutter execute my code on Android?
6.3.
What is Pigeon in flutter?
7.
Conclusion
Last Updated: Mar 27, 2024
Easy

Flutter Android Platform Specific Features

Author Nagendra
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Flutter is a fantastic framework that allows you to handle and access platform-specific functionality. This feature allows the developer to enhance the Flutter framework's capabilities. Camera, battery level, browser, and other platform-specific features may all be accessed easily through the framework. This feature allows the developer to enhance the Flutter framework's capabilities. In this article we will learn about the details of Flutter Android platform specific features. Let's start by having a quick discussion on the architectural overview. 

Architectural Overview

In order to keep the user interface responsive, messages and responses are transmitted asynchronously. Platform channels are used to send messages between the client (UI) and the host (platform), as seen in this diagram:

Source

MethodChannel allows clients to transmit messages that correspond to method calls. FlutterMethodChannel on iOS (MethodChanneliOS) and MethodChannel on Android (MethodChannelAndroid) enable receiving method calls and returning a result on the platform side. These classes make it possible to create a platform plugin with little 'boilerplate' code. Let's continue our learning with support for data types and codecs on the platform.

Platform channel data types support and codecs

The basic platform channels use a message codec that allows for efficient binary serialisation of simple JSON-like values like booleans, integers, Strings, byte buffers, and Lists and Maps of these (see StandardMessageCodec for details). When you transmit and receive values, the serialisation and deserialization of these data to and from messages happen automatically. The table below depicts how Dart values are received on the platform and vice versa:

The following code shows how to retrieve and display the current battery level using a platform-specific API. The Android BatteryManager API is used, as well as the iOS device. With a single platform message, getBatteryLevel(), the batteryLevel API can be used.

Calling platform-specific Android code using platform channels

Follow the following steps to call platform specific android code using the platform channels :

Step 1: Create a new project for your app.

  • Run the following code in the terminal
flutter create batterylevel

Step 2: Create a client for the Flutter platform.

  • The current app state is stored in the State class of the app. Extend that to keep the battery in its current state.
  • Create the channel first. Use a MethodChannel to return the battery level from a single platform method.
  • A channel's client and host sides are linked by a channel name specified in the channel constructor. All channel names in a single app must be unique; add a unique 'domain prefix' to the channel name, such as samples.flutter.dev/battery.

Code:

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.
  • Then, using the String identifier getBatteryLevel, call a method on the method channel, specifying the particular method to call. Wrap the invokeMethod call with a try-catch statement in case the call fails.
  • In setState, use the returned result to change the user interface state in _batteryLevel.

Code:

// Get battery level.
String _batteryLevel = 'Unknown battery level.';

Future<void> _getBatteryLevel() async {
  String batteryLevel;
  try {
    final int result = await platform.invokeMethod('getBatteryLevel');
    batteryLevel = 'Battery level at $result % .';
  } on PlatformException catch (e) {
    batteryLevel = "Failed to get battery level: '${e.message}'.";
  }
  setState(() {
    _batteryLevel = batteryLevel;
  });
}
  • Finally, replace the template's build method with a tiny user interface that shows the battery state as a string and a button to refresh the data.

Code:

@override
Widget build(BuildContext context) {
  return Material(
    child: Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          ElevatedButton(
            child: const Text('Get Battery Level'),
            onPressed: _getBatteryLevel,
          ),
          Text(_batteryLevel),
        ],
      ),
    ),
  );
}

Step 3: Add a platform-specific implementation for Android.

Open Android Studio and open the Android host component of your Flutter app:

  1. Launch Android Studio.
  2. Select File > Open... from the File menu.
  3. Select the android folder inside the directory where your Flutter app is stored. Click the OK button.
  4. In the Project view, open the file MainActivity.kt in the kotlin folder.

Create a MethodChannel and run setMethodCallHandler() inside the configureFlutterEngine() method . Make sure the channel name is the same as the one used on the Flutter client.

Code:

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 retrieves the battery level using the Android battery APIs. This code is identical to what you'd find in a native Android app. At begin, add the following imports to the top of the file:

Code:

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

After that, below the configureFlutterEngine() method, add the following method to the MainActivity class:

Code:

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 earlier added setMethodCallHandler() procedure. You only have to deal with one platform method, getBatteryLevel(), so make sure to test for it in the call argument. The result argument is used in the implementation of this platform method, which executes the Android code written in the previous step and returns a response for both success and error instances. Instead, if an unknown method is called, you should report it. Remove the following code from your program.

Code:

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      call, result ->
      // This method is invoked on the main thread.
    }

replace with:

Code:

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()
      }
    }

On Android, you should now be able to use the app. Set the battery level in the Extended Controls panel, accessible through the ... button in the toolbar.

Output

We will get the following output:

Typesafe platform channels using Pigeon

The previous example communicates between the host and client via MethodChannel, which isn't typesafe. Messages must be called and received with the same arguments and data types declared by both the host and the client. To generate code that sends messages in a structured, typesafe manner, you can use the Pigeon package instead of MethodChannel.
Pigeon uses a subset of Dart to define the messaging protocol, which then creates messaging code for Android or iOS. The pigeon page on pub.dev has a more detailed example and more details. It reduces the requirement to match strings for message names and datatypes between the host and client. It enables nested classes, message grouping into APIs, asynchronous wrapper code generation, and message transmission in both directions. The produced code is intelligible and ensures that numerous clients of various versions do not conflict.

Let's look at the example for the implementation of the pigeon file.

Example: Pigeon file

The following example illustrates implementing the pigeon file

import 'package:pigeon/pigeon.dart';
class SearchRequest {
  String query = '';
}

class SearchReply {
  String result = '';
}

@HostApi()
abstract class Api {
  Future search(SearchRequest request);
}

Example: Dart Usage

The following code explains the dart usage implementation

Code

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}');
}

Frequently Asked Questions

Is there a framework included with Flutter?

Yes! Flutter is pre-installed with a react-style framework. The framework for Flutter is built to be layered and configurable. Developers can use specific elements of the framework, or even completely alter the framework's higher levels.

How does Flutter execute my code on Android?

Android's NDK is used to compile the engine's C and C++ code. The Dart code is compiled into native, ARM, and x86 libraries ahead of time. Those libraries are included in an Android project called "runner," and the entire package is packaged as an.apk file. The Flutter library is loaded when the app is launched. The built Flutter and app code is in charge of rendering, input, and event handling, among other things. Many gaming engines operate in a similar manner.

What is Pigeon in flutter?

Pigeon is a code generating tool that makes type-safe, easier, and faster communication between Flutter and the host platform.

Conclusion

In this article, we have extensively discussed the details of Flutter Android Platform Specific Features along with the steps of calling platform-specific Android code using platform channels and details of Typesafe platform channels using Pigeon.

We hope that this blog has helped you enhance your knowledge regarding the Flutter Android Platform, and if you would like to learn more, check out our articles on Android Development. You can refer to our guided paths on the Coding Ninjas Studio platform to learn more about DSADBMSCompetitive ProgrammingPythonJavaJavaScript, etc. To practice and improve yourself in the interview, you can also check out Top 100 SQL problemsInterview experienceCoding interview questions, and the Ultimate guide path for interviews
Do upvote our blog to help other ninjas grow. 

Happy Coding!!

Live masterclass