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 {
  @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("进入第二页"),
          )
        ],
      ),
    );
  }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是Linux挂载

首先先说一下在Linux中一切皆文件&#xff08;硬件设备也是文件&#xff09;&#xff0c;所有文件都是存放在以根目录为树形目录结构中&#xff1b;下面来说说一下什么是挂载 挂载&#xff1a;指的就是将设备文件中的顶级目录连接到 Linux 根目录下的某一目录&#xff08;最好是…

5.音视频基础 FLV

目录 简说FLV FLV Header FLV Body Tag Header ​编辑Tag Data Audio Data Video Data Script Data 简说FLV FLV格式可以包含音频、视频和文本数据&#xff0c;并且可以在网络上进行流媒体传输。优点是文件大小较小&#xff0c;压缩效率高&#xff0c;并且可以在较低…

深度解析ISO9001质量管理体系认证的核心优势

ISO9001质量管理体系认证是一项全球通用的标准&#xff0c;旨在帮助企业优化质量管理&#xff0c;提升市场竞争力。本文将详细解析ISO9001认证为企业带来的多重核心优势。 首先&#xff0c;ISO9001认证显著提升了企业的产品和服务质量。通过建立和实施系统化的质量管理流程&…

为数据安全护航,袋鼠云在数据分类分级上的探索实践

在大数据时代&#xff0c;数据具有多源异构的特性&#xff0c;且价值各异&#xff0c;企业需依据数据的重要性、价值指数等予以区分&#xff0c;以利采取不同的数据保护举措&#xff0c;避免数据泄露。故而&#xff0c;数据分类分级管理属于数据安全保护中极为重要的环节之一。…

小白速成AI大模型就看这份资源包

前言 在数字化浪潮席卷全球的今天&#xff0c;人工智能&#xff08;AI&#xff09;技术已成为推动社会进步的重要引擎。尤其是AI大模型&#xff0c;以其强大的数据处理能力和广泛的应用前景&#xff0c;吸引了无数人的目光。然而&#xff0c;对于初学者“小白”来说&#xff0…

面向AI时代的软件开发新范式

作为一名软件开发者&#xff0c;有幸站在了AI时代的风口浪尖。在这篇博客中&#xff0c;我将分享我的个人看法&#xff0c;一起走向AI时代软件开发新范式。 首先&#xff0c;我们要明确软件开发活动产生的各种制品&#xff0c;都是人类知识的载体&#xff0c;也是人类文明的高级…

22种常用设计模式示例代码

文章目录 创建型模式结构型模式行为模式 仓库地址https://github.com/Xiamu-ssr/DesignPatternsPractice 参考教程 refactoringguru设计模式-目录 创建型模式 软件包复杂度流行度工厂方法factorymethod❄️⭐️⭐️⭐️抽象工厂abstractfactory❄️❄️⭐️⭐️⭐️生成器bui…

【Linux】Xshell和Xftp简介_安装_VMware虚拟机使用

1、简介 Xshell简介 Xshell是一款强大的安全终端模拟软件支持SSH1、SSH2以及Microsoft Windows平台的TELNET协议。该软件通过互联网实现到远程主机的安全连接&#xff0c;并通过其创新性的设计和特色帮助用户在复杂的网络环境中高效工作。Xshell可以在Windows界面下访问远端不…

阿里云域名解析

阿里云域名控制台&#xff1a;https://dc.console.aliyun.com/next/index#/domain-list/all

【两数之和】

两数之和 一、题目二、暴力解法三、哈希表四、map字典1.基本方法.set()添加键值对.get()通过键获取值.has()判断map是否有这个键 2.map和set的联系和区别共同点共同点MapSet 一、题目 二、暴力解法 三、哈希表 解题思路&#xff1a;将nums的元素依次以键值对的方式存储在map字典…

Hadoop升级失败,File system image contains an old layout version -64

