无需第三方SDK,Flutter延迟深度链接参数丢失解决方案咨询
自定义Flutter延迟深度链接解决方案(无第三方依赖)
针对你遇到的「应用未安装时跳转商店后深度链接参数丢失」问题,以下是一套完全自定义的实现方案,分为网页端、Android端、iOS端、Flutter统一处理四个部分:
核心思路
通过后端临时存储深度链接参数,配合平台专属的安装归因机制,在应用首次启动时恢复参数:
- 网页端捕获深度链接,生成唯一UUID,将参数与UUID绑定存储到后端(设7天过期)
- 跳转应用商店时携带UUID
- 应用安装后首次启动,通过平台API获取UUID,请求后端取回深度链接参数并处理
1. 网页端实现
// 检测应用是否已安装并处理跳转 function handleDeeplink() { const currentDeeplink = window.location.href; const appSchemeUrl = `yourapp://${new URL(currentDeeplink).pathname}${new URL(currentDeeplink).search}`; const uuid = self.crypto.randomUUID(); // 生成唯一标识 // 先尝试打开应用 window.location.href = appSchemeUrl; // 1秒后未跳转则进入商店逻辑 setTimeout(() => { // 把深度链接参数和UUID存到后端(这里用fetch示例) fetch('https://your-backend.com/api/save-deeplink', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({uuid, deeplink: currentDeeplink}) }); // 跳转对应商店,携带UUID const storeUrl = /Android/i.test(navigator.userAgent) ? `https://play.google.com/store/apps/details?id=com.your.app.id&referrer=${uuid}` : `https://apps.apple.com/us/app/your-app-name/id1234567890?cid=${uuid}`; // 同时把参数存在本地存储,作为iOS端的备用方案 localStorage.setItem('pending_deeplink', currentDeeplink); window.location.href = storeUrl; }, 1000); }
2. Android端实现
利用Google Play Referrer API获取安装时携带的UUID,再请求后端取回参数:
步骤1:集成依赖
在android/app/build.gradle中添加:
dependencies { implementation 'com.android.installreferrer:installreferrer:2.2' }
步骤2:Flutter端调用获取Referrer
使用google_play_referrer插件简化调用:
import 'package:google_play_referrer/google_play_referrer.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; Future<void> restoreAndroidDeeplink() async { final referrerDetails = await GooglePlayReferrer.installReferrer; final uuid = referrerDetails.referrer; if (uuid != null && uuid.isNotEmpty) { // 请求后端获取深度链接 final response = await http.get(Uri.parse('https://your-backend.com/api/get-deeplink?uuid=$uuid')); if (response.statusCode == 200) { final data = jsonDecode(response.body); final deeplink = data['deeplink']; // 用你已集成的app_link处理链接 await AppLink().processLink(Uri.parse(deeplink)); // 通知后端标记该UUID已使用,避免重复处理 await http.post(Uri.parse('https://your-backend.com/api/mark-used'), body: {'uuid': uuid}); } } }
3. iOS端实现
iOS无法直接获取商店跳转参数,采用「透明WebView读取网页本地存储」的方案:
Flutter端代码
在应用首次启动时加载专用页面,读取网页存储的参数:
import 'package:webview_flutter/webview_flutter.dart'; import 'package:flutter/material.dart'; Future<void> restoreIOSDeeplink(BuildContext context) async { final webViewController = WebViewController() ..loadRequest(Uri.parse('https://your-redirect-page.com/fetch-deeplink')) ..setJavaScriptMode(JavaScriptMode.unrestricted) ..addJavaScriptChannel( 'DeeplinkChannel', onMessageReceived: (JavaScriptMessage message) { final deeplink = message.message; if (deeplink.isNotEmpty) { // 处理深度链接 AppLink().processLink(Uri.parse(deeplink)); } Navigator.pop(context); // 关闭透明WebView }, ); // 显示透明WebView,获取参数后自动关闭 showDialog( context: context, barrierDismissible: false, builder: (_) => Dialog( backgroundColor: Colors.transparent, child: SizedBox(height: 0, width: 0, child: WebViewWidget(controller: webViewController)), ), ); }
网页端fetch-deeplink页面代码
<script> window.addEventListener('load', () => { const deeplink = localStorage.getItem('pending_deeplink'); if (deeplink) { window.DeeplinkChannel.postMessage(deeplink); localStorage.removeItem('pending_deeplink'); // 清除已处理的参数 } setTimeout(() => window.close(), 500); // 确保参数传递完成 }); </script>
4. Flutter统一启动逻辑
在应用入口处理首次启动的参数恢复:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'dart:io'; void main() async { WidgetsFlutterBinding.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); final isFirstLaunch = prefs.getBool('is_first_launch') ?? true; if (isFirstLaunch) { // 根据平台处理参数恢复 if (Platform.isAndroid) { await restoreAndroidDeeplink(); } else if (Platform.isIOS) { // 需在MaterialApp context中调用,可延迟到首页initState处理 } await prefs.setBool('is_first_launch', false); } runApp(const MyApp()); }
关键注意事项
- 后端存储需设置过期时间(建议7天),定期清理无效数据
- Android端需确保Referrer API权限配置正确,避免被广告拦截工具影响
- iOS端需在
Info.plist中添加网页域名的权限,允许WebView访问 - 处理参数后需标记为已使用,防止应用重复触发跳转逻辑
内容的提问来源于stack exchange,提问作者Taniksha Sharma
相关产品推荐
相关产品推荐

