flutter系列之:创建一个内嵌的navigation

flutter,系列,创建,一个,内嵌,navigation · 浏览次数 : 136

小编点评

## 总结 代码展示了如何使用多个Navigator构建主Navigator和子路由的实现。 **主路由** * 定义了三个页面:`HomePage`, `SettingsPage`和`FriendMatchFlow`。 * `FriendMatchFlow`是一个嵌套的Navigator,包含了选择好友页面、等待页面、匹配页面和匹配完毕页面。 **子路由** * `FriendMatchFlow`是一个`StatefullWidget`,用于管理子路由的导航。 * 在 `_onGenerateRoute`方法中,根据路由名称跳转到相应的页面。 * 使用 `Navigator.of`的`pop`方法可以从子路由跳出到父路由。 **使用** * 运行代码,会看到一个主页面、一个设置页面和一个好友匹配页面。 * 点击“添加好友”按钮,会跳转到`FriendMatchFlow`页面。 * 在`FriendMatchFlow`页面中,可以选择好友,并进行匹配操作。 **注意事项** * `FriendMatchFlow`页面是一个嵌套的Navigator,需要在构建页面之前定义 `_navigatorKey`。 * `_exitSetup`方法使用 `Navigator.of`的`pop`方法返回当前页面,并跳转到主页面。

正文

简介

我们在flutter中可以使用Navigator.push或者Navigator.pushNamed方法来向Navigator中添加不同的页面,从而达到页面调整的目的。

一般情况下这样已经足够了,但是有时候我们有多个Navigator的情况下,上面的使用方式就不够用了。比如我们有一个主页面app的Navigator,然后里面有一个匹配好友的功能,这个功能有多个页面,因为匹配好友功能的多个页面实际上是一个完整的流程,所以这些页面需要被放在一个子Navigator中,并和主Navigator区分开。

那么应该如何处理呢?

搭建主Navigator

主Navigator是我们app的一些主要界面,这里我们有三个界面,分别是主home界面,一个setting配置界面和好友匹配界面。

其中好友匹配界面包含了三个子界面,这三个子界面将会用到子路由。

先来看下主路由,主路由的情况跟普通的路由没啥区别,这里我们首先定义和home和setting匹配的两个widget:HomePage和SettingsPage:

class HomePage extends StatelessWidget {
  const HomePage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(context),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24.0),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: const [
              SizedBox(
                width: 250,
                height: 250,
                child: Center(
                  child: Icon(
                    Icons.home,
                    size: 175,
                    color: Colors.blue,
                  ),
                ),
              ),
              SizedBox(height: 32),
              Text(
                '跳转到好友匹配页面',
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).pushNamed(routeFriendMatch);
        },
        child: const Icon(Icons.add),
      ),
    );
  }

HomePage很简单,它包含了一个floatingActionButton,当点击它的时候会调用 Navigator.pushNamed方法进行路由切换。

然后是SettingsPage,它是一个简单的Column:

class SettingsPage extends StatelessWidget {
  const SettingsPage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildAppBar(),
      body: SingleChildScrollView(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: List.generate(8, (index) {
            return  ListTile(
              title: Text('设置项$index'),
            );
          }),
        ),
      ),
    );
  }

最后一个页面是FriendMatchFlow,这个页面比较复杂,我们在下一个再进行讲解。

然后我们为主路由在onGenerateRoute方法中进行定义:

void main() {
  runApp(
    MaterialApp(
      onGenerateRoute: (settings) {
        late Widget page;
        if (settings.name == routeHome) {
          page = const HomePage();
        } else if (settings.name == routeSettings) {
          page = const SettingsPage();
        } else if (settings.name == routeFriendMatch) {
          page = const FriendMatchFlow(
            setupPageRoute: routeFriendMatchPage,
          );
        }

        return MaterialPageRoute<dynamic>(
          builder: (context) {
            return page;
          },
          settings: settings,
        );
      },
      debugShowCheckedModeBanner: false,
    ),
  );
}

主路由很简单,跟普通的路由没有太多的区别。

构建子路由

接下来是构建子路由的步骤。在主路由中,如果路由的名称是routeFriendMatch,那么就会跳转到FriendMatchFlow。

而这个flow页面实际上是由几个子页面组成的:选择好友页面,等待页面,匹配页面和匹配完毕页面。

具体的页面代码这里就不写了,我们主要来讲一下子路由的使用。

对于FriendMatchFlow来说,它本身是一个Navigator,所以我们的build方法是这样的:

  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: _isExitDesired,
      child: Scaffold(
        appBar: _buildFlowAppBar(),
        body: Navigator(
          key: _navigatorKey,
          initialRoute: widget.setupPageRoute,
          onGenerateRoute: _onGenerateRoute,
        ),
      ),
    );
  }

因为他需要根据用户的不同点击来进行内部路由的切换,所以需要保存对当前子Navigator的应用,所以这里FriendMatchFlow是一个StatefulWidget,并且上面的_navigatorKey是一个GlobalKey对象,以提供对子Navigator的引用:

  final _navigatorKey = GlobalKey<NavigatorState>();

这里的_onGenerateRoute方法,跟主路由也是很类似的,主要定义的是子路由中页面的跳转:

  Route _onGenerateRoute(RouteSettings settings) {
    late Widget page;
    switch (settings.name) {
      case routeFriendMatchPage:
        page = WaitingPage(
          message: '匹配附近的好友...',
          onWaitComplete: _onDiscoveryComplete,
        );
        break;
      case routeFriendSelectPage:
        page = SelectFriendPage(
          onFriendSelected: _onFriendSelected,
        );
        break;
      case routeFriendConnectingPage:
        page = WaitingPage(
          message: '匹配中...',
          onWaitComplete: _onConnectionEstablished,
        );
        break;
      case routeFriendFinishedPage:
        page = FinishedPage(
          onFinishPressed: _exitSetup,
        );
        break;
    }

这里的on***Selected是VoidCallback回调,用来进行路由的切换:

  void _onDiscoveryComplete() {
    _navigatorKey.currentState!.pushNamed(routeFriendSelectPage);
  }

  void _onFriendSelected(String deviceId) {
    _navigatorKey.currentState!.pushNamed(routeFriendConnectingPage);
  }

  void _onConnectionEstablished() {
    _navigatorKey.currentState!.pushNamed(routeFriendFinishedPage);
  }

可以看到上面的路由切换实际上是在子路由上切换,跟父路由无关。

如果想要直接从子路由跳出到父路由该怎么处理呢?很简单,调用Navigator.of的pop方法即可:

  void _exitSetup() {
    Navigator.of(context).pop();
  }

这里的context默认是全局的context,所以会导致主路由的跳转变化。

总结

以上的代码运行结果如下:

虽然上面的例子看起来复杂,但是大家只要记住了不同的路由使用不同的Navigator范围进行跳转就行了。

本文的例子:https://github.com/ddean2009/learn-flutter.git

与flutter系列之:创建一个内嵌的navigation相似的内容:

flutter系列之:创建一个内嵌的navigation

简介 我们在flutter中可以使用Navigator.push或者Navigator.pushNamed方法来向Navigator中添加不同的页面,从而达到页面调整的目的。 一般情况下这样已经足够了,但是有时候我们有多个Navigator的情况下,上面的使用方式就不够用了。比如我们有一个主页面ap

flutter系列之:永远不用担心组件溢出的Wrap

简介 我们在flutter中使用能够包含多个child的widget的时候,经常会遇到超出边界范围的情况,尤其是在Column和Row的情况下,那么我们有没有什么好的解决办法呢?答案就是今天我们要讲解的Wrap。 Row和Column的困境 Row和Column中可以包含多个子widget,如果子w

flutter 系列之:flutter 中的幽灵offstage

简介 我们在使用flutter的过程中,有时候需要控制某些组件是否展示,一种方法是将这个组件从render tree中删除,这样这个组件就相当于没有出现一样,但是有时候,我们只是不想展示这个widget,但是这个组件还是存在的,并且可以接受键盘输入,还可以使用CPU。它和真正的组件唯一不同的就是他是

flutter系列之:在flutter中使用流式布局

简介 我们在开发web应用的时候,有时候为了适应浏览器大小的调整,需要动态对页面的组件进行位置的调整。这时候就会用到flow layout,也就是流式布局。 同样的,在flutter中也有流式布局,这个流式布局的名字叫做Flow。事实上,在flutter中,Flow通常是和FlowDelegate一

flutter系列之:flutter中的变形金刚Transform

简介 虽然我们在开发APP的过程中是以功能为主,但是有时候为了美观或者其他的特殊的需求,需要对组件进行一些变换。在Flutter中这种变换就叫做Transform。 flutter的强大之处在于,可以对所有的widget进行Transform,因此可以做出非常酷炫的效果。 Transform简介 在

flutter系列之:如丝般顺滑的SliverAppBar

简介 对于一个APP来说,肯定会有一个AppBar,这个AppBar一般包含了APP的导航信息等。虽然我们可以用一个固定的组件来做为AppBar,但是这样就会丢失很多特效,比如将AppBar固定在顶部,AppBar可以在滑动的过程中进行大小变换等。 当然这一切都不需要自己来实现,flutter已经为

flutter系列之:移动端手势的具体使用

简介 之前我们介绍了GestureDetector的定义和其提供的一些基本的方法,GestureDetector的好处就是可以把任何一个widget都赋予类似button的功能。 今天将会通过几个具体的例子来讲解一下GestureDetector的具体使用。 赋予widget可以点击的功能 一般情况

flutter系列之:flutter中listview的高级用法

简介 一般情况下,我们使用Listview的方式是构建要展示的item,然后将这些item传入ListView的构造函数即可,通常情况下这样做是够用了,但是不排除我们会有一些其他的特殊需求。 今天我们会来讲解一下ListView的一些高级用法。 ListView的常规用法 ListView的常规用法

flutter系列之:使用SliverList和SliverGird

简介 在上一篇文章我们讲解SliverAppBar的时候有提到过,Sliver的组件一般都用在CustomScrollView中。除了SliverAppBar之外,我们还可以为CustomScrollView添加List或者Grid来实现更加复杂的组合效果。 今天要向大家介绍的就是SliverLis

flutter系列之:在flutter中使用导航Navigator

简介 一个APP如果没有页面跳转那么是没有灵魂的,页面跳转的一个常用说法就是Navigator,flutter作为一个最为优秀的前端框架,Navigator肯定是必不可少的,那么在flutter中如何使用Navigator呢? 一起来看看吧。 flutter中的Navigator Navigator