Uni Links

一个Flutter插件项目,用于处理App/深层链接(Android)和
通用链接和自定义URL方案(iOS)。

这些链接本质上是浏览器式的链接,可以激活您的应用,并且可能
包含您用于加载应用特定部分或
在网站(或其他应用)上继续特定用户活动的信息。

App Links和Universal Links是普通的https链接,因此,如果应用未安装
(或未正确设置),它们将在浏览器中加载,允许您
呈现网页以供进一步操作,例如安装应用。

请确保您已仔细阅读安装和使用指南,
特别是关于App/Universal Links(https方案)。

安装

要使用该插件,请将uni_links添加为
pubspec.yaml文件中的依赖项。.

0.5.0 主要变更

由于迁移到空安全,一些API已更改。这些更改
主要涉及函数变为getter,类型变为
显式可空。

示例包中的更改是如何升级到
此版本的良好示例。

Permission

Android和iOS需要在配置文件中声明链接的权限。

随时查看example目录中的示例应用,了解
深层链接(Android)和自定义URL方案(iOS)。

以下步骤不是Flutter特定的,而是平台特定的。您可能
可以在线找到更深入的指南,通过搜索Android上的App
Links或Deep Links;iOS上的Universal Links或Custom URL Schema。

对于 Android

Uni Links支持两种类型的Android链接:“App Links”和“Deep Links”。

  • App Links仅适用于https方案,并需要指定主机,以及
    一个托管文件 – assetlinks.json。请查看以下指南链接。
  • Deep Links可以具有任何自定义方案,并且不需要主机,也不需要
    托管文件。缺点是任何应用程序都可以声明一个方案+主机组合,所以
    请确保您的组合尽可能独特,例如HST0000001://host.com

您需要在android/app/src/main/AndroidManifest.xml中声明至少一个意图过滤器。

<manifest ...>
  <!-- ... other tags -->
  <application ...>
    <activity ...>
      <!-- ... other tags -->

      <!-- Deep Links -->
      <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
        <data
          android:scheme="[YOUR_SCHEME]"
          android:host="[YOUR_HOST]" />
      </intent-filter>

      <!-- App Links -->
      <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with https://YOUR_HOST -->
        <data
          android:scheme="https"
          android:host="[YOUR_HOST]" />
      </intent-filter>
    </activity>
  </application>
</manifest>

对于深度链接,android:host 属性是可选的。

为了进一步提高特异性,您可以添加android:pathPrefix属性。

<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST/NAME/NAME... -->
<!-- Accepts URIs that begin with       https://YOUR_HOST/NAME/NAME... -->
<!-- note that the leading "/" is required for pathPrefix -->
<data
  android:scheme="[YOUR_SCHEME_OR_HTTPS]"
  android:host="[YOUR_HOST]"
  android:pathPrefix="/[NAME][/NAME...]" />

更多信息请阅读
终极指南.
请密切注意指南中关于必需的/.well-known/assetlinks.json
文件的
App Links
部分。

Android开发者文档也是有关
Deep Links和App Links.

对于 iOS

的绝佳信息来源。

  • iOS中有两种链接:“Universal Links”和“Custom URL schemes”。
    Universal Links仅适用于https方案,并需要指定主机、
    entitlements(权限)和一个托管文件 – apple-app-site-association。请查看以下指南链接。
  • Custom URL schemes可以有…任何自定义方案,并且没有主机
    特异性、权限或托管文件。缺点是任何应用
    都可以声明任何方案,所以请确保您的方案尽可能独特,
    例如hst0000001myIncrediblyAwesomeScheme

您需要声明至少一个。

对于Universal Links,您需要添加或创建
com.apple.developer.associated-domains entitlement(权限),可以通过Xcode
(见下文)进行操作,或通过编辑(或创建并添加到Xcode)ios/Runner/Runner.entitlements文件。
部分。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <!-- ... other keys -->
  <key>com.apple.developer.associated-domains</key>
  <array>
    <string>applinks:[YOUR_HOST]</string>
  </array>
  <!-- ... other keys -->
