乐趣区

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

简介

咱们在 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

退出移动版