12
Writing Platform‐Native Code

WHAT YOU WILL LEARN IN THIS CHAPTER

  • How to use platform channels to send and receive messages from the Flutter app to iOS and Android to access specific API functionality
  • How to write native platform code in iOS Swift and Android Kotlin to access device information
  • How to use MethodChannel to send messages from the Flutter app (on the client side)
  • How to use FlutterMethodChannel on iOS and MethodChannel on Android to receive calls and send back results (on the host side)

Platform channels give you the ability to use native features such as accessing the device information, GPS location, local notifications, local file system, sharing, and many more. In the “External Packages” section of Chapter 2, “Creating a Hello World App,” you learned how to use third‐party packages to add functionality to your apps. In this chapter, instead of relying on third‐party packages, you'll learn how to add custom functionality to your apps by using platform channels and writing the API code yourself. You'll build an app that asks the iOS and Android platforms to return the device information.

UNDERSTANDING PLATFORM CHANNELS

When you need to access platform‐specific APIs for iOS and Android, you use platform channels to send and receive messages. The Flutter app is the client, and the platform‐native code for iOS and Android is the host. If needed, it is also possible to have the platform‐native code to act as a client to call methods written in the Flutter app dart code.

The messages between the client and host are asynchronous, making sure that the UI remains responsive and not blocked. In Chapter 3, “Learning Dart Basics,” you learned that async functions perform time‐consuming operations without waiting for those operations to complete.

For the client side (Flutter app), you use the MethodChannel from an async method to send messages that contain the method call to be executed by the host side (iOS and Android). Once the host sends the response back, you can update the UI to display the information received.

For the host side, you use the FlutterMethodChannel on iOS and the MethodChannel on Android. Once the client call is received by the host, the native platform code executes the called method and then sends back the result (Figure 12.1).

Schematic of platform channel messages.

FIGURE 12.1: Platform channel messages

IMPLEMENTING THE CLIENT PLATFORM CHANNEL APP

To start communication from the Flutter client app to the iOS and Android platforms, you use the MethodChannel. A MethodChannel uses asynchronous method calls, and the channel requires a unique name. The channel name needs to be the same for the client as for the iOS and Android host. I suggest when you're creating a unique name for the channel that you use the app name, a domain prefix, and a descriptive name for the task such as platformchannel.companyname.com/deviceinfo.

// Name template
appname.domain.com/taskname
// Channel name
platformchannel.companyname.com/deviceinfo

At first, it seems that you are going overboard naming the channel, so why is it important for the name to be unique? If you have multiple named channels and they share the same name, they will cause conflicts with each other's messages.

To implement a channel, you create the MethodChannel through the default constructor by passing the unique channel name. The default constructor takes two arguments: the first is the channel name, and the second (which is optional) declares the default MethodCodec. The MethodCodec is the StandardMethodCodec, which uses Flutter's standard binary encoding; this means the serialization of data sent between the client and the host is automatically handled. Since you know the name of the channel at compile time and it will not change, you create the MethodChannel to a static const variable. Make sure you use the static keyword, or you will receive the error “Only static fields can be declared as const.”

static const platform = const
MethodChannel('platformchannel.companyname.com/deviceinfo');

Table 12.1 displays the supported value types for Dart, iOS, and Android.

TABLE 12.1: StandardMessageCodec‐Supported Value Types

DART iOS ANDROID
null nil null
bool NSNumber numberWithBool: java.lang.Boolean
int NSNumber numberWithInt: java.lang.Integer
int (bigger than 32 bits) NSNumber numberWithLong: java.lang.Long
double NSNumber numberWithDouble: java.lang.Double
String NSString java.lang.String
Uint8List FlutterStandardTypedData typedDataWithBytes: byte[]
Int32List FlutterStandardTypedData typedDataWithInt32: int[]
Int64List FlutterStandardTypedData typedDataWithInt64: long[]
Float64List FlutterStandardTypedData typedDataWithFloat64: double[]
List NSArray java.util.ArrayList
Map NSDictionary java.util.HashMap

To call and specify which method to execute on the iOS and Android host, you use the invokeMethod constructor to pass the method name as a String. The invokeMethod is called from inside a Future method since the call is asynchronous.

String deviceInfo = await platform.invokeMethod('getDeviceInfo');

Once the client and the iOS and Android platform channels are implemented, the Flutter client side of the app will display the appropriate device information depending on the device (Figure 12.2).

Screenshot of iOS and Android device information.

FIGURE 12.2: iOS and Android device information