</dict>
</plist>

这允许您的应用从https://YOUR_HOST链接启动。

在Xcode中创建entitlements文件:

  • 通过双击ios/Runner.xcworkspace文件打开Xcode。
  • 转到项目导航器(Cmd+1),然后选择顶部的Runner根项。
  • 选择Runner目标,然后选择Signing & Capabilities(签名和功能)选项卡。
  • 点击+ Capability(加号)按钮添加新功能。
  • 键入“associated domains”并选择该项。
  • 双击Domains列表中的第一项,并将其从webcredentials:example.com更改为:applinks: + 您的主机(例如:my-fancy-domain.com)。
  • 一个名为Runner.entitlements的文件将被创建并添加到项目中。
  • 完成。 这是截图

更多信息,请阅读Apple的
Universal Links.

对于自定义 URL 方案,您需要在
指南,以及ios/Runner/Info.plist(或通过Xcode的目标信息编辑器,
在 URL Types 下)进行声明

<?xml ...>
<!-- ... other tags -->
<plist>
<dict>
  <!-- ... other tags -->
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleTypeRole</key>
      <string>Editor</string>
      <key>CFBundleURLName</key>
      <string>[ANY_URL_NAME]</string>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>[YOUR_SCHEME]</string>
      </array>
    </dict>
  </array>
  <!-- ... other tags -->
</dict>
</plist>

这使得您的应用程序可以通过 您的 SCHEME://ANYTHING 链接启动。

有关更多信息,请阅读Apple关于
Inter-App Communication.

的指南)。我**强烈**推荐观看Apple WWDC 2015, session 509 – Seamless Linking to Your App,以了解Universal Links的工作原理(以及如何设置)。

用法

您的应用将通过两种方式接收链接:从冷启动和从后台唤醒。
更多信息请参阅示例用法中的
更多关于App启动链接的信息.

注意getInitialLink/getInitialUri应在应用程序生命周期中**仅处理一次**,因为它在应用程序的整个生命周期中不应更改。
在应用程序生命周期中,它不打算更改。
您的应用程序的生命周期。

初始链接(字符串)

如果存在,则返回启动应用程序的链接。

您应该在应用程序生命周期的早期处理它,并且只处理一次。
您可以根据需要多次读取该值,但只能处理一次。

import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';
import 'package:flutter/services.dart' show PlatformException;

// ...

  Future<void> initUniLinks() async {
    // Platform messages may fail, so we use a try/catch PlatformException.
    try {
      final initialLink = await getInitialLink();
      // Parse the link and warn the user, if it is not correct,
      // but keep in mind it could be `null`.
    } on PlatformException {
      // Handle exception by warning the user their action did not succeed
      // return?
    }
  }

// ...

初始链接(Uri)

getInitialLink相同,但已转换为Uri

注意:您应该在应用程序生命周期的早期处理此链接,并**仅处理**一次。
处理一次。

    // Uri parsing may fail, so we use a try/catch FormatException.
    try {
      final initialUri = await getInitialUri();
      // Use the uri and warn the user, if it is not correct,
      // but keep in mind it could be `null`.
    } on FormatException {
      // Handle exception by warning the user their action did not succeed
      // return?
    }
    // ... other exception handling like PlatformException

可以通过使用Uri.parse(initialLink)来实现相同的功能,这也是这个
便捷方法所做的。

更改事件(字符串)

通常,您会检查getInitialLink并监听更改。

import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';

// ...

  StreamSubscription _sub;

  Future<void> initUniLinks() async {
    // ... check initialLink

    // Attach a listener to the stream
    _sub = linkStream.listen((String? link) {
      // Parse the link and warn the user, if it is not correct
    }, onError: (err) {
      // Handle exception by warning the user their action did not succeed
    });

    // NOTE: Don't forget to call _sub.cancel() in dispose()
  }

// ...

更改事件(Uri)

linkStream相同,但已转换为发出Uri对象。

