A nova estrutura multiplataforma do Google, Flutter, está conquistando cada vez mais fãs. Mais e mais pessoas estão interessadas nessa tecnologia e a experimentam em projetos comerciais e de animais de estimação. Mais e mais artigos e exemplos aparecem no RuNet, mas há algum tempo notei que, diferentemente do Medium, os artigos de revisão principalmente sobre a tecnologia como um todo e suas vantagens ou novidades apresentadas na versão mais recente predominam em Habré. Existem poucos textos sobre casos específicos. Portanto, decidi que era necessário corrigir a situação atual. Começarei não com o caso mais comum, mas com bastante frequência - Deep Links.

Recentemente, tive a tarefa de iniciar um aplicativo Flutter usando links diretos. Eu tive que vasculhar a documentação e experimentar para ter uma idéia adequada de como trabalhar com eles no Flutter. Neste artigo, agreguei os resultados, para que aqueles que enfrentam a mesma tarefa fiquem mais fáceis de entender.
Links diretos são URLs que permitem aos usuários navegar para conteúdo específico em um aplicativo móvel no iOS ou Android. Isso significa que devemos rastrear como o aplicativo foi aberto: da maneira padrão ou usando um link e, além disso, o aplicativo já pode estar aberto quando a transição foi concluída. Portanto, precisamos rastrear os cliques e o plano de fundo de um aplicativo em execução. Vamos ver a melhor maneira de fazer isso no Flutter.
A primeira coisa é configuração
Para usar os Deep Links no desenvolvimento nativo, você precisa preparar a configuração apropriada no projeto. Para um aplicativo Flutter, isso é feito exatamente da mesma maneira que no nativo.
iOS
Existem duas maneiras de criar esses links no ecossistema da Apple: "Esquemas de URL personalizados" e "Links universais".
- Esquemas de URL personalizados - permitem usar um esquema personalizado, independentemente do host especificado. Essa abordagem é a mais simples, mas existem nuances: você deve ter certeza de que o esquema é único e, além disso, o link não funcionará sem um aplicativo instalado. Se você usar esquemas de URL personalizados, poderá usar links como:
your_scheme://any_host
- O Universal Links é uma abordagem um pouco mais complexa. Eles permitem que você trabalhe apenas com o esquema https e com um host específico, mas é necessário confirmar os direitos de uso desse host, para os quais você precisa colocar o arquivo no servidor - apple-app-site-association. O Universal Links oferece a você a oportunidade de executar o aplicativo no URL:
https://your_host
e, na ausência de um aplicativo instalado, oferecerá instalá-lo na loja ou abrir o link em um navegador.
Por exemplo, eu uso a abordagem de esquemas de URL personalizados, pois é mais simples. Adicione a seguinte peça ao arquivo 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
O ecossistema do Android também tem duas maneiras de vincular aproximadamente as mesmas propriedades:
- Links diretos - (assim como os esquemas de URL personalizado no iOS) permitem que você use um esquema personalizado, independentemente do host especificado.
- App Links - eles permitem que você trabalhe apenas com o esquema https e com um host específico (assim como o Universal Links no iOS) e também precisa confirmar os direitos de uso desse host colocando um arquivo JSON no servidor Digital Asset Links.
Para o Android, também decidi não complicá-lo e usei o Deep Links. Adicione isso ao 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>
Assim, configuramos aplicativos para ambas as plataformas para esquemas de
poc
e poderemos processar o URL
poc://deeplink.flutter.dev
neles
poc://deeplink.flutter.dev
Canais da plataforma de cozinha
Portanto, a configuração nativa para cada plataforma está pronta. Além da configuração, você precisa preparar os Canais da Plataforma, graças aos quais a parte nativa irá interagir com o Flutter. E, novamente, você precisa preparar sua implementação para Android e iOS.
Vamos começar com o Android. Você só precisa fazer nada - apenas processe a intenção de entrada no método onCreate, crie um MethodChannel e passe o URI a ele se o aplicativo estiver sendo executado através do 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(); } }
No iOS, tudo será um pouco diferente, embora em geral a mesma coisa: passar o URI para o aplicativo através do MethodChannel. Decidi implementá-lo no Swift, já que com o Objecttive-C as coisas não são muito boas para mim)). Em seguida, é o AppDelegate.swift modificado
@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) }
Portanto, lidaremos com o lançamento do aplicativo através do Deep Link. Mas e se o link seguisse quando o aplicativo já estava em execução? É necessário levar em consideração esse momento.
No Android, para isso, substituiremos o método onNewIntent e processaremos cada intenção recebida. Se for um clique no link, lançaremos um evento no EventChannel criado para isso por meio de um BroadcastReceiver especialmente criado.
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) {
Vamos fazer o mesmo na parte do iOS. No Swift, precisamos criar um FlutterStreamHandler e processar qualquer link que recebermos enquanto o aplicativo estiver em segundo plano. É hora de mudar AppDelegate.swift um pouco novamente
@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?
Quando combinamos as duas partes: a parte para iniciar o aplicativo e a parte para o aplicativo em segundo plano - controlaremos todos os cliques do usuário nos Links diretos.
Processando links diretos no Flutter
A parte da plataforma está pronta para isso, é hora de passar para a parte Flutter. Como você provavelmente sabe, é possível criar aplicativos no flutter usando diferentes abordagens arquiteturais. Muitos artigos já foram escritos sobre esse tópico (
por exemplo, este ), mas, pessoalmente, parece-me que o BLoC puro é a abordagem mais adequada. Portanto, prepararei um BLoC separado que manipulará esses links. Como resultado, obtemos um código absolutamente não vinculado à interface do usuário e que pode lidar com o recebimento de links sempre que conveniente.
class DeepLinkBloc extends Bloc {
Especialmente para aqueles que não tinham experiência anterior com BLoC e StreamBuilders, prepararei um exemplo de widget que funcionará com este BLoC. O widget é baseado no StreamBuilder, que reconstrói a interface do usuário dependendo dos eventos recebidos do fluxo.
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! Isso é tudo. Agora tudo funciona!
Para testar, execute o aplicativo de três maneiras diferentes. Manualmente e por meio do Deep Links, primeiro com o URI
poc://deeplink.flutter.dev
e, em seguida, com
poc://deeplink.flutter.dev/parameter
. Aqui estão as capturas de tela do que aconteceu:

Existem outras maneiras de trabalhar com o Deep Links. Por exemplo, você pode usar os
Links dinâmicos do Firebase para isso. Há
um excelente artigo sobre como usá-los com o Flutter. Também existe uma
biblioteca 'uni-links' pronta para conectar o Deep Links - você pode usá-lo. E se você não quiser depender de bibliotecas de terceiros, sempre poderá implementar suas próprias. Espero que meu artigo o ajude com isso!
Código fonte
O código fonte do exemplo acima pode ser
visto aqui .
Algumas informações úteis
Se você leu o artigo até este ponto, provavelmente está interessado no desenvolvimento do Flutter). Quero falar sobre vários recursos que podem ser úteis para você. Há pouco tempo, foi criado um par de podcasts de língua russa diretamente relacionados ao desenvolvimento do Flutter. Eu recomendo assinar:
Flutter Dev Podcast (
canal de telegrama ), onde discutimos os problemas mais graves do desenvolvimento do Flutter, e
Mobile People Talks (
canal de telegrama ), onde discutimos os problemas do desenvolvimento móvel em princípio, sob diferentes pontos de vista. As principais palestras sobre celular são iOS, Android, ReactNative e Flutter.
