Google的新的多平台框架Flutter一直在吸引粉丝。 越来越多的人对此技术感兴趣,并在宠物和商业项目中尝试了该技术。 RuNet上出现了越来越多的文章和示例,但是不久前,我注意到,与Medium不同,本文的评论文章主要是关于该技术的整体及其最新版本中介绍的技术的优点或新颖性,而这些文章和文章主要出现在Habré上。 关于具体案例的文本很少。 因此,我认为有必要纠正当前情况。 我不是从最常见的情况开始,而是经常使用的深度链接。

最近,我的任务是使用深层链接启动Flutter应用程序。 我不得不翻阅文档和实验,以充分了解如何在Flutter中使用它们。 在本文中,我汇总了结果,以便使那些面对相同任务的人更容易理解。
深度链接是使用户能够导航到iOS或Android上的移动应用程序中的特定内容的URL。 这意味着我们必须跟踪应用程序的打开方式:以标准方式或使用链接,此外,转换完成后,该应用程序可能已经打开。 因此,我们需要在运行的应用程序的后台跟踪点击率。 让我们看看如何在Flutter中做到最好。
首先是配置
要在本机开发中使用深层链接,您需要在项目中准备适当的配置。 对于Flutter应用程序,此操作与本机程序完全相同。
的iOS
在Apple生态系统中有两种创建此类链接的方法:“自定义URL方案”和“通用链接”。
- 自定义URL方案 -允许您使用自定义方案,而不管指定了哪个主机。 这种方法是最简单的,但是有细微差别:您必须确保该方案是唯一的,此外,如果没有安装的应用程序,该链接将不起作用。 如果使用自定义URL方案,则可以使用以下链接:
your_scheme://any_host
- 通用链接是一种稍微复杂一些的方法。 它们仅允许您使用https方案和特定主机,但是您必须确认使用此主机的权利,为此,您需要将文件放置在服务器上-apple-app-site-association。 通用链接使您有机会在以下URL上运行该应用程序:
https://your_host
,如果没有安装的应用程序,它将提供从商店中安装它或在浏览器中打开链接。
例如,我使用“自定义URL方案”方法,因为它更简单。 将以下内容添加到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生态系统还具有两种链接大致相同属性的方式:
- 深度链接 -(就像iOS中的自定义URL方案一样)允许您使用自定义方案,无论指定了哪个主机。
- 应用程序链接 -仅允许您使用https方案和特定主机(与iOS中的通用链接一样)使用,并且还需要通过在Digital Asset Links服务器上放置JSON文件来确认使用此主机的权利。
对于android,我还决定不使其复杂化,并使用了深层链接。 将此添加到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>
因此,我们为这两个平台的
poc
方案配置了应用程序,并将能够在其中处理URL
poc://deeplink.flutter.dev
烹饪平台频道
因此,每个平台的本机配置已准备就绪。 但是,除了配置之外,您还需要准备平台通道,因此本机部分将与Flutter交互。 同样,您需要准备针对Android和iOS的实现。
让我们从Android开始。 您仅需执行任何操作-如果应用程序正在通过Deep Link运行,则只需在onCreate方法中处理传入的Intent,创建MethodChannel并将URI传递给它即可。
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(); } }
在iOS中,一切都会有些不同,尽管通常是相同的:通过MethodChannel将URI传递给应用程序。 我决定在Swift上实现它,因为使用Objecttive-C对我来说不是很好)。 接下来是修改后的AppDelegate.swift
@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) }
因此,我们将通过Deep Link处理应用程序的启动。 但是,如果在应用程序已经运行时链接仍然存在,该怎么办? 此刻有必要考虑。
在Android中,为此,我们将重写onNewIntent方法并处理每个传入的意图。 如果这是单击链接,我们将通过专门创建的BroadcastReceiver将事件扔到为此创建的EventChannel中。
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) {
让我们在iOS部分中做同样的事情。 在Swift中,我们需要创建FlutterStreamHandler并处理在应用程序处于后台时收到的任何链接。 是时候再次更改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?
当我们将这两部分结合在一起时:用于启动应用程序的部分和用于后台应用程序的部分-我们将控制所有用户对Deep Links的点击。
在Flutter中处理深层链接
平台部分已经为此做好了准备,是时候继续进行Flutter部分了。 您可能知道,可以使用不同的架构方法在Flutter上创建应用程序。 已经有很多关于该主题的文章(
例如,这一篇 ),但就我个人而言,纯BLoC是最合适的方法。 因此,我将准备一个单独的BLoC,以处理这些链接。 结果,我们得到的代码绝对不依赖于UI,并且可以在任何方便的地方处理接收链接。
class DeepLinkBloc extends Bloc {
特别是对于那些以前没有使用BLoC和StreamBuilders的人,我将准备一个示例示例小部件,它可以与此BLoC一起使用。 该小部件基于StreamBuilder,该StreamBuilder会根据从流中接收到的事件来重建UI。
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}')))); } }, ); } }
塔达姆! 仅此而已。 现在一切正常!
要进行测试,请以三种不同的方式运行该应用程序。 手动并通过深度链接,首先使用URI
poc://deeplink.flutter.dev
,然后使用
poc://deeplink.flutter.dev/parameter
。 以下是发生的情况的屏幕截图:

还有其他使用深度链接的方法。 例如,您可以为此使用
Firebase动态链接 。 关于如何在Flutter上使用它们,有
一篇很棒的文章 。 还有一个现成的
“ uni-links”库用于连接深层链接-您可以使用它。 而且,如果您不想依赖第三方库,则可以始终实现自己的库。 希望我的文章能对您有所帮助!
源代码
上面示例的源代码可以在
这里查看 。
一些有用的信息
如果您到目前为止阅读了这篇文章,那么您很可能对Flutter开发感兴趣。 我想谈谈可能对您有用的几种资源。 不久前,创建了一对与Flutter开发直接相关的俄语播客。 我建议订阅:
Flutter Dev Podcast (
电报频道 ),我们在其中讨论Flutter开发的紧迫问题;以及
Mobile People Talks (
电报频道 ,在这里我们从不同的角度原则上讨论移动开发问题)。 领先的Mobile People Talks是iOS,Android,ReactNative和Flutter。
