You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 13:32:02