Flutter Course — Message Communication between Flutter and iOS Native Code

Ali Akhtar
6 min readDec 15, 2022

It will be good if you read this blog before continue reading this one. Here are the few terms you should know

host → Native iOS code. Also called platform

Flutter portion → having flutter , dart code (also called client or UI )

platform channel → host and flutter portion communication channels send and receive message

  • Messages are passed between the client (UI) and host (platform) using platform channels as illustrated in this diagram: Messages and responses are passed asynchronously, to ensure the user interface remains responsive.
  • Even though Flutter sends messages to and from Dart asynchronously, whenever you invoke a channel method, you must invoke that method on the platform’s main thread.
Figure 1

Send Message to Native iOS Code

We will use the same code that we previously write here in this blog

First is to import these files

import 'package:flutter/services.dart';
import 'dart:async';

As shown in Figure 2 we did a few things

  • The client and host sides of a channel are connected through a channel name passed in the channel constructor MethodChannel. All channel names used in a single app must be unique; prefix the channel name with a unique ‘domain prefix’, for example: my_flutter_app.sendMessage. MethodChannel exists in import ‘package:flutter/services.dart’;
  • Next, invoke a method on the method channel, specifying the concrete method to call using the String identifier sendMessage. The call might fail—for example, if the platform doesn’t support the platform API (such as when running in a simulator), so wrap the invokeMethod call in a try-catch statement.
  • Use the returned result to update the user interface state in _messageText inside setState.
Figure 2

As shown in Figure 3 , when we tap on button _sendMessageToNativeCode method will execute that will communicate to our native code, Here we are showing Text(_messageText) return by our native code

Figure 3

As shown in Figure 4, here we handle code to receive message in native code send by our flutter portion code , here are the few things

  • create a FlutterMethodChannel tied to the channel name my_flutter_app.sendMessage
  • add the iOS Swift code callSomeMethod that currently return some hard coded message as a result , note since this code is async you can do whatever you want , call some API, logging everything
Figure 4

Send Message to Native iOS Code with param using manual technique

As shown in Figure 5, we are now passing Map<String, String> to native iOS code

Figure 5

As shown in Figure 6, we are now parsing these param value and combining these value to our hardcoded string and sending back to flutter code

Figure 6
Figure 7

Send Message to Native iOS Code with param using code generation libraries

To include json_serializable in your project, you need one regular dependency, and two dev dependencies. In short, dev dependencies are dependencies that are not included in our app source code—they are only used in the development environment.

As show in Figure 8, we add new packages, and save it , it will automatically install this package it not so you can run this command in terminal in flutter portion of git sub module

flutter pub get
Figure 8

As shown in Figure 9 , we created a class name User but we get some error. These errors are entirely normal and are simply because the generated code for the model class does not exist yet. To resolve this, run the code generator that generates the serialization boilerplate

Figure 9

By running flutter pub run build_runner build — delete-conflicting-outputs in the project root, you generate JSON serialization code for your models whenever they are needed. This triggers a one-time build that goes through the source files, picks the relevant ones, and generates the necessary serialization code for them. As shown in Figure 10 now error gone

remember, you have to run the build manually every time you make changes in your model classes.

flutter pub run build_runner build --delete-conflicting-outputs
Figure 10

As shown in Figure 11, we did few things

  • Create user model instance and populate with data
  • using import ‘dart:convert’; lib we convert model to json string that will pass to our native code
Figure 11

In our native code we parse it as String json and using swift Codable feature we convert it to User struct instance

Figure 12

Receive Message from Native iOS Code with param

As shown in Figure 13, we created a class FlutterNativeCodeListenerMethodChannel , this class will be responsible for handling message from native code

Figure 13

As shown in Figure 14, in main.dart main method we called configure method of FlutterNativeCodeListenerMethodChannel which basically create a MethodChannel and create a communication channel between native and flutter code

Figure 14

As shown in Figure 15, we called setMethodCallHandler to handle message from native code , we expect string from native code , then we convert it into Map<String, dynamic> and finally User using dependency we previously installed, after that we update _messageText property setState callback which re-render build method for this screen

Figure 15

As shown in Figure 16, when user tap on button in native code , previously we are opening flutter screen, now we send message to flutter screen then open that screen,

Note : In AppDelegate didFinishLaunchingWithOptions we already warm up our flutter engine which means that flutter screen is loaded but not showing and also main.dart loaded as well and our listener is setup, so when user tap on button it first send message to flutter code which already warm up so it receive first callback update _messageText property and then we open flutter controller which basically show directly the screen with proper message as shown in Figure 17

Figure 16
Figure 17

Update git submodule

As you see in previous blog, we are using flutter code using git submodule and we already see how we can update it, so in root folder of your native . platform code just run these commands

cd my_flutter_app
git add .
git commit -m 'communication between platform'
git push

Now run git pull and quit source tree and open again , in my case, now our git sub module now pointing to latest master, push that code,

git pull
cd my_flutter_app
flutter build ios --simulator
cd ..
pod install
Figure 18

You can checkout code until this here