您的位置:首页 > 文旅 > 旅游 > Flutter第十三弹 路由和导航

Flutter第十三弹 路由和导航

2024/12/23 15:51:44 来源:https://blog.csdn.net/joedan0104/article/details/139796949  浏览:    关键词:Flutter第十三弹 路由和导航

目标:

1.Flutter怎么创建路由?

2.怎么实现路由跳转?页面返回?

一、路由

1.1 什么是路由?

路由(Route)在移动开发中通常指页面(Page),在Android中通常指一个Activity。所谓路由管理,就是管理页面之间如何跳转,通常也可被称为导航管理。这和原生开发类似,无论是Android还是iOS,导航管理都会维护一个路由栈,路由入栈(push)操作对应打开一个新页面,路由出栈(pop)操作对应页面关闭操作,而路由管理主要是指如何来管理路由栈。

路由通常通过维护一个路由表,建立页面导航表。

1.2 路由导航

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {// 需要返回MaterialApp,MaterialApp内部已经实现了Navigatorreturn MaterialApp(home: Scaffold(appBar: AppBar(title: Text("首页")),body: Column(children: [Text("这是第一页"),RaisedButton(onPressed: () {/// 实现点击事件/// TODO: 导航跳转第二页debugPrint("导航跳转第二页");// 定义导航路由(导航到SecondRoute)Navigator.push(context, MaterialPageRoute(builder: (_) {return SecondRoute();}));},// 按钮显示内容child: Text("进入第二页"),)],),),);}
}class SecondRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {// 返回页面为脚手架开始,公用MaterialAppreturn Scaffold(appBar: AppBar(title: Text("第二页"),),body: Column(children: [Text("这是第二页"),RaisedButton(onPressed: () {/// 实现点击事件Navigator.pop(context);},// 按钮显示内容(返回上一页)child: Text("返回"),)],),);}
}

新建两个页面,第一个页面点击按钮,跳转第二个页面。

报错信息如下。

1.2.1 导航问题分析

 导航操作请求使用了不包含Navigator的上下文context

`Navigator`实际上也是一个Widget,这个异常出现在`Navigator.of(context)`路由器的获取上,而这句代码会**从当前的context的父级一层层向上去查找一个`Navigator`**,我们当前传递的context就是MyApp,它的父级是root——UI根节点。`Navigator`这个widget的并不是由root创建的,因此在root下一级的上下文中无法获得`Navigator`。

在之前所有的路由案例中,我们的上下文是MainRoute,它的父级是MaterialApp。MaterialApp内部就会创建一个Navigator

MaterialApp->\_MaterialAppState->WidgetsApp->\_WidgetsAppState

所以问题就在于,`Navigator`需要通过MaterialApp或者它孩子的上下文。

1.2.2  导航解决方案

Navigator必须在MaterialApp下一级,这样获取的Element的上下文才是MaterialApp的上下文。

 解决方案一:MaterialApp下body提取一级MainRoute

新的层级结构

root

 |---MaterialApp-->Navigator

           |--------->MainRoute

是指MainRoute的层级在MaterialApp下一级。

这样,MainRoute就能够访问父Element的Navigator。

跳转第二页成功。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {// 需要返回MaterialApp,MaterialApp内部已经实现了Navigatorreturn MaterialApp(home: MainRoute(),);}
}class MainRoute extends StatelessWidget{@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页")),body: Column(children: [Text("这是第一页"),RaisedButton(onPressed: () {/// 实现点击事件/// TODO: 导航跳转第二页debugPrint("导航跳转第二页");// 定义导航路由(导航到SecondRoute)/// 因为context是MyApp的BuildContext,MyApp不包含Navigator,因此报错/// Navigator必须在MaterialApp下一级///Navigator.push(context, MaterialPageRoute(builder: (context) {return SecondRoute();}));// Navigator.push(MaterialPageRoute(//// ))},// 按钮显示内容child: Text("进入第二页"),)],),);}
}class SecondRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {// 返回页面为脚手架开始,公用MaterialAppreturn Scaffold(appBar: AppBar(title: Text("第二页"),),body: Column(children: [Text("这是第二页"),RaisedButton(onPressed: () {/// 实现点击事件Navigator.pop(context);},// 按钮显示内容(返回上一页)child: Text("返回"),)],),);}
}
解决方案二:MaterialApp.Builder构建子树

MaterialApp下的子控件Builder,通过Builder构建的子树,上下文是Builder,因此一定在MaterialApp下面。

1.3 命名路由

给页面增加路由名字,建立路由表。

 1.3.1 注册路由表

MaterialApp.routes注册路由表。

路由表定义路由名称和对应的路由导航页面。

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';void main() {runApp(MyApp());
}class RouteTable {static String ROUTE_MAIN = "/main";static String ROUTE_SECOND = "/second";
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {// 需要返回MaterialApp,MaterialApp内部已经实现了Navigatorreturn MaterialApp(home: MainRoute(),routes: {RouteTable.ROUTE_MAIN: (_) {return new MainRoute();},RouteTable.ROUTE_SECOND: (_) {return new SecondRoute();}},);}
}class MainRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页")),body: Column(children: [Text("这是第一页"),RaisedButton(onPressed: () {/// 实现点击事件/// TODO: 导航跳转第二页debugPrint("命名路由导航跳转第二页");// 定义导航路由(导航到SecondRoute)/// 因为context是MyApp的BuildContext,MyApp不包含Navigator,因此报错/// Navigator必须在MaterialApp下一级/// 命令路由跳转的时候采用路由表Navigator.pushNamed(context, RouteTable.ROUTE_SECOND);// Navigator.push(MaterialPageRoute(//// ))},// 按钮显示内容child: Text("进入第二页"),)],),);}
}class SecondRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {// 返回页面为脚手架开始,公用MaterialAppreturn Scaffold(appBar: AppBar(title: Text("第二页"),),body: Column(children: [Text("这是第二页"),RaisedButton(onPressed: () {/// 实现点击事件Navigator.pop(context);},// 按钮显示内容(返回上一页)child: Text("返回"),)],),);}
}

