Flutter第十三弹 路由和导航

2024-06-20 03:44

本文主要是介绍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("进入第二页"),)],),);}
}

这篇关于Flutter第十三弹 路由和导航的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1077006

相关文章

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

Flutter Button使用

Material 组件库中有多种按钮组件如ElevatedButton、TextButton、OutlineButton等,它们的父类是于ButtonStyleButton。         基本的按钮特点:         1.按下时都会有“水波文动画”。         2.onPressed属性设置点击回调,如果不提供该回调则按钮会处于禁用状态,禁用状态不响应用户点击。

【vue3|第28期】 Vue3 + Vue Router:探索路由重定向的使用与作用

日期:2024年9月8日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉在这里插入代码片得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.4083;0.98365 = 0.0006 说

flutter开发实战-flutter build web微信无法识别二维码及小程序码问题

flutter开发实战-flutter build web微信无法识别二维码及小程序码问题 GitHub Pages是一个直接从GitHub存储库托管的静态站点服务,‌它允许用户通过简单的配置,‌将个人的代码项目转化为一个可以在线访问的网站。‌这里使用flutter build web来构建web发布到GitHub Pages。 最近通过flutter build web,通过发布到GitHu

Flutter 中的低功耗蓝牙概述

随着智能设备数量的增加,控制这些设备的需求也在增加。对于多种使用情况,期望设备在需要进行控制的同时连接到互联网会受到很大限制,因此是不可行的。在这些情况下,使用低功耗蓝牙(也称为 Bluetooth LE 或 BLE)似乎是最佳选择,因为它功耗低,在我们的手机中无处不在,而且无需连接到更广泛的网络。因此,蓝牙应用程序的需求也在不断增长。 通过阅读本文,您将了解如何开始在 Flutter 中开

HCIA--实验十:路由的递归特性

递归路由的理解 一、实验内容 1.需求/要求: 使用4台路由器,在AR1和AR4上分别配置一个LOOPBACK接口,根据路由的递归特性,写一系列的静态路由实现让1.1.1.1和4.4.4.4的双向通信。 二、实验过程 1.拓扑图: 2.步骤: (下列命令行可以直接复制在ensp) 1.如拓扑图所示,配置各路由器的基本信息: 各接口的ip地址及子网掩码,给AR1和AR4分别配置

flutter开发多端平台应用的探索 下 (跨模块、跨语言通信之平台通道)

前文 Flutter 是一个跨平台的开发框架,它允许开发者使用相同的代码库来构建 iOS、Android、Web 和桌面应用程序。 上文flutter开发多端平台应用的探索 上(基本操作)-CSDN博客列举了一些特定平台的case(桌面端菜单,鼠标快捷键)的使用方法,有些是flutter提供了对应能力,只需要学习如何调API,有些事三方库支持,本文要探讨的平台通道是更为强大的工具,很多三方插件

Flutter-使用dio插件请求网络(get ,post,下载文件)

引入库:dio: ^2.1.13可直接运行的代码:包含了post,get 下载文件import 'package:flutter/material.dart';import 'package:dio/dio.dart';void main() {runApp(new MaterialApp(title: 'Container demo',home: new visitNetPage(),)

Flutter-选择附件,图片,视频。file_picker

仅供参考: 引入插件: file_picker: ^1.3.8 按照返回值,分了三组: // Single file path String filePath;第一组:返回文件地址 //选择任何文件 filePath = await FilePicker.getFilePath(type: FileType.ANY); // will let you pick one file path, fr