Le nouveau cadre multiplateforme de Google, Flutter, gagne régulièrement des fans. De plus en plus de gens sont intéressés par cette technologie et l'essaient aussi bien dans des projets familiers que commerciaux. De plus en plus d'articles et d'exemples apparaissent dans RuNet, mais il y a quelque temps, j'ai remarqué que, contrairement à Medium, les articles de revue principalement sur la technologie dans son ensemble et ses avantages ou nouveautés présentés dans la dernière version prédominent sur Habré. Il existe peu de textes sur des cas spécifiques. J'ai donc décidé qu'il était nécessaire de corriger la situation actuelle. Je ne vais pas commencer par le cas le plus courant, mais assez souvent utilisé - Deep Links.

Récemment, j'ai eu la tâche de lancer une application Flutter à l'aide de liens profonds. J'ai dû fouiller dans la documentation et expérimenter pour avoir une idée adéquate de la façon de travailler avec eux dans Flutter. Dans cet article, j'ai agrégé les résultats, afin que ceux qui sont confrontés à la même tâche, il soit plus facile à comprendre.
Les liens profonds sont des URL qui permettent aux utilisateurs de naviguer vers un contenu spécifique dans une application mobile sur iOS ou Android. Cela signifie que nous devons suivre la façon dont l'application a été ouverte: de manière standard ou en utilisant un lien, et en plus, l'application peut déjà être ouverte lorsque la transition a été terminée. Nous devons donc suivre les clics et en arrière-plan d'une application en cours d'exécution. Voyons comment mieux faire cela dans Flutter.
La première chose est la configuration
Pour utiliser Deep Links dans le développement natif, vous devez préparer la configuration appropriée dans le projet. Pour une application Flutter, cela se fait exactement de la même manière que dans le natif.
iOS
Il existe deux façons de créer de tels liens dans l'écosystème Apple: les «schémas d'URL personnalisés» et les «liens universels».
- Schémas d'URL personnalisés - vous permettent d'utiliser un schéma personnalisé, quel que soit l'hôte spécifié. Cette approche est la plus simple, mais il y a des nuances: vous devez être sûr que le schéma est unique et, en outre, le lien ne fonctionnera pas sans application installée. Si vous utilisez des schémas d'URL personnalisés, vous pouvez utiliser des liens comme:
your_scheme://any_host
- Universal Links est une approche légèrement plus complexe. Ils vous permettent de travailler uniquement avec le schéma https et avec un hôte spécifique, mais vous devez confirmer les droits d'utilisation de cet hôte, pour lequel vous devez placer le fichier sur le serveur - apple-app-site-association. Les liens universels vous donnent la possibilité d'exécuter l'application à l'URL:
https://your_host
, et en l'absence d'une application installée vous proposera de l'installer depuis la boutique ou d'ouvrir le lien dans un navigateur.
Par exemple, j'utilise l'approche des schémas d'URL personnalisés, car elle est plus simple. Ajoutez la pièce suivante au fichier Info.plist:
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLName</key> <string>deeplink.flutter.dev</string> <key>CFBundleURLSchemes</key> <array> <string>poc</string> </array> </dict> </array>
Android
L'écosystème Android a également deux façons de créer des liens avec à peu près les mêmes propriétés:
- Les liens profonds - (tout comme les schémas d'URL personnalisés dans iOS) vous permettent d'utiliser un schéma personnalisé quel que soit l'hôte spécifié.
- Liens d'application - vous permettent de travailler uniquement avec le schéma https et avec un hôte spécifique (tout comme Universal Links dans iOS), et vous devez également confirmer les droits d'utilisation de cet hôte en plaçant un fichier JSON sur le serveur Digital Asset Links.
Pour Android, j'ai également décidé de ne pas le compliquer et j'ai utilisé Deep Links. Ajoutez ceci à AndroidManifest.xml:
<intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="poc" android:host="deeplink.flutter.dev" /> </intent-filter>
Ainsi, nous avons configuré des applications pour les deux plates-formes pour les schémas
poc
et nous pourrons y traiter l'URL
poc://deeplink.flutter.dev
Chaînes de plate-forme de cuisson
Ainsi, la configuration native de chaque plate-forme est prête. Mais en plus de la configuration, vous devez préparer des canaux de plate-forme, grâce auxquels la partie native interagira avec Flutter. Et encore une fois, vous devez préparer votre implémentation pour Android et iOS.
Commençons par Android. Il vous suffit de ne rien faire - il suffit de traiter l'intention entrante dans la méthode onCreate, de créer un MethodChannel et de lui transmettre l'URI si l'application s'exécute via Deep Link.
private static final String CHANNEL = "poc.deeplink.flutter.dev/channel"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); Intent intent = getIntent(); Uri data = intent.getData(); new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( new MethodChannel.MethodCallHandler() { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { if (call.method.equals("initialLink")) { if (startString != null) { result.success(startString); } } } }); if (data != null) { startString = data.toString(); } }
Sous iOS, tout sera un peu différent, bien qu'en général la même chose: passer l'URI à l'application via le MethodChannel. J'ai décidé de l'implémenter sur Swift, car avec Objecttive-C, les choses ne sont pas très bonnes pour moi)). Ensuite, le AppDelegate.swift modifié
@UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { private var methodChannel: FlutterMethodChannel? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? ) -> Bool { let controller = window.rootViewController as! FlutterViewController methodChannel = FlutterMethodChannel(name: "poc.deeplink.flutter.dev/channel", binaryMessenger: controller) methodChannel?.setMethodCallHandler({ (call: FlutterMethodCall, result: FlutterResult) in guard call.method == "initialLink" else { result(FlutterMethodNotImplemented) return } }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) }
Nous allons donc gérer le lancement de l'application via Deep Link. Mais que faire si le lien suivait alors que l'application était déjà en cours d'exécution? Il faut prendre en compte ce moment.
Dans Android, pour cela, nous remplacerons la méthode onNewIntent et traiterons chaque intention entrante. S'il s'agit d'un clic sur un lien, nous lançons un événement dans le EventChannel créé à cet effet via un BroadcastReceiver spécialement créé.
private static final String EVENTS = "poc.deeplink.flutter.dev/events"; private BroadcastReceiver linksReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GeneratedPluginRegistrant.registerWith(this); new EventChannel(getFlutterView(), EVENTS).setStreamHandler( new EventChannel.StreamHandler() { @Override public void onListen(Object args, final EventChannel.EventSink events) { linksReceiver = createChangeReceiver(events); } @Override public void onCancel(Object args) { linksReceiver = null; } } ); } @Override public void onNewIntent(Intent intent){ super.onNewIntent(intent); if(intent.getAction() == android.content.Intent.ACTION_VIEW && linksReceiver != null) { linksReceiver.onReceive(this.getApplicationContext(), intent); } } private BroadcastReceiver createChangeReceiver(final EventChannel.EventSink events) { return new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) {
Faisons de même dans la partie iOS. Dans Swift, nous devons créer un FlutterStreamHandler et traiter tout lien que nous recevons lorsque l'application est en arrière-plan. Il est temps de changer encore un peu AppDelegate.swift
@UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { private var eventChannel: FlutterEventChannel? private let linkStreamHandler = LinkStreamHandler() override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? ) -> Bool { let controller = window.rootViewController as! FlutterViewController eventChannel = FlutterEventChannel(name: "poc.deeplink.flutter.dev/events", binaryMessenger: controller) GeneratedPluginRegistrant.register(with: self) eventChannel?.setStreamHandler(linkStreamHandler) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } override func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { eventChannel?.setStreamHandler(linkStreamHandler) return linkStreamHandler.handleLink(url.absoluteString) } } class LinkStreamHandler:NSObject, FlutterStreamHandler { var eventSink: FlutterEventSink?
Lorsque nous combinons les deux parties: la partie pour lancer l'application et la partie pour l'application en arrière-plan - nous contrôlerons tous les clics des utilisateurs sur Deep Links.
Traitement des liens profonds dans Flutter
La partie plateforme est prête pour cela, il est temps de passer à la partie Flutter. Comme vous le savez probablement, vous pouvez créer des applications sur flutter en utilisant différentes approches architecturales. De nombreux articles ont déjà été écrits sur ce sujet (
par exemple celui-ci ), mais personnellement, il me semble que le BLoC pur est l'approche la plus appropriée. Par conséquent, je préparerai un BLoC distinct qui gérera ces liens. En conséquence, nous obtenons un code qui n'est absolument pas lié à l'interface utilisateur et peut gérer la réception de liens lorsque cela est possible.
class DeepLinkBloc extends Bloc {
Surtout pour ceux qui n'avaient aucune expérience avec BLoC et StreamBuilders, je vais préparer un exemple de widget qui fonctionnera avec ce BLoC. Le widget est basé sur StreamBuilder, qui reconstruit l'interface utilisateur en fonction des événements reçus du flux.
class PocWidget extends StatelessWidget { @override Widget build(BuildContext context) { DeepLinkBloc _bloc = Provider.of<DeepLinkBloc>(context); return StreamBuilder<String>( stream: _bloc.state, builder: (context, snapshot) { if (!snapshot.hasData) { return Container( child: Center( child: Text('No deep link was used '))); } else { return Container( child: Center( child: Padding( padding: EdgeInsets.all(20.0), child: Text('Redirected: ${snapshot.data}')))); } }, ); } }
Tadam! C’est tout. Maintenant, tout fonctionne!
Pour tester, exécutez l'application de trois manières différentes. Manuellement et via Deep Links, d'abord avec l'URI
poc://deeplink.flutter.dev
, puis avec
poc://deeplink.flutter.dev/parameter
. Voici des captures d'écran de ce qui s'est passé:

Il existe d'autres façons de travailler avec Deep Links. Par exemple, vous pouvez utiliser des
liens dynamiques Firebase pour cela. Il existe
un excellent article sur la façon de les utiliser avec Flutter. Il existe également une
bibliothèque «uni-links» prête à l'emploi pour la connexion de Deep Links - vous pouvez l'utiliser. Et si vous ne voulez pas dépendre de bibliothèques tierces, vous pouvez toujours implémenter les vôtres. J'espère que mon article vous y aidera!
Code source
Le code source de l'exemple ci-dessus peut être
consulté ici .
Quelques informations utiles
Si vous avez lu l'article jusqu'à ce point, alors vous êtes probablement intéressé par le développement de Flutter). Je veux parler de plusieurs ressources qui pourraient vous être utiles. Il n'y a pas si longtemps, une paire de podcasts russophones a été créée et est directement liée au développement de Flutter. Je vous recommande de vous y abonner:
Flutter Dev Podcast (
canal télégramme ), où nous discutons des problèmes brûlants du développement Flutter, et
Mobile People Talks (
canal télégramme ), où nous discutons des problèmes de développement mobile en principe, de différents points de vue. Les principales conversations sur les personnes mobiles sont iOS, Android, ReactNative et Flutter.
