【问题标题】:How to handle deeplinking in Flutter using routes如何使用路由处理 Flutter 中的深度链接
【发布时间】:2021-07-10 01:17:58
【问题描述】:

我正在尝试构建深度链接功能,到目前为止,应用程序的初始启动和从深度链接中检索参数都很好。

但是,我在深度链接到应用程序后导航到屏幕时遇到问题。我该怎么做?

我的代码如下所示:

void main() { 
    runApp(MyApp()); 
}

class MyApp extends StatefulWidget {   
    @override   
    _MyAppState createState() => _MyAppState(); 
}

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {   
   Uri _latestUri;  
   Object _err;

  StreamSubscription _sub;

  @override   void initState() {
    super.initState();
    _handleIncomingLinks();
  }

  @override void dispose() {
    _sub?.cancel();
    super.dispose();   
  }

  void _handleIncomingLinks() {
    _sub = uriLinkStream.listen((Uri uri) {
      if (!mounted) return;
      print('got uri: $uri'); // printed: got uri: myapp://?key1=test
      setState(() {
        _latestUri = uri;
        _err = null;

        Navigator.pushNamed(context, 'login'); // This doesn't work because the context does not include navigator
      });
    }, onError: (Object err) {
      if (!mounted) return;
      print('got err: $err');
      setState(() {
        _latestUri = null;
        if (err is FormatException) {
          _err = err;
        } else {
          _err = null;
        }
      });
    });   
  }

  @override Widget build(BuildContext context) {
    return MaterialApp(
          initialRoute: 'splash-screen',
          onGenerateRoute: (settings) {
            switch (settings.name) {
              case 'splash-screen':
                return
                  PageTransition(
                        child: BlocProvider(
                          create: (context) => SplashScreenCubit(APIRepository(
                              apiClient: APIClient(httpClient: http.Client()))),
                          child: SplashScreen(),
                        ),
                        type: PageTransitionType.rightToLeft,
                        settings: settings);
                break;
              case 'create-account':
                return PageTransition(
                    child: BlocProvider(
                      create: (context) => CreateAccountScreenCubit(
                          APIRepository(
                              apiClient: APIClient(httpClient: http.Client()))),
                      child: CreateAccountScreen(),
                    ),
                    type: PageTransitionType.rightToLeft,
                    settings: settings);
                break;
              case 'login':
                return PageTransition(
                        child: BlocProvider(
                          create: (context) => LoginScreenCubit(APIRepository(
                              apiClient: APIClient(httpClient: http.Client()))),
                          child: LoginScreen(),
                        ),
                        type: PageTransitionType.rightToLeft,
                        settings: settings);
                break;
              default:
                 return null;
           },
        );
     }
  }

【问题讨论】:

    标签: flutter deep-linking navigator


    【解决方案1】:

    如果您需要在不从Navigtor.of 获取上下文的情况下进行导航,因为您想处理深度链接,则需要使用navigatorKey 属性,您可以阅读详细信息here

    那么您的代码将如下所示。 [已编辑,我添加了在材质应用程序上添加导航键的位置]

    void main() { ... }
    
    class MyApp extends StatefulWidget { ... }
    
    class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {   
       Uri _latestUri;  
       Object _err;
       GlobalKey<NavigatorState> navigatorKey = GlobalKey();
    
      StreamSubscription _sub;
    
      @override   void initState() { ... }
    
      @override void dispose() { ... }
    
      void _handleIncomingLinks() {
         _sub = uriLinkStream.listen((Uri uri) {
          if (!mounted) return;
          print('got uri: $uri'); // printed: got uri: myapp://?key1=test
          setState(() {
            _latestUri = uri;
            _err = null;
          });
    
          // use the navigatorkey currentstate to navigate to the page you are intended to visit
          navigatorKey.currentState.pushNamedAndRemoveUntil('login', (route) => false);
        }, onError: (Object err) { ... });
    
      @override Widget build(BuildContext context) { 
          return MaterialApp(
              ...
              navigatorKey: navigatorKey,
              ...
          );
      }
    
    }
    

    【讨论】:

    • 感谢您的回复。不幸的是,您的解决方案导致打印以下错误:[ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: NoSuchMethodError: The method 'pushNamedAndRemoveUntil' was called on null.
    • @RutgerHuijsmans 你在 MaterialApp(... , navigatorKey: ... , ...) 中注册了密钥吗?
    • 这行得通,应该是答案。但它需要显示添加的导航键
    • @RutgerHuijsmans,我在答案中添加了更多详细信息,您需要将 navigatorKey 放在 Materail 应用程序中,这样 currentState 值就不会为空
    【解决方案2】:

    你的deep link stream可以在build方法之前触发,但当时不允许调用Navigator。因此,您可以使用SchedulerBinding 提供的addPostFrameCallback 修复它:

    addPostFrameCallback

    为此帧结束安排回调。

    不请求新帧。

    此回调在一帧期间运行,就在持久帧之后 回调(这是当主渲染管道已经 酡)。如果一个帧正在进行中并且帧后回调没有 还没有执行,那么注册的回调仍然执行 帧期间。否则,执行注册的回调 在下一帧中。

    回调按照添加的顺序执行。

    后帧回调不能取消注册。他们被准确地称为 一次。

    ...
    
    void _handleIncomingLinks() {
      _sub = uriLinkStream.listen((Uri uri) {
        if (!mounted) return;
        print('got uri: $uri'); // printed: got uri: myapp://?key1=test
        setState(() {
          _latestUri = uri;
          _err = null;
    
          // Call your navigator inside addPostFrameCallback
          WidgetsBinding.instance?.addPostFrameCallback((_) {
            Navigator.pushNamed(context, 'login');
          });
        });
      }, onError: (Object err) {
        if (!mounted) return;
        print('got err: $err');
        setState(() {
          _latestUri = null;
          if (err is FormatException) {
            _err = err;
          } else {
            _err = null;
          }
        });
      });
    }
    
    ...
    

    【讨论】:

      猜你喜欢
      • 2020-05-15
      • 1970-01-01
      • 2021-01-22
      • 2016-10-10
      • 1970-01-01
      • 2020-03-19
      • 2020-04-12
      • 2021-08-09
      • 2017-03-05
      相关资源
      最近更新 更多