前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter中的基本路由、命名路由、替换路由,返回到根路由

Flutter中的基本路由、命名路由、替换路由,返回到根路由

作者头像
拉维
发布2019-08-19 11:42:43
8.8K1
发布2019-08-19 11:42:43
举报
文章被收录于专栏:iOS小生活iOS小生活

Flutter中的路由,通俗地讲就是页面跳转。在Flutter中通过 Navigator 组件管理路由导航。

Flutter中给我们提供了两种配置路由跳转的方式:基本路由和命名路由。

今天我们先来聊聊基本路由。

基本路由

首先我们创建一个 Searchpage 页面:

代码语言:javascript
复制
import 'package:flutter/material.dart';

class Searchpage extends StatelessWidget {
  final String info;//用于路由传值
  const Searchpage({Key key, this.info="默认值"}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(//在最底层采取Scaffold组件
      appBar: AppBar(
        title: Text("搜索页面"),
      ),
      body: Text("Search Page!传递过来的参数值是:${this.info}"),
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          //返回上一级页面
          Navigator.of(context).pop();
        },
        child: Text("back"),
      ),
    );
  }
}

然后在 Category 页面中引入SearchPage.dart,并新增一个按钮执行页面跳转。

代码语言:javascript
复制
import 'package:flutter/material.dart';
import '../SearchPage.dart';//引入其他页面

class Category extends StatefulWidget {
  Category({Key key}) : super(key: key);

  _CategoryState createState() => _CategoryState();
}

class _CategoryState extends State<Category> {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text("分类页面"),
        RaisedButton(
          child: Text("搜索页面"),
          onPressed: (){
            //普通路由跳转
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => Searchpage(info: "666",),
              )
            );
          },
        )
      ],
    );
  }
}

关于上述代码,有以下几点需要说明。

1,新增一个页面的时候,默认是没有主题样式的,现阶段我们基本是采取Scaffold页面主题样式。也就是说,当新建一个跳转页面的时候,我们需要在最底层采取Scaffold组件,如下所示:

2,普通路由执行跳转页面的关键代码如下:

代码语言:javascript
复制
Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => Searchpage(info: "666",),
              )
            );

3,普通路由也是可以传值的,我们只需要在需要跳入的页面新增一个属性,然后在跳入之前将给该参数赋值即可。对应代码如下:

代码语言:javascript
复制
//Searchpage定义
class Searchpage extends StatelessWidget {
  final String info;//用于路由传值
  const Searchpage({Key key, this.info="默认值"}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ......
}

//普通路由跳转
Navigator.of(context).push(
  MaterialPageRoute(
   builder: (context) => Searchpage(info: "666",),//页面跳转并且传值
  )
);

4,由A页面跳转到B页面后,B页面会自动有返回按钮以及返回操作。我们也可以自定义一个返回按钮来演示一下返回操作。Scaffold组件有一个浮动按钮的属性,我们对该属性直接配置来定义返回按钮,代码如下:

代码语言:javascript
复制
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          //返回上一级页面
          Navigator.of(context).pop();
        },
        child: Text("back"),
      )

最后,本文示例代码的演示效果如下:

命名路由

上文中介绍了Flutter中的普通路由,在小项目中使用普通路由是比较合适的,但是在一些大型商业项目中,我们最好还是统一管理路由,即使用命名路由。

我们先通过一个小例子来了解一下命名路由的大致流程

第1步,在根组件 MaterialApp 中配置路由信息:

代码语言:javascript
复制
//main.dart
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Tabs(),
      //配置路由信息
      routes: {
        "/search": (context) => Searchpage(),
      },
    );
  }
}

第2步,在合适的场景下,采用 Navigator.pushNamed 进行路由跳转:

代码语言:javascript
复制
Navigator.pushNamed(context, "/search");

了解了命名路由的基本使用之后,我们再来看看命名路由如何进行传值

第1步,在根组件中配置路由:

代码语言:javascript
复制
import 'package:flutter/material.dart';
import 'package:flutter_app_google/pages/SearchPage.dart';
import 'pages/tabs/Tabs.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {

  //配置命名路由信息
  final routes = {
    //如果需要传参,那么在配置的时候加上{arguments};如果不需要传参,则不用加{arguments}
    "/search": (context, {arguments}) => Searchpage(arguments: arguments,),
  };

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Tabs(),
        //统一处理命名路由
        onGenerateRoute: (RouteSettings settings) {
          final String name = settings.name;
          final Function pageContentBuilder = this.routes[name];
          if (pageContentBuilder != null) {//能寻找到对应的路由
            if (settings.arguments != null) {//页面跳转前有传参
              final Route route = MaterialPageRoute(
                  builder: (context) => pageContentBuilder(context,
                      arguments: settings.arguments));
              return route;
            } else {//页面跳转前没有传参
              final Route route = MaterialPageRoute(
                  builder: (context) => pageContentBuilder(context));
              return route;
            }
          }
        });
  }
}

第2步,如果所要跳入页面需要传递参数过来的话,那么就需要在需要跳入的页面中声明参数信息。

代码语言:javascript
复制
import 'package:flutter/material.dart';

class Searchpage extends StatelessWidget {
  final arguments;//用于接收命名路由传递过来的参数值
  const Searchpage({Key key, this.arguments}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(//在最底层采取Scaffold组件
      appBar: AppBar(
        title: Text("搜索页面"),
      ),
      //获取命名路由传递过来的参数值
      body: Text("Search Page!传递过来的参数值是:${arguments != null ? arguments['info'] : '默认值'}"),
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          //返回上一级页面
          Navigator.of(context).pop();
        },
        child: Text("back"),
      ),
    );
  }
}

第3步,在何时的时刻进行命名路由跳转传值。

代码语言:javascript
复制
 //命名路由跳转传值
 Navigator.pushNamed(context, "/search", arguments: {"info":"777"});

现在我们已经了解了命名路由传值该怎么去操作了,但是此时的代码看起来很乱,如果后期需要管理的命名路由多了,那么如果不做代码分离,而是直接像上面那样写的话,就会造成代码堆积,可读性变差,也不利于后期维护。所以,我们有必要做代码分离,那么该如何去做呢

第1步,在lib文件夹下新建一个routes文件夹,然后在routes文件夹下新增一个 Routes.dart 文件,如下:

第2步,将命名路由配置的相关代码都分离到Routes.dart中:

代码语言:javascript
复制
//Routes.dart

import 'package:flutter/material.dart';
import 'package:flutter_app_google/pages/SearchPage.dart';

//配置命名路由信息
final routes = {
  //如果需要传参,那么在配置的时候加上{arguments};如果不需要传参,则不用加{arguments}
  "/search": (context, {arguments}) => Searchpage(
        arguments: arguments,
      ),
};

//统一处理命名路由
var onGenerateRoute = (RouteSettings settings) {
  final String name = settings.name;
  final Function pageContentBuilder = routes[name];
  if (pageContentBuilder != null) {
    //能寻找到对应的路由
    if (settings.arguments != null) {
      //页面跳转前有传参
      final Route route = MaterialPageRoute(
          builder: (context) =>
              pageContentBuilder(context, arguments: settings.arguments));
      return route;
    } else {
      //页面跳转前没有传参
      final Route route =
          MaterialPageRoute(builder: (context) => pageContentBuilder(context));
      return route;
    }
  }
};

第3步,在main.dart中引入Routes.dart,并且使用暴露出来的接口

代码语言:javascript
复制
import 'package:flutter/material.dart';
import 'package:flutter_app_google/routes/Routes.dart' as prefix0;
import 'pages/tabs/Tabs.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Tabs(),
        //统一处理命名路由
        onGenerateRoute: prefix0.onGenerateRoute);
  }
}

现在我已经将命名路由的配置代码分离到 Routes.dart 文件中了,这样一分离,main.dart中的代码就简洁多了。其实,我们还可以对main.dart中的代码进一步进行优化,也就是说,我们还可以将 Tabs 这个主页面也通过命名路由进行管理,代码如下:

代码语言:javascript
复制
//Routes.dart
//配置命名路由信息
final routes = {
  //如果需要传参,那么在配置的时候加上{arguments};如果不需要传参,则不用加{arguments}
  "/": (context) => Tabs(),
  "/search": (context, {arguments}) => Searchpage(arguments: arguments),
};


//main.dart
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        // home: Tabs(),
        initialRoute: "/",//初始化的时候加载的路由
        //统一处理命名路由
        onGenerateRoute: prefix0.onGenerateRoute);
  }
}

最后,我们再来看看有状态的组件如何进行路由传值:

代码语言:javascript
复制
import 'package:flutter/material.dart';

class DetailPage extends StatefulWidget {
  final Map arguments;//1,定义传值参数
  DetailPage({Key key, this.arguments}) : super(key: key);//2,重新写构造函数

  _DetailPageState createState() => _DetailPageState(arguments: arguments);//3,将参数值传递给_DetailPageState
}

class _DetailPageState extends State<DetailPage> {
  Map arguments;//4,定义传值参数
  _DetailPageState({this.arguments});//5,重新写构造函数
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("详情页面")),
      body: Text("hulalaDetail!~${this.arguments["name"]}")//6,获取到传递过来的值
    );
  }
}

这里的DetailPage是一个StatefulWidget类型的组件,我们按照上述123456的步骤就可以完成一个可变状态组件的路由传值。

总结

关于命名路由使用的前前后后,我在该文中都做了详细总结,并且做了代码分离,后续在项目中,我们可以参考该文进行命名路由的配置

替换路由

前文中我们了解了Flutter中的普通路由和命名路由。今天我们接着来聊聊Flutter中的替换路由和如何返回到跟路由。

首先,我们先来考虑一个场景:APP的注册页面,可能要分好几步才能注册成功,比如输入手机号——输入验证码——输入密码,然后注册成功,注册成功之后跳转到登录页面,在登录页面登陆成功之后返回到主页面。

如果按照我们之前了解的知识,页面的跳转都是通过 Navigator.pushNamed 实现的,这样的话,如果我们采用 Navigator.pop(context) 返回页面的话,就只能返回上一页面。比如,我们采用 Navigator.pushNamed 按下面的顺序一级一级跳转,然后采用 Navigator.pop(context) 返回页面,这样的话,LoginPage只能返回到RegistThirdPage,RegistThirdPage只能返回到RegistSecondPage,RegistSecondPage只能返回到RegistFirstPage,RegistFirstPage只能返回到Setting。

代码语言:javascript
复制
Setting.dart -> RegistFirstPage.dart -> RegistSecondPage.dart -> RegistThirdPage.dart -> LoginPage.dart 
       |                                                                                         |
       ^                                                                                         |
       |------------------------------<---------------<-------------<---------------<------------|

如果是按照这样的话,我们在登陆成功以后就不能直接返回到首页面了。如果我们想在登陆成功之后直接返回到首页面,那么可以采用替换路由 Navigator.pushReplacementNamed 的方式进行页面的跳转:

代码语言:javascript
复制
//在Setting.dart页面跳转到注册RegistFirstPage.dart页面
Navigator.pushNamed(context, "/registFirst");

//在RegistFirstPage.dart页面跳转到RegistSecondPage.dart页面
Navigator.pushReplacementNamed(context, "/registSecond");

//在RegistSecondPage.dart页面跳转到RegistThirdPage.dart页面
Navigator.pushReplacementNamed(context, "/registThird");

//在RegistThirdPage.dart页面跳转到LoginPage.dart页面
Navigator.pushReplacementNamed(context, "/login");

//在LoginPage.dart页面返回到Setting.dart页面
Navigator.pop(context);

替换路由 Navigator.pushReplacementNamed 的作用是,用即将跳入的页面来替换当前页面在路由栈中的位置。比如上例中,在 Setting.dart 页面中使用命名路由的方式跳转到 RegistFirstPage.dart 页面,在 RegistFirstPage.dart 页面则使用替换路由的方式跳转到 RegistSecondPage.dart 页面,那么在 RegistSecondPage.dart 页面中使用 Navigator.pop(context) 返回,返回到的是Setting.dart页面,而不是 RegistFirstPage.dart 页面。同理,在上例中的RegistThirdPage.dartLoginPage.dart中,点击返回按钮,使用 Navigator.pop(context) 方式返回的时候,返回到的都是 Setting.dart 页面。

返回到根路由

上面我们了解了替换路由如何使用,以及如果通过替换路由返回到主页面。那么在绝大部分情况下,我们在页面跳转的时候,还是采取普通命名路由跳转的方式(而不是采取替换路由),此时,在跳转到多级页面之后,如何一键返回到主页面呢?

采用,我们可以实现该效果,代码如下:

代码语言:javascript
复制
//LoginPage.dart
Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (context) => Tabs(currentIndex: 2,)), (route)=>route==null);

需要注意的是,为了能够控制跳入Tabs的第几个页面(首页还是分类还是设置),我们需要给Tabs组件增加一个控制展示第几个页面的属性 currentIndex ,

代码语言:javascript
复制
//Tabs.dart

class Tabs extends StatefulWidget {
  final int currentIndex;//1,定义传值参数
  Tabs({Key key, this.currentIndex=0}) : super(key: key);//2,重新写构造函数

  _TabsState createState() => _TabsState(this.currentIndex);//3,将参数值传递给_TabsState
}

class _TabsState extends State<Tabs> {
  int _tabIndex;//4,定义传值参数
  List _pageList = [
    HomePage(),
    Category(),
    SettingPage()
  ];

  _TabsState(this._tabIndex);//5,重新写构造函数

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("AppBarTitle"),
        ),
        //6,获取到传递过来的值,并使用
        body: this._pageList[this._tabIndex],
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          iconSize: 30,
          fixedColor: Colors.pink,
          currentIndex: _tabIndex,
          onTap: (int index){
            setState(() {
              _tabIndex = index;
            });
          },
          items: [
            ...
          ],
        ),
      );
  }
}

以上。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS小生活 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档