Flutter iOS端Universal Links在APP终止时无法跳转指定页面
iOS冷启动时Universal Links无法触发GoRouter路由的解决方案
1. 处理iOS原生层的Universal Links回调
APP处于终止状态时,iOS会通过AppDelegate的application(_:continue:restorationHandler:)方法传递Universal Links的URL,需要在这里把URL传递给Flutter端的GoRouter:
import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void ) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL { let channel = FlutterMethodChannel(name: "universal_links", binaryMessenger: window?.rootViewController as! FlutterBinaryMessenger) channel.invokeMethod("handleLink", arguments: url.absoluteString) return true } return super.application(application, continue: userActivity, restorationHandler: restorationHandler) } }
2. Flutter端接收URL并配置GoRouter初始路由
在main函数中先监听原生传递的URL,初始化GoRouter时动态设置initialLocation:
import 'package:flutter/services.dart'; import 'package:go_router/go_router.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); String? initialLocation; const channel = MethodChannel('universal_links'); // 监听冷启动后传递的链接 channel.setMethodCallHandler((call) async { if (call.method == 'handleLink') { final url = call.arguments as String; final uri = Uri.parse(url); initialLocation = uri.path + (uri.hasQuery ? '?${uri.query}' : ''); // 若APP已初始化完成,直接跳转目标路由 if (WidgetsBinding.instance.hasScheduledFrame) { GoRouter.of(WidgetsBinding.instance.context).go(initialLocation!); } } }); // 获取冷启动时的初始链接 try { final url = await channel.invokeMethod<String>('getInitialLink'); if (url != null) { final uri = Uri.parse(url); initialLocation = uri.path + (uri.hasQuery ? '?${uri.query}' : ''); } } catch (e) { print('获取初始链接失败: $e'); } runApp(MyApp(initialLocation: initialLocation)); } class MyApp extends StatelessWidget { final String? initialLocation; const MyApp({super.key, this.initialLocation}); @override Widget build(BuildContext context) { final router = GoRouter( initialLocation: initialLocation ?? '/', routes: [ // 其他路由配置... GoRoute( name: RoutesName.resetPassword, path: RoutesName.resetPassword, // 对应"/reset-password" builder: (context, state) { final email = state.uri.queryParameters['email']; final token = state.uri.queryParameters['token']; return ResetPasswordView(token, email); }, ), ], ); return MaterialApp.router( routerConfig: router, ); } }
3. 补充原生层初始链接获取方法
在AppDelegate中添加获取初始链接的逻辑,并注册到MethodChannel:
extension AppDelegate { @objc func getInitialLink(_ call: FlutterMethodCall, result: @escaping FlutterResult) { if let userActivity = UIApplication.shared.delegate?.application?.userActivities.first(where: { $0.activityType == NSUserActivityTypeBrowsingWeb }), let url = userActivity.webpageURL { result(url.absoluteString) } else { result(nil) } } }
在application(_:didFinishLaunchingWithOptions:)中注册该方法:
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let channel = FlutterMethodChannel(name: "universal_links", binaryMessenger: controller.binaryMessenger) channel.setMethodCallHandler { [weak self] call, result in if call.method == "getInitialLink" { self?.getInitialLink(call, result: result) } else { result(FlutterMethodNotImplemented) } } GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) }
4. 验证Universal Links配置
确保apple-app-site-association文件包含正确的路径匹配规则:
{ "applinks": { "apps": [], "details": [ { "appID": "你的TeamID.你的BundleID", "paths": ["/reset-password*"] } ] } }
测试注意事项
- 必须完全终止APP(后台划掉)后测试,后台状态的逻辑不同
- 使用真机测试,模拟器对Universal Links支持有限
- 确认XCode的
Signing & Capabilities中已开启Associated Domains,并添加applinks:你的域名
内容的提问来源于stack exchange,提问作者Dušan Perić
相关产品推荐
相关产品推荐

