Flutter iOS端Singular SDK Deep Link跳转异常求助
问题
Flutter应用使用singular_flutter_sdk: ^1.0.12实现Deep Link功能,Android端运行正常,但iOS端存在异常:应用在后台运行时点击链接可正常跳转,但通过链接冷启动应用时仅首次有效,再次点击链接只会唤醒后台应用,无法跳转到指定页面。期望每次通过链接启动应用时都能跳转到对应页面。
现有代码:
AppDelegate.swift
import UIKit import Flutter import singular_flutter_sdk @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GeneratedPluginRegistrant.register(with: self) if let singularAppDelegate = SingularAppDelegate.shared() { singularAppDelegate.launchOptions = launchOptions } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if let singularAppDelegate = SingularAppDelegate.shared() { singularAppDelegate.continueUserActivity(userActivity, restorationHandler: nil) } return true } override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { if let singularAppDelegate = SingularAppDelegate.shared() { singularAppDelegate.handleOpen(url, options: options) } return true } }
main.dart
void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => MyAppState(); } class MyAppState extends State<MyApp> with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); SingularConfig config = SingularConfig(singularApiKey, singularApiSecret); config.singularLinksHandler = ((SingularLinkParams params) { String? deeplink = params.deeplink; String? passthrough = params.passthrough; bool? isDeferred = params.isDeferred; // Add your code here to handle the deep link }); config.waitForTrackingAuthorizationWithTimeoutInterval = 60; config.skAdNetworkEnabled = true; config.clipboardAttribution = true; Singular.event("Launched"); Singular.event("Completed"); Singular.start(config); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: const SplashScreen(), ); } }
解决方案
问题出在iOS端链接参数传递不完整,以及Flutter端未处理应用后台唤醒后的链接检查,以下是具体修改步骤:
1. 修改iOS端AppDelegate.swift
- 正确调用Singular的启动参数处理方法,确保链接参数能被正确传递
- 在
continueUserActivity和open url方法中,返回Singular的处理结果,并调用父类方法,避免系统链接处理逻辑被中断
修改后的代码:
import UIKit import Flutter import singular_flutter_sdk @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GeneratedPluginRegistrant.register(with: self) if let singularAppDelegate = SingularAppDelegate.shared() { // 让Singular处理启动参数 singularAppDelegate.application(application, didFinishLaunchingWithOptions: launchOptions) } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if let singularAppDelegate = SingularAppDelegate.shared() { // 优先让Singular处理链接,处理成功则返回true if singularAppDelegate.continueUserActivity(userActivity, restorationHandler: restorationHandler) { return true } } // 未处理则交给父类处理 return super.application(application, continue: userActivity, restorationHandler: restorationHandler) } override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { if let singularAppDelegate = SingularAppDelegate.shared() { // 优先让Singular处理链接,处理成功则返回true if singularAppDelegate.handleOpen(url, options: options) { return true } } // 未处理则交给父类处理 return super.application(app, open: url, options: options) } }
2. 修改Flutter端main.dart
- 提取链接处理逻辑为单独方法,方便复用
- 添加应用生命周期监听,在应用恢复到前台时主动检查Singular的最新链接
- 使用全局NavigatorKey确保跳转时上下文有效
修改后的代码:
import 'package:flutter/material.dart'; import 'package:singular_flutter_sdk/singular_flutter_sdk.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => MyAppState(); } class MyAppState extends State<MyApp> with WidgetsBindingObserver { // 全局NavigatorKey,确保跳转时上下文有效 final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _initSingular(); } void _initSingular() { SingularConfig config = SingularConfig(singularApiKey, singularApiSecret); config.singularLinksHandler = _handleDeepLink; config.waitForTrackingAuthorizationWithTimeoutInterval = 60; config.skAdNetworkEnabled = true; config.clipboardAttribution = true; Singular.event("Launched"); Singular.event("Completed"); Singular.start(config); } // 统一处理Deep Link跳转逻辑 void _handleDeepLink(SingularLinkParams params) { String? deeplink = params.deeplink; if (deeplink == null) return; // 确保Navigator已初始化完成后再跳转 WidgetsBinding.instance.addPostFrameCallback((_) { _navigatorKey.currentState?.pushNamed(deeplink); // 如果是冷启动后的跳转,可根据需要关闭启动页 // _navigatorKey.currentState?.pushReplacementNamed(deeplink); }); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); // 应用从后台恢复到前台时,检查最新的Deep Link if (state == AppLifecycleState.resumed) { Singular.getLastSingularLink().then((params) { if (params != null) { _handleDeepLink(params); } }); } } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, navigatorKey: _navigatorKey, // 设置全局NavigatorKey initialRoute: '/splash', // 明确初始路由 routes: { '/splash': (context) => const SplashScreen(), // 添加你的其他路由,比如'/product/:id'等 }, ); } }
额外注意事项
- 确保iOS项目已正确配置Associated Domains和Universal Links
- 测试冷启动场景时,需完全关闭应用(从后台划掉)后再点击链接
- 若使用自定义路由,需确保路由表包含所有Deep Link对应的路径
内容的提问来源于stack exchange,提问作者Yugal Kalyan
相关产品推荐
相关产品推荐

