通过外部链接启动 Flutter App(firebase_dynamic_links 和 app_links)
详细介绍 通过外部链接启动flutter App 的使用及示例
在我们的APP中,经常有点击链接启动并进入APP的需求(如果未安装跳转到应用商店)。Android通过deep link或者app link(是deep link 的增强版),iOS通过 url schema,可以打开对应的app,因此我们需要对我们的app进行对应的配置。下面将会详细介绍两种方式。
推荐使用app_link
一、firebase_dynamic_links (该服务已经弃用,2025 年 8 月将关闭)
该三方服务提供了 生成分享链接、通过链接启动跳转到指定页面的方法,通知支持 未安装App的时候跳转其他的链接(例如跳转到应用商店),重定向逻辑 由 Firebase 服务自动处理,并且支持短链接。
实现步骤:
1. 安装依赖:
在 pubspec.yaml 文件中添加:
dependencies:flutter:sdk: flutterfirebase_dynamic_links: ^6.0.5
2. 配置 Android
在 android/app/src/main/AndroidManifest.xml 文件中,添加 intent-filter 配置,以便在 Android 中处理 App Links。
<activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><dataandroid:host="i89trillion.page.link"android:scheme="https" /></intent-filter>
</activity>
3. 初始化链接监听 || 生成分享链接(代码实现)
- 先在main方法中初始化监听:(在 runApp 之前初始化)
// 初始化动态链接
await FirebaseDynamicLinkService.initDynamicLink();
- 实现生成分享链接和监听链接的方法:
示例如下:
import "dart:async";
import "dart:io";import "package:easy_localization/easy_localization.dart";
import "package:firebase_dynamic_links/firebase_dynamic_links.dart";class FirebaseDynamicLinkService {static Duration maxDuration = Duration(seconds: 5);static Future<void> initDynamicLink() async {// 未启动的时候监听final PendingDynamicLinkData? initialLink =await FirebaseDynamicLinks.instance.getInitialLink();if (initialLink != null) {final Uri deepLink = initialLink.link;FirebaseAnalyticsService.logEvent(FirebaseAnalyticsEvent.link_join_guild_finish, '0');_handleDeepLink(deepLink);}// 应用程序在后台启动时有效FirebaseDynamicLinks.instance.onLink.listen((dynamicLinkData) {final Uri deepLink = dynamicLinkData.link;FirebaseAnalyticsService.logEvent(FirebaseAnalyticsEvent.link_join_guild_finish, '1');_handleDeepLink(deepLink);}, onError: (e) {LogUtil.error(e.toString());});}static void _handleDeepLink(Uri deepLink) async {var isJoinGuildLink = deepLink.pathSegments.contains('joinServer');if (isJoinGuildLink) {String? id = deepLink.queryParameters['id'];if (id != null) {LogUtil.info("deepLink serverId: $id");}}}// 创建一个群邀请链接static Future<String> createJoinServerDynamicLink(bool short, int guildID) async {if (!Platform.isAndroid && !Platform.isIOS) {return "该功能只在 Android 和 iOS 上可用";}String _linkMessage;final DynamicLinkParameters parameters = DynamicLinkParameters(uriPrefix: "https://xxx.page.link",link: Uri.parse('https://xxx.page.link/joinServer?id=$guildID'),androidParameters: AndroidParameters(// 未安装应用程序打开的链接fallbackUrl: Uri.parse('https://play.google.com/store/apps/details?id=xxx'),packageName: 'xxx',),);Uri url;if (short) {final ShortDynamicLink shortLink = await FirebaseDynamicLinksPlatform.instance.buildShortLink(parameters);url = shortLink.shortUrl;} else {url = await FirebaseDynamicLinksPlatform.instance.buildLink(parameters);}_linkMessage = url.toString();return _linkMessage;}
}
4. 未安装时跳转
在生成链接的时候参数 fallbackUrl 为未安装App时打开的链接,firebase 服务自己做了重定向。
二、app_links
该三方库 也支持 通过链接启动App,同时解析链接参数,跳转指定页面,但是不支持App未安装的时候跳转其他链接(需要自己的服务处理重定向)
官方文档见:https://developer.android.com/studio/write/app-link-indexing?hl=zh-cn#testindent
实现步骤:
1. 安装依赖:
在 pubspec.yaml 文件中添加:
dependencies:flutter:sdk: flutterapp_links: ^6.0.1 # 用于处理动态链接
2. 配置 Android 和 iOS 支持
在 android/app/src/main/AndroidManifest.xml 文件中,添加 intent-filter 配置,以便在 Android 中处理 App Links。
<activity android:name=".MainActivity"><!-- Add the intent-filter for handling 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" /><data android:scheme="https" android:host="yourapp.page.link" android:pathPrefix="/joinServer"/></intent-filter>
</activity>
注意: 这里注意下,开启autoVerify的activity中的<intent-filter…>的action必须为android.intent.action.VIEW,category必须包含android.intent.category.BROWSABLE,data的scheme必须包含http/https,否则不生效,而且AppLinks必须在Android 6.0 以上的手机才可生效。
android:host 为域名,android:pathPrefix为地址前缀,根据实际需求配置
2. 初始化链接监听 || 生成分享链接(代码实现)
- 先在main方法中初始化监听:(在 runApp 之前初始化)
// 初始化动态链接await AppLinkService.initializeDynamicLinks();
- 实现生成分享链接和监听链接的方法:
示例如下:
import 'package:app_links/app_links.dart';
import "package:beehive/utils/logger.dart";
import "dart:async";class AppLinkService {static final AppLinks appLinks = AppLinks();// 初始化动态链接static Future<void> initializeDynamicLinks() async {print('Initializing dynamic links...');// 监听动态链接流appLinks.uriLinkStream.listen((Uri? uri) {print("appLink listen: $uri");if (uri != null) {_handleDeepLink(uri);}});}// 处理动态链接,跳转到相应页面static void _handleDeepLink(Uri deepLink) async {var isJoinGuildLink = deepLink.pathSegments.contains('joinServer');if (!isJoinGuildLink){return;}String? id = deepLink.queryParameters['id'];LogUtil.info("appLink serverId: $id");}// 生成分享链接static Future<String> generateShareLink(int serverId) async {final String deepLinkUrl = 'https://yourapp.page.link/joinServer?id=$serverId';final Uri dynamicLink = Uri.parse(deepLinkUrl);return dynamicLink.toString(); // 返回生成的动态链接}
}
3. 链接识别启动进App
需要在域名<https://yourapp.page.link> 根目录下面 放一个JSON文件:.well-known/assetlinks.json 。该JSON文件就是用于识别进入App的,如果是谷歌商店的App,可以直接在管理中心生成,如果是测试,就自己生成。
内容格式如下:
[{"relation": ["delegate_permission/common.handle_all_urls"],"target": {"namespace": "android_app","package_name": "xxxx","sha256_cert_fingerprints":["7F:48:F9:..."]}}
]
4. 未安装时跳转逻辑
app link没有实现未安装跳转其他URL,需要自己在自己的服务域名下面进行重定向处理。(例如重定向到应用商店)
快速的方法:将 一个重定向的HTML也放到域名根目录下面。客户端 识别链接就是这个html,可以在链接中加参数。分享链接就是:https://yourapp.page.link/applink.html
例如:applink.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><title>App Link</title>
</head>
<script>window.onload = function() {if (/(android)/i.test(navigator.userAgent)) {// 用户使用的是 Android 设备window.location.href = "https://play.google.com/store/apps/details?id=xxx";} else if (/(iphone|ipad|ipod|ios)/i.test(navigator.userAgent)) {// 用户使用的是 iOS 设备window.location.href = "https://itunes.apple.com/app/idxxx";} else {// 用户使用的是其他设备console.log("This device is not supported.");}};
</script>
<body></body>
</html>
也可以在该域名对应的服务中对某个接口实现重定向逻辑:
例如:(go语言 gframe框架为例)
func (ctrl *linkCtrl) LinkRedirect(r *ghttp.Request) {userAgent := r.Header.Get("User-Agent")log.LogRuntime(log.LEVEL_INFO, "[LinkRedirect] userAgent:", userAgent)if strings.Contains(strings.ToLower(userAgent), "android") {// 用户使用的是 Android 设备r.Response.RedirectTo(define.GooglePlayAdd)} else {// 用户使用的是其他设备r.Response.Write("This device is not supported.")}
}
或者也可以直接在NGINX上面重定向跳转.