原始版本 Hadoop 3.1.3 升级版本 Hadoop 3.3.3 报错内容如下 datasophon 部署Hadoop版本 查看Hadoop格式化版本 which hadoop-daemon.sh/bigdata/app/hadoop-3.1.3/sbin/hadoop-daemon.sh删除原来的旧版本 rm -rf /bigdata/app/hadoop-3.1.3查看环境变量 env|grep HADOOPHAD…

VMware虚拟机三种网络模式设置 - Bridged(桥接模式)

一、前言 由于linux目前很热门&#xff0c;越来越多的人在学习linux&#xff0c;但是买一台服务放家里来学习&#xff0c;实在是很浪费。那么如何解决这个问题&#xff1f;虚拟机软件是很好的选择&#xff0c;常用的虚拟机软件有vmware workstations和virtual box等。 在使用虚…

五种HTTP数据传输方式

在前端开发过程中,后端主要提供 http 接口来传输数据,而这种数据传输方式主要有五种: url paramqueryform-urlencodedform-datajson 下面就让我们一起来了解一下在Nest.js中如何使用这五种HTTP数据传输方式: 一,创建项目 使用nest new 创建一个nest的项目 nest new 项目名称 …

基于IDEA的Maven(properties属性配置)

&#xff08;property &#xff1a;财产&#xff09;properties&#xff1a;它的复数。 同样也是基于上篇博客进行学习。&#xff08;具体的全部项目代码和结构可以去查看上篇...&#xff09; <properties><!--当前jdk版本 , 这一步可以完全省略--><maven.com…

找不到concrt140.dll无法继续执行代码的几种解决方法

在数字时代&#xff0c;电脑用户经常会遇到各种技术问题&#xff0c;其中DLL文件缺失是最常见的问题之一。今天&#xff0c;我们将重点介绍CONCRT140.DLL文件的重要性&#xff0c;以及当它丢失时对电脑的影响。同时&#xff0c;我们提供了五种解决方法和预防措施&#xff0c;帮…

vue3+ts 使用vue3-ace-editor实现Json编辑器

1、效果图 输入代码&#xff0c;点击格式化就出现以上效果&#xff0c;再点击压缩&#xff0c;是以下效果2、安装 npm i vue3-ace-editor 3、使用 新建aceConfig.js文件 // ace配置&#xff0c;使用动态加载来避免第一次加载开销 import ace from ace-builds// 导入不同的主…

从钉钉到跨境电商领域的技术演变,HHO如何通过NineData实现全球化业务布局

两氢一氧&#xff08;HHO&#xff09;是一家跨境出海电商平台&#xff0c;专注于通过数字化手段连接全球市场和中国优质供应链&#xff0c;致力于打造数字化时代的全球化新品牌。 创始人陈航&#xff0c;曾任钉钉 CEO 并成功打造行业领先的亿级活跃用户产品--钉钉。离开阿里后创…

Elasticsearch安装(windows)

先给出网址 elasticsearch&#xff1a;Download Elasticsearch | Elastic elasticKibana&#xff1a;Download Kibana Free | Get Started Now | Elastic Logstash&#xff1a;Download Logstash Free | Get Started Now | Elastic ik分词&#xff1a;Releases infinilabs/…

嵌入式调试接口

嵌入式系统的开发和调试是一项复杂的任务&#xff0c;需要强大的工具和接口来确保硬件和软件的正确性。在这个领域&#xff0c;JTAG&#xff08;Joint Test Action Group&#xff09;和SWD&#xff08;Serial Wire Debug&#xff09;是两个常用的调试接口标准&#xff0c;它们在…

jeecg spring数据源用户名和密码加密 避免明文安全漏洞

1.目的 由于系统部署在互联网&#xff0c;配置文件中的数据库账号密码使用明文&#xff0c;存在安全隐患&#xff0c;做等保测试时要求对其加密。 2.实现方法 Jeecg框架本身有PasswordUtil可以使用PBEWITHMD5andDES进行加密&#xff0c;这里为方便改造&#xff0c;且安全性较…