Hello Devs,
You know flutter is now a hot topic. Learning flutter will always help in your future career.
In this article we will see how we can communicate between Flutter and Android.
Watch Video Tutorial
MethodChannel
Flutter communicate with Native using “MethodChannel“. So you have to create a method channel with a constant as identifier. Make sure you give the same constant name both on Native side(Android or iOS) and Flutter side as well…Here it is “MyNativeChannel“.
static const platform = const MethodChannel('MyNativeChannel');
Communication with native is always asynchronous, so the return type will always be of type “Future<>“. This is important to understand.
Below is a simple function that communicates with the Native side.
Future<void> getData() async { String message; try { message = await platform.invokeMethod('getData'); print(message); } on PlatformException catch (e) { message = "Failed to get data from native : '${e.message}'."; } setState(() { msg = message; }); }
The method should be async with the await callback.
await platform.invokeMethod(‘getData’) will send the method name “getData” to the native side and on the native side we can check what is the method name and execute that method.
Android Native side
We are trying to call a method that is defined in the Android native side and we know that the method name is “getData()”.
Open the MainActivity inside the android/src/your_package folder and create a method named “getData()“. Our method simply returns a String and looks like this.
private String getData() { return "Message From Android"; }
iOS Native side
NSString *from = call.arguments[@"from"];
Receive the event from Flutter Side
we have to define something on the Android side so that we can receive the call from Flutter side.
In your MainActivity
private static final String CHANNEL = "MyNativeChannel"; new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.equals("getData")) { result.success(getData()); } else { result.notImplemented(); } } });
When you call “getData()” function in Flutter that invokes the “MethodCallHandler” in the Android side and we will receive the function name with params from the Flutter side.
Complete MainActivity
The complete MainActivity would look like this.
import android.os.Bundle; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity { private static final String CHANNEL = "MyNativeChannel"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.equals("getData")) { result.success(getData()); } else { result.notImplemented(); } } }); } private String getData() { return "Message From Android"; } }
iOS Native Side
Go to AppDelegate.m file and replace the contents with this
#include "AppDelegate.h" #include "GeneratedPluginRegistrant.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { FlutterViewController *controller = (FlutterViewController *) self.window.rootViewController; FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"MyChannel" binaryMessenger:controller]; [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { NSString *from = call.arguments[@"from"]; if([@"myNativeFunction" isEqualToString:call.method]){ NSString *messageToFlutter = [self myNativeFunction]; messageToFlutter = [NSString stringWithFormat:@"%@, Back to %@", messageToFlutter, from]; result(messageToFlutter); } }]; [GeneratedPluginRegistrant registerWithRegistry:self]; // Override point for customization after application launch. return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -(NSString *) myNativeFunction{ return @"Message from iOS"; } @end
Note: Once you change the native side, you have restart the complete app to take effect.
Sending Params to Native
To Send params to the native side, you can modify the above flutter function like this.
Flutter Side
Future<void> getData() async { var params = <String, dynamic>{"from": "Flutter"}; String message; try { message = await platform.invokeMethod('getData', params); print(message); } on PlatformException catch (e) { message = "Failed to get data from native : '${e.message}'."; } setState(() { msg = message; }); }
Android Side
Map<String, Object> params = (Map<String, Object>) call.arguments; if (call.method.equals("getData")) { result.success(getData() + " Back to " + params.get("from")); } else { result.notImplemented(); }
Complete Flutter Code
The complete flutter code will look like this…
Our UI has a Text and a button that will invoke the getData function in the Android side.
When we get the result back from the Android side, we will show it in the Text in the Flutter side.
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class NativeDemoScreen extends StatefulWidget { @override _NativeDemoScreenState createState() => _NativeDemoScreenState(); } class _NativeDemoScreenState extends State<NativeDemoScreen> { static const platform = const MethodChannel('MyNativeChannel'); String msg = "No Message"; Future<void> getData() async { String message; try { message = await platform.invokeMethod('getData'); print(message); } on PlatformException catch (e) { message = "Failed to get data from native : '${e.message}'."; } setState(() { msg = message; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Native Demo"), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( msg, ), RaisedButton( child: Text("Call Native Function"), onPressed: getData, ) ], ), ), ); } }
It’s that simple. Let me know your feedback in the comments section below.
Thanks for reading.