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

如何从原生iOS Widget跳转至Flutter应用指定页面

解决Flutter Widget跳转指定路由的问题

问题原因

Widget点击链接只能打开Flutter初始路由,是因为Flutter默认的routes配置仅处理App内部导航跳转,不会自动解析外部URL Scheme传递的路由信息。需要同时配置iOS侧的URL Scheme,并在Flutter侧添加外部链接的处理逻辑。

解决步骤

1. 确认iOS项目的URL Scheme配置

打开iOS/Runner/Info.plist,添加URL Scheme配置(确保CFBundleURLSchemes的值和你pubspec.yaml里的应用名一致,这里是interval_timer):

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>interval_timer</string>
        </array>
        <key>CFBundleURLName</key>
        <string>你的Bundle ID</string> <!-- 替换成你项目的Bundle ID,比如com.yourname.intervaltimer -->
    </dict>
</array>

调整Swift代码里的链接,把目标路由通过参数传递给Flutter,形式更灵活:

Link(destination: URL(string: "interval_timer://deeplink?route=/profile")!) {
    Text(intervalString)
}

也可以直接用路径形式:interval_timer:///profile,后续解析时取URL的path即可。

3. Flutter侧添加Deep Link处理逻辑

需要监听App冷启动和后台唤醒时的外部URL,解析后跳转到对应路由。这里用原生MethodChannel实现,无需依赖第三方库:

第一步:iOS侧传递URL到Flutter

打开iOS/Runner/AppDelegate.swift,添加URL处理代码:

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
        
        // 处理冷启动时的URL
        if let url = launchOptions?[.url] as? URL {
            let channel = FlutterMethodChannel(name: "com.yourname.interval_timer/deeplink", binaryMessenger: window?.rootViewController as! FlutterViewController)
            channel.invokeMethod("handleDeepLink", arguments: url.absoluteString)
        }
        
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    // 处理App从后台唤醒时的URL
    override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        let channel = FlutterMethodChannel(name: "com.yourname.interval_timer/deeplink", binaryMessenger: window?.rootViewController as! FlutterViewController)
        channel.invokeMethod("handleDeepLink", arguments: url.absoluteString)
        return true
    }
}

注意把com.yourname.interval_timer/deeplink里的Bundle ID部分替换成你自己的。

第二步:Flutter侧监听并处理路由

修改你的Flutter主页面代码,添加MethodChannel监听和路由处理:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  late MethodChannel _deepLinkChannel;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    // 初始化MethodChannel,名称要和iOS侧一致
    _deepLinkChannel = const MethodChannel('com.yourname.interval_timer/deeplink');
    _deepLinkChannel.setMethodCallHandler(_handleDeepLink);
  }

  Future<void> _handleDeepLink(MethodCall call) async {
    if (call.method == 'handleDeepLink') {
      final String link = call.arguments as String;
      final Uri uri = Uri.parse(link);
      
      // 解析路由参数(对应Widget里的?route=/profile)
      final String? targetRoute = uri.queryParameters['route'];
      
      if (targetRoute != null && mounted) {
        // 跳转目标路由,如果App已在后台,用pushReplacement避免重复栈
        if (Navigator.of(context).canPop()) {
          Navigator.pushReplacementNamed(context, targetRoute);
        } else {
          Navigator.pushNamed(context, targetRoute);
        }
      }
    }
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    // 确保App从后台唤醒时能处理最新的链接
    if (state == AppLifecycleState.resumed) {
      // 可在此主动触发链接检查,比如通过channel调用iOS侧获取最新链接
    }
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/workouts',
      routes: {
        '/workouts': (context) => const Home(screenIndex: 0),
        '/jumpin': (context) => const Home(screenIndex: 1),
        '/profile': (context) => const Home(screenIndex: 2),
      },
    );
  }
}

// 你的Home组件
class Home extends StatelessWidget {
  final int screenIndex;
  const Home({super.key, required this.screenIndex});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(child: Text('Screen $screenIndex')),
    );
  }
}

4. 测试验证

重新运行iOS项目,添加Widget到桌面,点击链接后应该能直接跳转到/profile页面,无论App是冷启动还是后台唤醒状态。

内容的提问来源于stack exchange,提问作者Nikita

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 04:45:17