通常,您会检查getInitialUri并监听更改。

import 'dart:async';
import 'dart:io';

import 'package:uni_links/uni_links.dart';

// ...

  StreamSubscription _sub;

  Future<void> initUniLinks() async {
    // ... check initialUri

    // Attach a listener to the stream
    _sub = uriLinkStream.listen((Uri? uri) {
      // Use the uri and warn the user, if it is not correct
    }, onError: (err) {
      // Handle exception by warning the user their action did not succeed
    });

    // NOTE: Don't forget to call _sub.cancel() in dispose()
  }

// ...

更多关于App启动链接的信息

如果应用程序已被终止(或者更准确地说,未在后台运行),并且操作系统
必须重新启动它——这是一个冷启动。在这种情况下,getInitialLink
包含启动您应用程序的链接,而Stream在那时将不会产生链接。
在那时。

或者——如果应用程序正在后台运行,并且操作系统必须将其
带到前台,则Stream将是产生链接的那个,而
getInitialLink将是null,或者启动
应用程序的初始链接。

由于这两种情况——您应该始终检查
初始链接(或URI),并订阅链接(或URI)的Stream。

调用链接的工具

如果您注册了一个模式,例如unilink,您可以使用这些CLI工具。

Android

您可以在Android Studio中完成以下任务。

假设您已安装Android Studio(并附带SDK平台工具)。

adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://host/path/subpath"'
adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://example.com/path/portion/?uid=123&token=abc"'
adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://example.com/?arr%5b%5d=123&arr%5b%5d=abc&addr=1%20Nowhere%20Rd&addr=Rand%20City%F0%9F%98%82"'
adb shell 'am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "unilinks://@@malformed.invalid.url/path?"'

如果您没有adb
在您的路径中,但设置了$ANDROID_HOME环境变量,则使用
"$ANDROID_HOME"/platform-tools/adb ....

注意:或者,您可以直接进入adb shell并执行
am命令。

注意:我使用单引号,因为shell命令后面跟着的是将在
模拟器(或设备)上运行的内容,而shell元字符,例如问号
?)和与号(&),通常对您的shell有不同的含义。

adb shell与唯一可用的设备(或模拟器)通信,所以如果您
有多个设备,您必须通过指定它来指定您想在哪个设备上运行
shell。

  • 唯一USB连接的设备 – adb -d shell '...'
  • 唯一模拟的设备 – adb -e shell '...'

您可以使用adb devices列出当前可用的设备(类似地,
flutter devices也能完成相同的工作)。

iOS

假设您已经安装了Xcode。

/usr/bin/xcrun simctl openurl booted "unilinks://host/path/subpath"
/usr/bin/xcrun simctl openurl booted "unilinks://example.com/path/portion/?uid=123&token=abc"
/usr/bin/xcrun simctl openurl booted "unilinks://example.com/?arr%5b%5d=123&arr%5b%5d=abc&addr=1%20Nowhere%20Rd&addr=Rand%20City%F0%9F%98%82"
/usr/bin/xcrun simctl openurl booted "unilinks://@@malformed.invalid.url/path?"

如果您在路径中安装了xcrun(或simctl),您可以直接调用它。

booted标志假定一个已启动的模拟器(您可以通过
open -a Simulator启动它)并且有一个已启动的设备。您可以通过
指定其UUID(可在xcrun simctl listflutter devices中找到)来定位特定设备,
替换booted标志。

App Links 或 Universal Links

这些类型的链接使用https作为方案,因此您可以通过
unilinks替换为https来使用上述示例。

贡献

有关编辑插件代码的帮助,请参阅
文档.

此插件使用联合插件架构。新实现必须将uni_links_platform_interface包添加到其pubspec.yaml,扩展UniLinksPlatform类,并正确注册它。
有关联合插件的更多信息,请参阅提案文档Flutter插件文档以及Harry Terkelsen的Medium文章,描述了该架构

许可证

BSD 2条款

GitHub

查看 Github