美文网首页All in FlutterFlutter
Flutter局部路由实现方案

Flutter局部路由实现方案

作者: 吉原拉面 | 来源:发表于2019-05-15 16:34 被阅读30次

      本文主要讲一个Flutter中局部替换路由的方案,在实际开发中,需要局部替换的情况似乎还蛮多的,所以还是讲一下这个小技巧把。
      效果图:

    part_navigator.gif
      实现其实超级简单,主要是在压路由的时候注意下用哪个context就行了。
      代码:https://gist.github.com/yumi0629/a22bc590bbe7a3d10d3436afb27c7043

      首先页面分成两部分,上下的appBar和bottomNavigationBar是在局部替换的时候不变的,中间的body是需要局部替换的。我们知道,路由的替换是Navigator操控的,所以中间的body部分,我们只要自己维护一个Navigator,就可以实现局部替换了。而Flutter早就为我们设计好了,Navigator是一个Widget,我们可以直接拿来用:

    @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('PartNavigator'),
          ),
          body: Navigator(
            // 这里维护局部路由
          ),
          bottomNavigationBar: Container(
            height: 40.0,
            color: Colors.red,
          ),
        );
      }
    

      定义一个Navigator很简单,点进文档就能看到很详细的example,这里再次感谢Flutter爸爸文档写得真良心!

    Navigator(
            initialRoute: 'part/part1',
            onGenerateRoute: (RouteSettings settings) {
              RoutePageBuilder builder;
              switch (settings.name) {
                case 'part/part1':
                  builder = (_, __, ___) => PartPage1(
                        pageContext: context,
                      );
                  break;
                case 'part/part2':
                  builder = (_, __, ___) => PartPage2(
                        pageContext: context,
                      );
                  break;
                default:
                  throw Exception('Invalid route: ${settings.name}');
              }
              return PageRouteBuilder(
                pageBuilder: builder,
                transitionDuration: const Duration(milliseconds: 0),
              );
            },
          )
    

      其中,onGenerateRoute是必传的,initialRouteonGenerateRouteonUnknownRoute都是定义路由跳转的,他们之间有优先级关系,这个我以前讲过,还是再说一遍吧:
      Navigator会按照initialRoute----onGenerateRoute---->onUnknownRoute的顺序去寻找路由:

    • initialRoute,也就是初始路由;
    • onGenerateRoute用来处理路由,我们可以在里面定义路由映射,所以一般返回非空值;
    • onUnknownRoute如果说某个路由上面都没处理,那么就会由onUnknownRoute来处理这个路由。
        上述例子中我们将part1控件作为初始路由,我们在里面写两个按钮,一个按钮点击后做布局跳转到part2,另一个按钮点击后做全局的页面跳转:
    class PartPage1 extends StatelessWidget {
      final BuildContext pageContext;
    
      const PartPage1({Key key, this.pageContext}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text.rich(
              TextSpan(
                  style: TextStyle(fontSize: 20.0),
                  text: 'This is part',
                  children: [
                    TextSpan(
                      style: TextStyle(fontSize: 40.0, color: Colors.red),
                      text: ' 1',
                    )
                  ]),
            ),
            RaisedButton(
              onPressed: () {
                Navigator.of(context).pushNamed('part/part2');
              },
              child: Text('To Part 2'),
            ),
            RaisedButton(
              onPressed: () {
                Navigator.of(pageContext)
                    .push(MaterialPageRoute(builder: (_) => AnotherPage()));
              },
              child: Text('To Another Page'),
            )
          ],
        );
      }
    }
    

      我们可以看到,两次跳转时获取Navigator的context明显是不一样的,我开头就说了,压路由一定要使用对应的Navigator的context。PartPage1中的context是局部Navigator给它的,所以维护的是局部路由;而变量pageContextScaffold给它的,所以它并不属于Navigator。所以,Navigator.of(context)Navigator.of(pageContext)自然不是同一个实例。
      到这里,很多小伙伴肯定会问了,Scaffold的Navigator是哪里来的呢?翻下源码就知道啦,Flutter在WidgetsApp中维护了一个全局的路由池来供开发者使用:

    @override
      Widget build(BuildContext context) {
        Widget navigator;
        if (_navigator != null) {
          navigator = Navigator(
            key: _navigator,
            // If window.defaultRouteName isn't '/', we should assume it was set
            // intentionally via `setInitialRoute`, and should override whatever
            // is in [widget.initialRoute].
            initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName
                ? WidgetsBinding.instance.window.defaultRouteName
                : widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,
            onGenerateRoute: _onGenerateRoute,
            onUnknownRoute: _onUnknownRoute,
            observers: widget.navigatorObservers,
          );
        }
    

    相关文章

      网友评论

        本文标题:Flutter局部路由实现方案

        本文链接:https://www.haomeiwen.com/subject/dvnpaqtx.html