IMPLEMENTING THE iOS HOST PLATFORM CHANNEL

The host is responsible for listening to incoming messages from the client. Once a message is received, the channel checks for a matching method name, executes the call method, and returns the appropriate result. In iOS, you use the FlutterMethodChannel for listening to incoming messages that take two parameters. The first parameter is the same platform channel name—'platformchannel.companyname.com/deviceinfo'—as the client. The second is the FlutterViewController, which is the main rootViewController of an iOS app. The rootViewController is the root view controller for the iOS app window that provides the content view of the window.

let flutterViewController: FlutterViewController = window?.rootViewController as!
FlutterViewController
let deviceInfoChannel = FlutterMethodChannel(name: "platformchannel.companyname.
com/deviceinfo", binaryMessenger: controller) 

You then use the setMethodCallHandler (Future handler) to set up a callback for a matching method name that executes the iOS native platform code. Once completed, it sends back the result to the client.

deviceInfoChannel.setMethodCallHandler({
  (call: FlutterMethodCall, result: FlutterResult) -> Void in
  // Check for incoming method call name and return a result
})

Both the FlutterMethodChannel and the setMethodCallHandler will be placed in the didFinishLaunchingWithOptions method of the iOS app AppDelegate.swift file. The didFinishLaunchingWithOptions is responsible for notifying the app delegate that the app launch process is almost done.

override func application(
 _ application: UIApplication,
 didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
 // Code
}

IMPLEMENTING THE ANDROID HOST PLATFORM CHANNEL

The host is responsible for listening for incoming messages from the client. Once a message is received, the channel checks for a matching method name, executes the call method, and returns the appropriate result. In Android, you use the MethodChannel to listen to incoming messages that take two parameters. The first parameter is the FlutterView, which extends the Activity of an Android app screen and, by using the flutterView variable as the parameter, is the same as calling the getFlutterView() (FlutterView) method from the FlutterActivity class. The second parameter is the same platform channel name platformchannel.companyname.com/deviceinfo as the client.

private val DEVICE_INFO_CHANNEL = "platformchannel.companyname.com/deviceinfo"
val methodChannel = MethodChannel(flutterView, DEVICE_INFO_CHANNEL)

You then use the setMethodCallHandler (Future handler) to set up a callback for a matching method name that executes the Android native platform code. Once completed, it sends the result to the client.

methodChannel.setMethodCallHandler { call, result ->
 // Check for incoming method call name and return a result
}

Both the MethodChannel and the setMethodCallHandler are placed in the onCreate method of the Android app's MainActivity.kt file. The onCreate is called when the activity is first created.

  override fun onCreate(savedInstanceState: Bundle?) {
   // Code
  }

SUMMARY

In this chapter, you learned how to access and communicate with iOS and Android platform‐specific API code by implementing platform channels. Platform channels are a way for the Flutter app (client) to communicate (messages) with iOS and Android (host) to request and receive results specific to the operating system (OS). For the UI to remain responsive and not blocked, the messages between the client and host are asynchronous.

To start communicating from the Flutter app (client), you learned to use the MethodChannel that sends messages that contain method calls to be executed by the iOS and Android (host) side. Once the host processes the method requested, it then sends back a response to the client, and you update the UI to display the information. The MethodChannel uses a unique name, and you used a combination of the app name, the domain prefix, and the task name like platformchannel.companyname.com/deviceinfo. To start the call from the client and specify which method to execute on the host, you learned to use the invokeMethod constructor, and it is called from inside a Future method since calls are asynchronous.

For the iOS and Android host, you learned to use the Flutter FlutterMethodChannel on iOS and the MethodChannel on Android to start receiving communications from the client. The host is responsible for listening to incoming messages from the client. You used the setMethodCallHandler to set up a callback for an incoming matching method name that executes on the native platform‐specific API code. Once the method completes, it sends the result to the client.

In the next chapter, you'll learn to use local persistence to save data locally to the device storage area.

image WHAT YOU LEARNED IN THIS CHAPTER

TOPIC KEY CONCEPTS
Implementing MethodChannel (client) This enables communication from the Flutter app (client) by sending messages that contain method calls to be executed by the iOS and Android (host).
Implementing invokeMethod (client) This initiates (invokes) and specifies which method call to execute on the host side.
Implementing FlutterMethodChannel (iOS host)
Implementing MethodChannel (Android host)
This enables communication from the host to receive method calls to execute platform‐specific API code.
Implementing setMethodCallHandler (iOS and Android host) This sets up a callback for incoming matching method names to execute platform‐specific API code.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset