Flutter 路由管理 Route、Navigator 使用示例

路由管理

Flutter 中,页面之间的跳转是通过 RouteNavigator 来管理。
Router是页面的抽象,类似于Android中的Activity页面。该类定义了Navigator上的抽象接口push 和 pop
Navigator是用堆栈规则管理一组child widgetwidget。可以理解为管理页面的打开、关闭

页面跳转示例

页面不传参跳转
  • 不传值跳转
/// 从当前页面跳转到 SecondPage
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage()));
  • 返回不传值
Navigator.pop(context);
页面传参跳转
  • 传值跳转
/// 从当前页面跳转到 SecondPage,并通过构造函数传递参数的方式传递了 hello 字符串
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondPage('hello')));
  • 返回传值
/// 第一个页面跳转处。利用 then 接收 pop 携带回来的参数
Navigator.push(context,
	   MaterialPageRoute(builder: (BuildContext context) {
	 return SecondPage('hello');
	})).then((value) {
	/// value 就是从第二个页面传递回来的值
	 setState(() {
	   mFirstPageMsg = value;
	 });
});

/// 第二个页面,pop 参数
Navigator.pop(context, 'From SecondPage');
Navigator 的其他跳转方式

我们在调用Navigator方法的时候可以看到除了以上push , pop方法之后还有其他的方法.
在这里插入图片描述
获取Navigator实例:

  • of 获取Navigator最近的实例
/// 典型示例:关闭两个页面后跳转到 settings 页面

Navigator.of(context)
  ..pop()
  ..pop()
  ..pushNamed('/settings');

页面跳转:

  • push 页面跳转
  • pushNamed 使用命名路由跳转页面。
/// 典型示例:页面跳转
class WeatherRouteArguments {
   WeatherRouteArguments({ this.city, this.country });
  final String city;
  final String country;

  bool get isGermanCapital {
    return country == 'Germany' && city == 'Berlin';
  }
}

void _showWeather() {
  Navigator.pushNamed(
    context,
   '/weather',
   arguments: WeatherRouteArguments(city: 'Berlin', country: 'Germany'),
  );
}
  • pushAndRemoveUntil 跳转到新的页面,并删除先前的所有路由,常用于退出登录后返回登录页面,这时候关闭登录页面就直接退出App
/// 典型示例:关闭其他页面,回到首页
void _finishAccountCreation() {
     Navigator.pushAndRemoveUntil(
       context,
       MaterialPageRoute(builder: (BuildContext context) => MyHomePage()),
       ModalRoute.withName('/'),
     );
}
  • pushNamedAndRemoveUntil 同上,不过这里是使用命名路由
/// 典型示例:关闭其他页面,回到日历页面
void _resetToCalendar() {
     Navigator.pushNamedAndRemoveUntil(context, '/calendar', ModalRoute.withName('/'));
}
  • pushReplacement 路由替换。
/// 典型示例:完成登录后回到首页
void _completeLogin() {
     Navigator.pushReplacement(
         context, MaterialPageRoute(builder: (BuildContext context) => MyHomePage()));
}
  • pushReplacementNamed 路由替换。跳转到新页面且动画关闭之后之后,调用 dispose 关闭上一个页面.
/// 典型示例:跳转到 brightness 页面且动画关闭之后之后,调用 disposing 关闭上一个页面
void _switchToBrightness() {
    Navigator.pushReplacementNamed(context, '/settings/brightness');
}

页面关闭:

  • pop 页面关闭
  • canPop 判断是否可以导航到新页面
  • maybePop 可能会导航到新页面
  • popAndPushNamed 关闭当前页面,并导航到新页面。
/// 典型示例:关闭当前页面并用 accessibility 页面替换
void _selectAccessibility() {
     Navigator.popAndPushNamed(context, '/settings/accessibility');
}
  • popUntil 页面关闭,反复调用pop 方法直到该函数的参数predicate返回true为止
/// 典型示例如下
void _logout() {
     Navigator.popUntil(context, ModalRoute.withName('/login'));
}

页面移除:

  • removeRoute 页面删除,并执行Route.dispose
  • removeRouteBelow 页面删除,同时执行Route.dispose操作,要移除的页面是参数anchorRouter里面的路由。

页面替换:

  • replace 页面替换,参数中填写新、旧路由。
  • replaceRouteBelow 页面替换,要替换的页面是是参数anchorRouter里面的路由。
无 context 页面跳转

有时候页面跳转是没有context

这里使用GlobalKey的方式实现:

  • 首先创建NavigationService类,并创建staticnavigatorKey实例,后续的页面跳转方法也可封装在这个类中
/// 无 context 跳转页面
class NavigationService {

  static GlobalKey<NavigatorState> navigatorKey =  GlobalKey<NavigatorState>();

/// push 新页面
  static Future<T> navigateTo<T>(Route<T> route) {
    return navigatorKey.currentState.push<T>(route);
  }
  
/// pop 页面
  static bool goBack() {
    return navigatorKey.currentState.pop();
  }
}
  • 然后在main.dart中的MaterialApp下设置全局的navigatorKey,如下图:
    在这里插入图片描述
  • 最后就是调用方法实现页面跳转了
/// 调用已经封装好的方法跳转 ThirdPage 页面
NavigationService.navigateTo(MaterialPageRoute(builder: (context) => ThridPage('word')));

命名路由

直接使用Navigator push进行页面跳转是相当简单的,但是简单的同时也会带来维护的复杂。在页面很多的情况下,每个页面页面跳转都在不同的代码中,而且每次页面跳转都要创建 MaterialPageRoute 实例,还是比较麻烦的。我们在上面的Navigator中也看到一些命名路由的方式实现页面跳转。

那么我们来看一下命名路由如何使用吧:

1. 在 main.dart 的 MaterialApp 中注册路由
MaterialApp(
 ... 
 //注册路由 
 routes:{ 
 	/// 第三个页面的路由
 	"third_page":(context)=>ThridPage(), 
 },
);

2. 使用路由
//使用名字打开页面
Navigator.pushNamed(context,"third_page");
页面跳转传参

//打开页面时传递字符串参数
Navigator.of(context).pushNamed("second_page", arguments: "Hey");

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //取出路由参数
    String msg = ModalRoute.of(context).settings.arguments as String;
    return Text(msg);
  }
}
页面返回传参

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Text('Message from first screen: $msg'),
          RaisedButton(
            child: Text('back'),
            //页面关闭时传递参数
            onPressed: ()=> Navigator.pop(context,"Hi")
          )
        ]
      ));
  }
}

class _FirstPageState extends State<FirstPage> {
  String _msg='';
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: Column(children: <Widget>[
        RaisedButton(
            child: Text('命名路由(参数&回调)'),
            //打开页面,并监听页面关闭时传递的参数
            onPressed: ()=> Navigator.pushNamed(context, "third_page",arguments: "Hey").then((msg)=>setState(()=>_msg=msg)),
        ),
        Text('Message from Second screen: $_msg'),

      ],),
    );
  }
}
命名路由封装

上面这么使用完成了命名路由。但是当页面过多,在routers中的路由就会很多,就会导致main.dart入口文件臃肿。这时候可以考虑将路由封装出去。

这里参考的是Flutter-Go中的实现方式:

  • 首先定义Routers
class Routes {
 ...
 /// 路由名称
  static String thirdPage = '/third-page';

  /// 配置路由名称和 handler(参数)
  static void configureRoutes(Router router) {
  ...
    // 第三页
    router.define(thirdPage,handler:thirdHandler);

  }
}
  • 定义Handler,处理传递的参数
    这里将所有handler统一放到router_handler.dart文件下:
/// 第三个页面的 handler
var thirdHandler = new Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
  
    /// 传递的 msg 参数,这里的参数变量最好是定义为常量,免得修改不同步。
    String msg = params['msg']?.first;
    
    /// 其他参数获取也是一样
    ///  String title = params['title']?.first;
    return new ThridPage(msg);
  },
);
  • 接着在main.dart文件中定义,如下:
    在这里插入图片描述
  • 最后调用就比较简单了
 // 第二个参数后面拼接路由名称和传递的参数
Navigator.pushNamed(context,Routes.thirdPage+'?msg=word');

 // 多个参数传递
 // Navigator.pushNamed(context,Routes.thirdPage+'?msg=word&title=hello');
404 页面处理

由于路由的注册和使用都采用字符串来标识,如果我们打开了一个不存在,默认是会在控制台提示,但是app上没有任何反应。这时候体验就不是很好了。Flutter 提供了UnknownRoute属性,我们可以对未知的路由标识符进行统一的页面跳转处理。

使用方式也很简单:

  • 创建一个 404 页面:
/// 页面路由出错显示的页面
class PageNotFound extends StatelessWidget {

  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("page not found"),
        ),
        body: Container(
            child:  Text("page not found")
        )
    );
  }
}

  • main.dart 中引用
    在这里插入图片描述

返回按钮拦截

WillPopScope,点击两次返回才退出,点击返回出现提示框提示退出

实现可参考:Flutter-WillPopScope-双击返回与界面退出提示

wan~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_龙衣

赏杯快乐水喝喝

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值