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

相关文章

js+css二级导航

效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Con

vue子路由回退后刷新页面方式

最近碰到一个小问题,页面中含有 <transition name="router-slid" mode="out-in"><router-view></router-view></transition> 作为子页面加载显示的地方。但是一般正常子路由通过 this.$router.go(-1) 返回到上一层原先的页面中。通过路由历史返回方式原本父页面想更新数据在created 跟mounted

【第十三课】区域经济可视化表达——符号表达与标注

一、前言 地图最直接的表达就是使用符号表达。使用符号可以把简单的点线面要 素渲染成最直观的地理符号,提高地图的可读性。只要掌握了 ArcGIS 符号制 作的技巧,分析符号并总结出规则,就可以制作符合要求的地图+符号。 (一)符号的选择与修改 符号的选择在制图中至关重要,使用符号选择器对话框可从多个可用样式 中选择符号,并且每个符号都有一个标签用来描述其图形特征,如颜色或类型, 利用这些标签可

Django 路由系统详解

Django 路由系统详解 引言 Django 是一个高级 Python Web 框架,它鼓励快速开发和干净、实用的设计。在 Django 中,路由系统是其核心组件之一,负责将用户的请求映射到相应的视图函数或类。本文将深入探讨 Django 的路由系统,包括其工作原理、配置方式以及高级功能。 目录 路由基础URL 映射路由参数命名空间URL 反向解析路由分发include 路由路由修饰符自

26.3 Django路由层

1. 路由作用 在Django中, URL配置(通常称为URLconf)是定义网站结构的基础, 它充当着Django所支撑网站的'目录'.URLconf是一个映射表, 用于将URL模式(patterns)映射到Python的视图函数或类视图上.这种映射机制是Django处理HTTP请求的基础, 它决定了当客户端发送请求时, Django如何调用相应的视图函数来生成并返回响应.通过URL

ArkTS开发系列之导航 (2.7动画)

上篇回顾: ArkTS开发系列之导航 (2.6 图形) 本篇内容:动画的学习使用 一、 知识储备 1. 布局更新动画 包含显式动画(animateTo)和属性动画(animation) 动画类型名称特点显式动画闭包内的变化都会触发动画执行, 可以做较复杂的动画属性动画属性变化时触发动画执行, 设置简单 说白了,显示动画就是靠闭包事件触发,属性动画是挂在组件身上的属性变化触发 显式动画

项目总结-前端路由hash和history

项目总结-前端路由hash和history router模块 路由需要实现的功能 当浏览器地址发生变化的时候,切换页面点击浏览器后退前进的时候,网页内容发生变化刷新浏览器,网页加载当前路由对应内容 路由主要是通过监听事件,并利用js实现动态改变网页内容,有两种实现方式 hash模式:监听浏览器地址hash地址的变化,执行相应的js切换网页history模式:利用history API实现

Flutter ListView详解

文章示例代码 ListView常用构造 ListView 我们可以直接使用ListView 它的实现也是直接返回最简单的列表结构,粗糙没有修饰。 ListView 默认构建 效果 ///默认构建 Widget listViewDefault(List list) { List _list = new List(); for (int i = 0; i < list.length;

react实现导航守卫

React本身并没有像Vue那样的直接名为“导航守卫”的概念,但在React Router中,我们可以通过特定的方法和技术来模拟和实现类似的功能。以下是对React Router中模拟导航守卫的详解: 1. 导航守卫的概念 导航守卫:在路由切换之前执行的钩子函数,用于控制路由的跳转。在Vue Router中,这通常用于路由鉴权,即在路由跳转之前判断用户是否有权访问目标页面。React中的模拟:

为导航栏的li加上.selected样式

为导航栏的li加上.selected样式 HTML <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>myNav</title><link rel="stylesheet" type="text/css" href="main.css"></head><body><div class="nav-wr