1.3.2 路由导航

路由导航通过命名路由进行导航。

Navigator.pushNamed(context, RouteTable.ROUTE_SECOND);

二、页面参数返回

在项目中,跳转一个新页面以后,处理完成,回到第一个页面,可能需要处理返回来的参数。

这就需要涉及到页面参数返回和接收。

2.1 返回参数保存

Navigator.pop携带返回结果

class Result {String name;int score;Result(this.name, this.score);@overrideString toString() {return 'Result{name: $name, score: $score}';}
}class SecondRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {// 返回页面为脚手架开始,公用MaterialAppreturn Scaffold(appBar: AppBar(title: Text("第二页"),),body: Column(children: [Text("这是第二页"),RaisedButton(onPressed: () {/// 实现点击事件/// 返回上一个页面,携带处理结果。例如当前处理结果是一个对象Navigator.pop(context, new Result("超新星", 100));},// 按钮显示内容(返回上一页)child: Text("返回"),)],),);}
}

Navigator.pop携带一个结果返回上一页。

2.2 接收返回结果

第一页需要接收页面返回结果

2.2.1 onPress方法修改为异步方法 async

对应异步接收处理的方法,声明为async。

2.2.2 Navigator.push的异步返回结果接收

class MainRoute extends StatelessWidget{@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页")),body: Column(children: [Text("这是第一页"),RaisedButton(/// 1) 修改为异步任务,等待页面返回onPressed: () async {/// 实现点击事件debugPrint("导航跳转第二页");// 定义导航路由(导航到SecondRoute)/// 因为context是MyApp的BuildContext,MyApp不包含Navigator,因此报错/// Navigator必须在MaterialApp下一级/// 2) 通过await等待返回结果///Result result =  await Navigator.push(context, MaterialPageRoute(builder: (context) {return SecondRoute();}));debugPrint("接收结果 result = " + result.toString());},// 按钮显示内容child: Text("进入第二页"),)],),);}
}

返回结果数据,是泛型数据,顶级类Object的子类。因此几乎所有类型都可以。 

三、定制页面切换动画

Material库中提供了MaterialPageRoute,它在Android上会上下滑动切换。如果想自定义路由切换动画,可以使用PageRouteBuilder。

3.1 页面水平切换

导航到下一个页面的时候,增加水平滑动效果。

SlideTransition是水平滑动动画,position定义平移的动画效果。

我们采用Tween补间动画效果。 

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';void main() {runApp(MyApp());
}class RouteTable {/// 首页默认使用 / 定义这个路由的话,MaterialApp的home不需要重复定义static String ROUTE_MAIN = "/";static String ROUTE_SECOND = "/second";
}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {// 需要返回MaterialApp,MaterialApp内部已经实现了Navigatorreturn MaterialApp(home: MainRoute(),// routes: {//   RouteTable.ROUTE_MAIN: (_) {//     return new MainRoute();//   },//   RouteTable.ROUTE_SECOND: (_) {//     return new SecondRoute();//   }// },);}
}class MainRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页")),body: Column(children: [RaisedButton(onPressed: () {/// 实现点击事件debugPrint("命名路由导航跳转第二页");// 定义导航路由(导航到SecondRoute)/// 因为context是MyApp的BuildContext,MyApp不包含Navigator,因此报错/// Navigator必须在MaterialApp下一级/// push 的时候,增加路由跳转动画效果Navigator.push(context, PageRouteBuilder(pageBuilder:(BuildContext context, Animation<double> animation,Animation<double> secondaryAnimation) {return SlideTransition(position: Tween<Offset>(begin: const Offset(1.0, 0.0),end: const Offset(0.0, 0.0),).animate(animation),///  child导航的第二个页面child: SecondRoute(),);}));},// 按钮显示内容child: Text("进入第二页"),)],),);}
}class SecondRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {// 返回页面为脚手架开始,公用MaterialAppreturn Scaffold(appBar: AppBar(title: Text("第二页"),),body: Column(children: [Text("这是第二页"),RaisedButton(onPressed: () {/// 实现点击事件Navigator.pop(context);},// 按钮显示内容(返回上一页)child: Text("返回"),)],),);}
}

需要注意切换到第二个页面,child为SecondRoute

3.2 渐变+滑动动画

在滑动动画外层嵌套一层渐变动画。

child对应滑动动画。

class MainRoute extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("首页")),body: Column(children: [RaisedButton(onPressed: () {/// 实现点击事件debugPrint("命名路由导航跳转第二页");// 定义导航路由(导航到SecondRoute)/// 因为context是MyApp的BuildContext,MyApp不包含Navigator,因此报错/// Navigator必须在MaterialApp下一级/// push 的时候,增加路由跳转动画效果Navigator.push(context,PageRouteBuilder(/// 动画时长transitionDuration: Duration(milliseconds: 500),pageBuilder: (BuildContext context,Animation<double> animation,Animation<double> secondaryAnimation) {/// 嵌套一层渐变动画return FadeTransition(opacity: animation,/// 渐变动画+滑动动画child: SlideTransition(position: Tween<Offset>(begin: const Offset(1.0, 0.0),end: const Offset(0.0, 0.0),).animate(animation),///  child导航的第二个页面child: SecondRoute(),));}));},// 按钮显示内容child: Text("进入第二页"),)],),);}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com