Do you think IIT Guwahati certified course can help you in your career?
No
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:
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.
Step 3: Add a platform-specific implementation for Android.
Open Android Studio and open the Android host component of your Flutter app:
Launch Android Studio.
Select File > Open... from the File menu.
Select the android folder inside the directory where your Flutter app is stored. Click the OK button.
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:
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
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.