Flutter, API natives et plugins (3/3)
C’est également en mai, qu’a éclos le système de plugin, un magnifique mécanisme permettant d’utiliser de manière quasi transparente des fonctionnalités natives depuis Flutter/Dart.
- Dans la première partie nous avons vu les bases de l’utilisation des MethodChannels.
- Dans la seconde partie nous avons vu comment créer un canal dédié à la speech recognition.
Nous allons maintenant voir comment modulariser ce code en créant un plugin Flutter pour iOS & Android.
Le système de plugin 😍
Les plugins Flutter sont gérés comme de simples packages (dépendances), via pub
le gestionnaire de packages de Dart.
Il suffit de déclarer la dépendance, de pub get
, et Flutter se charge de metre à jour le projet XCode et Android.
C’est tellement efficace et transparent,
que pour le moment je n’ai même pas encore eu à comprendre ce qu’était Graddle,
ni comment marche un podfile. Tout est géré par Flutter 😎🍹! ça laisse plus de temps pour développer l’appli :)
Même si le catalogue de plugins Flutter est encore embryonnaire, il propose déjà quelques outils utiles, voire nécessaires.
Utilisation
Pour utiliser un plugin :
-
on déclare la dépendance dans le pubspec.yaml du projet
dependencies: speech_recognition: "^0.2.0+1"
-
on importe le package
flutter packages get flutter build ios flutter build apk
-
on importe le package
import 'package:speech_recognition/speech_recognition.dart';
Et c’est tout !
En fait c’est tout, sauf si le plugin ne nécessite des permissions particulières, auquel cas il est nécessaire
d’éditer le fichier .plist
pour iOS, et le manifest.xml
pour Android.
Ensuite au moment ou on lance l’application, selon l’OS visé, Flutter installera et gérera les dépendances iOS/Android, de manière à ce que le code natif soit correctement mis à disposition.
L’équipe a pour cela mis en place un système d’auto-enregistrement des plugins qui marche à merveille, et permet, dans la plupart des cas, de ne pas avoir à toucher à xCode ou Android Studio. 🥂🍾
Création
Mais ça va encore plus loin ! :) Et là où ça tombe encore + de la machoire, c’est dans l’outillage pour la création de plugin. Je l’ai trouvé tellement efficace que j’en suis à 4 plugins publiés, alors qu’à la base, le moins que je puisse dire, c’est que ce n’était pas du tout ma spécialité, les plugins crossplatform :).
Flutter CLI propose une commande de génération de dossier de plugin, contenant un projet d’exemple préconfiguré pour l’utilisation du plugin créé :
flutter create -i swift --org bz.rxla --plugin mon_plugin
-i swift
: on souhaite utiliser Swift pour le code iOS, et pas ObjC défini par défaut-a kotlin
: si on souhaite utiliser Kotlin à la place du Java par défaut côté Android--org mon.domaine
: namespace du plugin--plugin mon_plugin
: le nom du plugin
On peut noter que la même commande avec un_projet
à la place de --plugin mon_plugin
générera un projet Flutter Swift/Java.
Côté Dart, le code généré est très simple, il contient uniquement la création d’un canal “dédié”. L’aspect le plus intéressant de ce système est l’auto-détection / installation des plugins “natifs” côté iOS et Android.
Flutter / Dart
Voici le code généré pour la partie Dart d’un plugin :
// plugin_demo/lib/plugin_demo.dart
class PluginDemo {
static const MethodChannel _channel =
const MethodChannel('plugin_demo');
static Future<String> get platformVersion =>
_channel.invokeMethod('getPlatformVersion');
}
iOS / Swift
L’utilisation de Swift ajoute une petite couche, avec la génération à la fois de fichiers ObjC PluginDemoPlugin.h & .m, et la class SwiftPluginDemoPlugin.
Le fichier SwiftPluginDemoPlugin.swift
contient le mécanisme qui permet l’auto-détection du plugin par une application :
il s’agit ici de la méthode statique register
, appelée par l’application au lancement
pour transmettre un FlutterPluginRegistrar
. Le rôle est du registrar est d’ajouter le canal créé par le plugin
à la liste des “récepteurs” des appels MethodChannel
, de manière à lui permettre de recevoir les appels qui lui sont destinés,
via sa méthode handle
.
// plugin_demo/ios/classes/SwiftPluginDemoPlugin.swift
public class SwiftPluginDemoPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "plugin_demo", binaryMessenger: registrar.messenger());
let instance = SwiftPluginDemoPlugin();
registrar.addMethodCallDelegate(instance, channel: channel);
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion);
}
}
Android / Java
On retrouve le même principe de registrar
côté Java pour Android.
// plugin_demo/android/src/main/java/mon/domaine/plugin_demo/PluginDemoPlugin.java
public class PluginDemoPlugin implements MethodCallHandler {
/**
* Plugin registration.
*/
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "plugin_demo");
channel.setMethodCallHandler(new PluginDemoPlugin());
}
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
} else {
result.notImplemented();
}
}
}
Implementation
A partir de là, il ne reste plus qu’à “bouger” l’implementation de classe SpeechRecognition vers le projet plugin, ainsi que le code Swift et Java associé. cf. speech_recognition plugin
Pour la publication sur pub.dartlang.com:
pub publish