Flutter is a very beautiful and powerful toolkit for creating an app for mobile, web and desktop with a single codebase. It supports both Android and iOS for mobile applications. But in some scenarios, you could face the requirement to code separately for Android or IOS.
Read about the variety of Flutter App Development Services offered by Mobikul
So, In this blog, we will learn how we can run the Flutter with Android Native Code.
You can also check the flutter doc for more detailed information.
Suppose you have created a new flutter application. So your main.dart file will look something like the below-provided example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( // This is the theme of your application. // // Try running your application with "flutter run". You'll see the // application has a blue toolbar. Then, without quitting the app, try // changing the primarySwatch below to Colors.green and then invoke // "hot reload" (press "r" in the console where you ran "flutter run", // or simply save your changes to "hot reload" in a Flutter IDE). // Notice that the counter didn't reset back to zero; the application // is not restarted. primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); // This widget is the home page of your application. It is stateful, meaning // that it has a State object (defined below) that contains fields that affect // how it looks. // This class is the configuration for the state. It holds the values (in this // case the title) provided by the parent (in this case the App widget) and // used by the build method of the State. Fields in a Widget subclass are // always marked "final". final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { // This call to setState tells the Flutter framework that something has // changed in this State, which causes it to rerun the build method below // so that the display can reflect the updated values. If we changed // _counter without calling setState(), then the build method would not be // called again, and so nothing would appear to happen. _counter++; }); } @override Widget build(BuildContext context) { // This method is rerun every time setState is called, for instance as done // by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( // Here we take the value from the MyHomePage object that was created by // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: Center( // Center is a layout widget. It takes a single child and positions it // in the middle of the parent. child: Column( // Column is also a layout widget. It takes a list of children and // arranges them vertically. By default, it sizes itself to fit its // children horizontally, and tries to be as tall as its parent. // // Invoke "debug painting" (press "p" in the console, choose the // "Toggle Debug Paint" action from the Flutter Inspector in Android // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) // to see the wireframe for each widget. // // Column has various properties to control how it sizes itself and // how it positions its children. Here we use mainAxisAlignment to // center the children vertically; the main axis here is the vertical // axis because Columns are vertical (the cross axis would be // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'Press the plus button to inrease counter', ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, child: Icon(Icons.add), ), // This trailing comma makes auto-formatting nicer for build methods. ); } } |
Now to call the Android native code we would have to use MethodChannel class. Let’s get some info on MethodChannel class.
According to the flutter doc “A named channel for communicating with platform plugins using asynchronous method calls.” So using this class you will be calling a function according to its name and the program will look for its implementation in the Android or the iOS code. How we can do this? Let’s look at some code.
Steps to perform
First, we need to create a MethodChannel. You can do that inside your _MyHomePageState class.
1 |
static const platform = const MethodChannel('example.mobikul/native'); |
and we will call the “turnOnWifi” function using its name.
1 2 3 4 5 6 7 |
Future<void> _turnOnWifi() async { try { platform.invokeMethod('turnOnWifi'); } on PlatformException catch (e) { debugPrint("Failed to turn on wifi: '${e.message}'."); } } |
As you can see in the above code segment, We have used the “platform” variable and invoked its invokeMethod function with the function name in native code. This will call the native function. Right now, It will give you an exception because if have not declared our turnOnWifi yet in the native code.
After doing all this, Now we will add the Android-specific code in our MainActivity.
For this, You need to open the Android host portion of your Flutter app in Android Studio.
- Start Android Studio
- Select the menu item File > Open…
- Navigate to the directory holding your Flutter app, and select the android folder inside it. Click OK.
- Open the file
MainActivity.kt
located in the kotlin folder in the Project view. (Note: If editing with Android Studio 2.3, note that the kotlin folder is shown as if named java.)
Here, you will have to declare the same channel that you have declared in your flutter part.
1 |
private val CHANNEL = "example.mobikul/native" |
then, inside you configureFlutterEngine function below the GeneratedPluginRegistrant.registerWith(flutterEngine) you will have to add a method call handler
1 2 3 4 5 |
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> // Note: this method is invoked on the main thread. // TODO } |
Now suppose you have a function that turns the WIFI on.
1 2 3 4 |
private fun turnOnWifi() { val wifiManager = this.applicationContext.getSystemService(WIFI_SERVICE) as WifiManager wifiManager.isWifiEnabled = true } |
You can call this function from the setMethodCallHandler and the below code segment will provide you an idea on how to do it.
1 2 3 4 5 6 7 |
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .setMethodCallHandler { call, result -> when (call.method) { "turnOnWifi" -> turnOnWifi() else -> result.notImplemented() } } |
We have used a when here which will check the call.method and will execute the line of code accordingly.
That’s all for this blog. Thank you very much. This is Vedesh Kumar signing off.