Flutter第十三弹 路由和导航

news2024/11/22 20:12:49

目标:

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 {
  @override
  Widget build(BuildContext context) {
    // 需要返回MaterialApp,MaterialApp内部已经实现了Navigator
    return 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 {
  @override
  Widget build(BuildContext context) {
    // 返回页面为脚手架开始,公用MaterialApp
    return 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 {
  @override
  Widget build(BuildContext context) {
    // 需要返回MaterialApp,MaterialApp内部已经实现了Navigator
    return MaterialApp(
      home: MainRoute(),
    );
  }
}

class MainRoute extends StatelessWidget{
  @override
  Widget 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 {
  @override
  Widget build(BuildContext context) {
    // 返回页面为脚手架开始,公用MaterialApp
    return 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 {
  @override
  Widget build(BuildContext context) {
    // 需要返回MaterialApp,MaterialApp内部已经实现了Navigator
    return MaterialApp(
      home: MainRoute(),
      routes: {
        RouteTable.ROUTE_MAIN: (_) {
          return new MainRoute();
        },
        RouteTable.ROUTE_SECOND: (_) {
          return new SecondRoute();
        }
      },
    );
  }
}

class MainRoute extends StatelessWidget {
  @override
  Widget 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 {
  @override
  Widget build(BuildContext context) {
    // 返回页面为脚手架开始,公用MaterialApp
    return 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);

  @override
  String toString() {
    return 'Result{name: $name, score: $score}';
  }
}

class SecondRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 返回页面为脚手架开始,公用MaterialApp
    return 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{
  @override
  Widget 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 {
  @override
  Widget build(BuildContext context) {
    // 需要返回MaterialApp,MaterialApp内部已经实现了Navigator
    return MaterialApp(
      home: MainRoute(),
      // routes: {
      //   RouteTable.ROUTE_MAIN: (_) {
      //     return new MainRoute();
      //   },
      //   RouteTable.ROUTE_SECOND: (_) {
      //     return new SecondRoute();
      //   }
      // },
    );
  }
}

class MainRoute extends StatelessWidget {
  @override
  Widget 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 {
  @override
  Widget build(BuildContext context) {
    // 返回页面为脚手架开始,公用MaterialApp
    return 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 {
  @override
  Widget 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("进入第二页"),
          )
        ],
      ),
    );
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1841257.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

wsl报错在BIOS中启用虚拟化

解决&#xff1a; Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All 以高级管理员身份运行powershell&#xff0c;执行如上命令。

GoldWave软件下载 GoldWave 音频处理软件 v6.80.0 官方版下载附加详细安装步骤

准确来讲GoldWave软件防止GoldWave *原生音频插件被禁用。根据大数据结果显示回声效果&#xff1a;回声&#xff0c;顾名思义是指声音发出后经过一定的时间再返回被我们听到&#xff0c;就象在旷野上面对高山呼喊一样&#xff0c;在很多视频剪辑、配音中广泛采用&#xff0c; G…

最新版本IntelliJ IDEA安装与“坤活”使用

最新版本IntelliJ IDEA安装与“科学”使用 IntelliJ IDEA安装与坤活下载安装坤活idea1.将下面两个压缩文件解压到安装位置&#xff0c;注意路径不要包含中文空格等特殊符号2.双击 install-all-users.vbs &#xff0c;然后点击确定&#xff0c;等到出现 Done的弹窗3. 打开idea复…

【ajax基础04】form-serialize插件

目录 一&#xff1a;form-serialize插件 作用&#xff1a; 语法格式&#xff1a; 一&#xff1a;form-serialize插件 作用&#xff1a; 快速且大量的收集表单元素的值 例如上图对于多表单元素的情形&#xff0c;单靠通过”选择器获取节点.value”值的形式&#xff0c;获取…

Blazor 组件:创建、生命周期、嵌套和 UI 集成

在本文中&#xff0c;您将获得以下问题的答案。 什么是 Blazor 组件&#xff1f;如何使用组件&#xff1f;Blazor 组件的生命周期是什么&#xff1f;我们可以从一个组件调用另一个组件吗&#xff1f;如何创建 Blazor 组件&#xff1f;在组件中哪里写 C# 代码&#xff1f; 什么…

百度文心智能体平台(想象即现实):轻松上手,开启智能新时代!创建属于自己的智能体应用。

目录 1.1、文心智能体平台 1.2、创建智能体 1.3、智能体报名入口 1.4、古诗词小助手 1.5、访问我的智能体 在这个全新的时代里&#xff0c;人工智能技术正以前所未有的速度发展&#xff0c;渗透到我们生活的方方面面。无论是智能家居、自动驾驶&#xff0c;还是医疗诊断、…

漏洞挖掘 | 记一次src挖掘-小程序敏感信息泄露

权当是一次漏洞挖掘的思路分享 闲言 就现在的一个web漏洞挖掘强度还是非常高的&#xff0c;所以我们不妨把我们的眼光投向一个之前可能未曾涉及到的区域———小程序 是的微信小程序&#xff0c;这玩意的防范能力和过滤能力其实对比web方向是要弱小很多的 进入正题 以下就是…

如何设置Excel单元格下拉列表

如何设置Excel单元格下拉列表 在Excel中设置单元格下拉列表可以提高数据输入的准确性和效率。以下是创建下拉列表的步骤&#xff1a; 使用数据验证设置下拉列表&#xff1a; 1. 选择单元格&#xff1a; 选择你想要设置下拉列表的单元格或单元格区域。 2. 打开数据验证&…

充电学习—8、Type-C TCPC TCPCI

TCPC是usb Type-C port controller&#xff1b; 通用串行总线C型端口控制器 TCPCI是tcpc控制器接口规范&#xff1b; TCPC是个功能块&#xff0c;其中含有VBUS和VCONN的电源控制功能&#xff0c;CC信号的处理 逻辑&#xff0c;PD应用中的BMC物理层和协议层&#xff08;PD信息…

配置环境常规操作

一、看看显卡情况 1、看显卡驱动&#xff1a; nvidia-smi 2、验证cuda是否安装成功 nvcc -V 二、conda创建环境 conda create --name PatchCore_anomaly_detection python3.9 conda activate PatchCore_anomaly_detection 三、配置虚拟环境 cd C:\BaiduNetdiskDownload…

OSPF和RIP的路由引入(华为)

#交换设备 OSPF和RIP的路由引入 不同的网络会根据自身的实际情况来选用路由协议。比如有些网络规模很小&#xff0c;为了管理简单&#xff0c;部署了 RIP; 而有些网络很复杂&#xff0c;可以部署 OSPF。不同路由协议之间不能直接共享各自的路由信息&#xff0c;需要依靠配置路…

海外优青ppt美化_海优ppt录音视频制作

海外优青 优秀青年科学基金项目&#xff08;海外&#xff09;旨在吸引和鼓励在自然科学、工程技术等方面已取得较好成绩的海外优秀青年学者&#xff08;含非华裔外籍人才&#xff09;回国&#xff08;来华&#xff09;工作&#xff0c;自主选择研究方向开展创新性研究&#xf…

win 打包java项目为exe一键部署,包括mysql和redis

需求&#xff1a;打包springboot项目在win系统下执行&#xff0c;并且要一键部署和开机启动 把所需的程序放在同一个文件夹 1.jdk文件夹&#xff1a;自己去下载&#xff0c;jdk8的话拿jre目录好了 2.mysql文件夹&#xff1a;是8.0.36版&#xff0c;270M精简版了 3.redis文件夹…

Springboot整合Zookeeper分布式组件实例

一、Zookeeper概述 1.1 Zookeeper的定义 Zookeeper是一个开源的分布式协调服务&#xff0c;主要用于分布式应用程序中的协调管理。它由Apache软件基金会维护&#xff0c;是Hadoop生态系统中的重要成员。Zookeeper提供了一个高效且可靠的分布式锁服务&#xff0c;以及群集管理…

深入探讨:UART与USART在单片机中串口的实际应用与实现技巧

单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种集成了处理器、存储器和输入输出接口的微型计算机。它广泛应用于嵌入式系统中&#xff0c;用于控制各类电子设备。UART和USART是单片机中常见的通信接口&#xff0c;负责串行数据传输。下面我们详细介绍它们在单…

【机器学习系列】Python实战:使用GridSearchCV优化AdaBoost分类器及其基分类器

目录 一、AdaBoost的标准实现中是否支持使用不同类型的基分类器&#xff1f; 二、Adaboost的参数 三、Python实现Adaboost (一)导入库和数据集 &#xff08;二&#xff09; 划分训练集 &#xff08;三&#xff09;选择基分类器--决策树 &#xff08;四&#xff09;创建Ada…

数据结构_栈和队列

目录 一、栈 1.1 栈的使用 1.2 模拟实现栈 二、队列 2.1 队列的使用 2.2 环形队列 2.3 双端队列 总结 一、栈 栈是只允许在固定的一端进行元素的插入和删除操作的一种特殊线性表。其中进行元素的插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈遵循先进后…

MQTT服务器/MQTT_C#客户端/Websoket连MQTT

1 . 搭建MQTT服务器 找到上传中的 emqx-5.3.2-windows-amd64 打开bin如下: 链接: emqx-5.3.2-windows-amd64 如果安装失败 在上传中找到链接: VC_redist.x64.exe 安装。 正确后在浏览器输入 http://127.0.0.1:18083 会有如下mqtt服务端管理页面: 进入客户端认证,创建一个…

72. UE5 RPG 实现召唤技能数量的限制,并优化技能相关

在上一篇文章里&#xff0c;我们实现了召唤技能&#xff0c;并且能够无限的召唤。所以&#xff0c;这属于一个bug&#xff0c;我们不能无限制的去召唤&#xff0c;这会影响游戏的体验。所以&#xff0c;在这篇里面&#xff0c;我们实现一下对召唤物数量的限制&#xff0c;并优化…

32.双击列表启动目标游戏

上一个内容&#xff1a;31.加载配置文件中的游戏到辅助列表 以 31.加载配置文件中的游戏到辅助列表 它的代码为基础进行修改 效果图&#xff1a; 添加列表双击事件 实现代码&#xff1a; LPNMITEMACTIVATE pNMItemActivate reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR…