MethodChannel dans Flutter
Le SDK de Flutter permet déjà de faire beaucoup de choses avec son langage Dart, mais parfois, nous avons besoin de communiquer avec la plateforme spécifique Android et iOS. Les Method Channels dans Flutter servent à ça. Nous allons voir deux exemples d’utilisation pour écrire du code personnalisé propre à la plateforme (Java et Swift).
La class Method Channel est un canal mis en place par défaut dans Flutter pour communiquer avec les plugins de la plateforme à l’aide de fonction asynchrone. Sans le savoir, vous utilisez sûrement les Method Channels à travers les packages que vous installez dans vos projets Flutter. Cette fois-ci, nous allons faire nos propres appels. Flutter facilite aussi la création de package pour rendre la vie plus facile à la communauté si votre fonction peut intéresser d’autres développeurs.
Transmettre des données
Nous allons prendre un exemple par défaut dans la documentation officielle de Flutter pour l’utilisation des Method Channels. Nous allons récupérer le niveau de batterie de notre smartphone. L’exemple fonctionne pour le système Android en Java, mais la logique est la même en Kotlin et en swift pour iOS.
Les points importants dans votre main.dart sont :
– l’import de services.dart et async
– l’ajout de la constante methodchannel (dans notre exemple battery)
– l’appel asynchone invokeMethod (dans notre exemple getBatteryLevel)
Pour avoir un affichage du résultat, nous avons mis en place un button avec un texte.
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 | import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter/services.dart'; void main() => runApp(MaterialApp(home: MyApp())); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: "hmwkapp", debugShowCheckedModeBanner: false, home: new MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State { static const platform1 = const MethodChannel('samples.flutter.io/battery'); String _batteryLevel = 'Unknown battery level.'; Future _getBatteryLevel() async { String batteryLevel; try { final int result = await platform1.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { _batteryLevel = batteryLevel; }); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: const Text('hmwkapp'), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ new Container( padding: new EdgeInsets.all(10.0), child: new MaterialButton( child: new Text(""+_batteryLevel), elevation: 5.0, height: 48.0, minWidth: 250.0, color: Colors.blue, textColor: Colors.white, onPressed: () { _getBatteryLevel(); } ), ), ], ), ) ); } } |
Côté Android, il faut ajouter notre canal dans notre fichier MainActivity. Créez-le si ce n’est pas déjà fait (attention, il peut être créé par défaut en Kotlin)
Dans notre fichier, nous allons déclarer notre channel sous le même nom que celui de notre fichier main.dart, ainsi que notre fonction getBatteryLevel
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 | package family.homework.hmwkapp; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugins.GeneratedPluginRegistrant; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; public class MainActivity extends FlutterActivity { private static final String CHANNEL1 = "samples.flutter.io/battery"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); new MethodChannel(getFlutterView(), CHANNEL1).setMethodCallHandler(new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("getBatteryLevel")) { int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success(batteryLevel); } else { result.error("UNAVAILABLE", "Battery level not available.", null); } } else { result.notImplemented(); } } }); } private int getBatteryLevel() { int batteryLevel = -1; if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE); batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); } else { Intent intent = new ContextWrapper(getApplicationContext()).registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); } return batteryLevel; } } |
Créer une vue spécifique natif
Maintenant que nous avons vu comment appeler une fonction spécifique à la plateforme en Flutter, nous allons voir comment créer une vue spécifique à la plateforme en Flutter.
Rien de plus compliqué, la base reste la même côté Flutter.
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 | import 'package:flutter/material.dart'; import 'dart:async'; import 'package:flutter/services.dart'; void main() => runApp(MaterialApp(home: MyApp())); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: "hmwkapp", debugShowCheckedModeBanner: false, home: new MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State { static const platform1 = const MethodChannel('samples.flutter.io/battery'); static const platform2 = const MethodChannel('samples.flutter.io/view'); String _batteryLevel = 'Unknown battery level.'; Future _getBatteryLevel() async { String batteryLevel; try { final int result = await platform1.invokeMethod('getBatteryLevel'); batteryLevel = 'Battery level at $result % .'; } on PlatformException catch (e) { batteryLevel = "Failed to get battery level: '${e.message}'."; } setState(() { _batteryLevel = batteryLevel; }); } Future _getNewActivity() async { try { await platform2.invokeMethod('getNewActivity'); } on PlatformException catch (e) { print(e.message); } } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: const Text('hmwkapp'), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ new Container( padding: new EdgeInsets.all(10.0), child: new MaterialButton( child: new Text(""+_batteryLevel), elevation: 5.0, height: 48.0, minWidth: 250.0, color: Colors.blue, textColor: Colors.white, onPressed: () { _getBatteryLevel(); } ), ), new Container( padding: new EdgeInsets.all(10.0), child: new MaterialButton( child: const Text("Go new layout"), elevation: 5.0, height: 48.0, minWidth: 250.0, color: Colors.blue, textColor: Colors.white, onPressed: () { _getNewActivity(); } ), ), ], ), ) ); } } |
Dans notre MainActivity, cette fois-ci notre fonction getNewActivity va ouvrir une nouvelle activity créée en java.
A vous de voir pour la création de votre class et de votre layout. Les développeurs Android n’auront pas de problèmes là-dessus.
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 | package family.homework.hmwkapp; import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugins.GeneratedPluginRegistrant; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.os.BatteryManager; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; public class MainActivity extends FlutterActivity { private static final String CHANNEL1 = "samples.flutter.io/battery"; private static final String CHANNEL2 = "samples.flutter.io/view"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); new MethodChannel(getFlutterView(), CHANNEL1).setMethodCallHandler(new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("getBatteryLevel")) { int batteryLevel = getBatteryLevel(); if (batteryLevel != -1) { result.success(batteryLevel); } else { result.error("UNAVAILABLE", "Battery level not available.", null); } } else { result.notImplemented(); } } }); new MethodChannel(getFlutterView(), CHANNEL2).setMethodCallHandler(new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("getNewActivity")) { getNewActivity(); } else { result.notImplemented(); } } }); } private int getBatteryLevel() { int batteryLevel = -1; if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE); batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); } else { Intent intent = new ContextWrapper(getApplicationContext()).registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); } return batteryLevel; } private void getNewActivity() { startActivity(new Intent( this, ActivitySpecifique.class)); } } |
Sous Android, n’oubliez pas de déclarer votre nouvelle activity dans votre fichier manifest.
1 2 3 4 | <pre><activity android:name=".ActivitySpecifique" android:windowSoftInputMode="adjustResize"> </activity></pre> |