Flutter:路由、拦截返回事件、自定义独立路由

news2024/12/28 18:41:15

路由堆栈

Flutter 路由管理中有两个非常重要的概念:

  • Route:路由是应用程序页面的抽象,对应 Android 中 Activity 和 iOS 中的 ViewController,由 Navigator 管理。
  • Navigator:Navigator 是一个组件,管理和维护一个基于堆栈的历史记录,通过 push 和 pop 进行页面的跳转。

简单示例

class _YcHomeBodyState extends State<YcHomeBody> {
  
  Widget build(BuildContext context) {
    return Center(
      child: ElevatedButton(
        onPressed: () {
          // 使用PageRouteBuilder来加入淡入淡出的切换效果
          Navigator.of(context).push(PageRouteBuilder(
            transitionDuration: const Duration(milliseconds: 500),
            transitionsBuilder:
                (context, animation, secondaryAnimation, child) {
              return FadeTransition(
                opacity: animation,
                child: child,
              );
            },
            pageBuilder: (context, animation, secondaryAnimation) =>
                const SecondPage(),
          ));
        },
        child: const Text("跳转到B页面"),
      ),
    );
  }
}

class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("B页面", style: TextStyle(color: Colors.white)),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: const Text("返回主页面"),
        ),
      ),
    );
  }
}

在这里插入图片描述

命名路由

使用命名路由需要在 MaterialApp 中配置路由名称

配置

MaterialApp(
      title: 'Flutter Demo',
      routes: <String, WidgetBuilder>{
        '/A': (context) => APage(),
        '/B': (context) => BPage(),
      },
      home: Scaffold(
        body: APage(),
      ),
    )

跳转方法

Navigator.of(context).pushNamed('/B');

// 页面替换,一般用于不希望再返回上一个页面,比如欢迎页和登录页
Navigator.of(context).pushReplacementNamed('/B');

// 用于判断路由是否可以出栈
Navigator.of(context).canPop()

// 路由出栈
Navigator.of(context).pop();

// 实现在跳转到新页面的同时,将之前的所有页面都清除掉,只保留当前页面
// 比如:应用程序进入首页,点击登录进入登录页面,然后进入注册页面或者忘记密码页面...,登录成功后进入其他页面,此时不希望返回到登录相关页面,此场景可以使用
Navigator.pushNamedAndRemoveUntil

全局路由切换动画

使用系统提供的路由切换动画

淡入淡出动画

MaterialApp(
      routes: <String, WidgetBuilder>{
        '/A': (context) => const YcHomePage(),
        '/B': (context) => const SecondPage(),
      },
      // 设置页面过度主题
      theme: ThemeData(
        pageTransitionsTheme: const PageTransitionsTheme(
          builders: {
            TargetPlatform.android:FadeUpwardsPageTransitionsBuilder(),
            TargetPlatform.iOS:FadeUpwardsPageTransitionsBuilder()
          }
        )
      ),
      //指定显示哪一个页面
      home: const YcHomePage(),
    );
  }
}
  • FadeUpwardsPageTransitionsBuilder:在页面切换时实现从下往上淡入的效果。
  • OpenUpwardsPageTransitionsBuilder:用于构建从下往上的页面转场动画。在这种动画中,新页面从屏幕底部向上滑动,同时旧页面会向上淡出。
  • ZoomPageTransitionsBuilder:在页面切换时实现缩放效果。
  • CupertinoPageTransitionsBuilder:IOS风格的页面切换动画

路由传参

构造函数传参

此种方式无法用于命名路由的跳转方式

Navigator.of(context).push(MaterialPageRoute(builder: (context){
  return ProductDetail(productInfo: productInfo,);
}));

class ProductDetail extends StatelessWidget {
  final ProductInfo productInfo;

  const ProductDetail({Key key, this.productInfo}) : super(key: key);
  
  
  Widget build(BuildContext context) {
    return Container();
  }
} 

通过命名路由设置参数的方式

主页面 - > B页面

 // 发送参数
 Navigator.of(context).pushNamed('/B', arguments: '来自主页面');
 // 接收参数
 Text("B页面-${ModalRoute.of(context)?.settings.arguments}")

B页面 - > 主页面

 // 发送参数
Navigator.of(context).pop('从B页面返回');
 // 接收参数
 ElevatedButton(
        onPressed: () async {
          String res = await Navigator.of(context).pushNamed('/B', arguments: '来自主页面') as String;
         setState((){
           title = res.toString();
         });
        },
         child:  Text(title),
),

在这里插入图片描述

监听应用程序路由堆栈变化

具体内容见:监听应用程序路由堆栈变化

自定义RouteObserver,继承RouteObserver并重写其中的方法:

class MyRouteObserver<R extends Route<dynamic>> extends RouteObserver<R> {
  
  void didPush(Route route, Route? previousRoute) {
    super.didPush(route, previousRoute);
    print('didPush route: $route,previousRoute:$previousRoute');
  }

  
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    print('didPop route: $route,previousRoute:$previousRoute');
  }

  
  void didReplace({Route? newRoute, Route? oldRoute}) {
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    print('didReplace newRoute: $newRoute,oldRoute:$oldRoute');
  }

  
  void didRemove(Route route, Route? previousRoute) {
    super.didRemove(route, previousRoute);
    print('didRemove route: $route,previousRoute:$previousRoute');
  }

  
  void didStartUserGesture(Route route, Route? previousRoute) {
    super.didStartUserGesture(route, previousRoute);
    print('didStartUserGesture route: $route,previousRoute:$previousRoute');
  }

  
  void didStopUserGesture() {
    super.didStopUserGesture();
    print('didStopUserGesture');
  }
}

使用

void main() {
  runApp(MyApp());
}

MyRouteObserver<PageRoute> myRouteObserver = MyRouteObserver<PageRoute>();

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      navigatorObservers: [myRouteObserver],
      initialRoute: '/A',
      home: APage(),
    );
  }
}

拦截返回事件

WillPopScope 是 Flutter 中的一个小部件,它可以用来控制 Android 和 iOS 平台上的返回按钮行为。通常情况下,当用户点击返回按钮时,应用程序会关闭当前页面并返回上一个页面。但是,有时候我们需要在用户点击返回按钮时执行一些自定义的操作,例如弹出一个确认对话框或者执行一些数据保存操作等。

WillPopScope 就是用来实现这种自定义返回按钮行为的。它可以监听用户点击返回按钮的事件,并根据需要执行一些操作。在 WillPopScope 中,我们可以通过 onWillPop 回调函数来处理返回按钮事件。如果 onWillPop 返回 true,则表示允许关闭当前页面并返回上一个页面;如果返回 false,则表示不允许关闭当前页面。

通常,它被放置在主页面的Scaffoldbody属性中,以便在用户按下返回按钮时,可以在Scaffold中处理返回操作。

Scaffold(
   //导航条
   appBar: AppBar(
     title: const Text("路由", style: TextStyle(color: Colors.white)),
   ),
   //页面主题内容
   body: WillPopScope(
     onWillPop: () async {
       bool confirmExit = await showDialog(
           context: context,
           builder: (content) {
             return AlertDialog(
               title: const Text('确定要推出吗?'),
               actions: [
                 ElevatedButton(
                     child: const Text('退出'),
                     onPressed: () => Navigator.of(context).pop(true)),
                 ElevatedButton(
                     child: const Text('取消'),
                     onPressed: () => Navigator.of(context).pop(false)),
               ],
             );
           });
       return confirmExit;
     },
     child: const YcHomeBody(),
   ));

在这里插入图片描述

自定义独立路由

Navigator 是管理路由的控件,通常情况下直接使用Navigator.of(context)的方法来跳转页面,之所以可以直接使用Navigator.of(context)是因为在WidgetsApp中使用了此控件,应用程序的根控件通常是MaterialAppMaterialApp包含WidgetsApp,所以可以直接使用Navigator的相关属性。

Navigator用法非常简单,如下:

Navigator(
  initialRoute: '/',
  onGenerateRoute: (RouteSettings settings) {
    WidgetBuilder builder;
    switch (settings.name) {
      case 'home':
        builder = (context) => PageA();
        break;
      case 'user':
        builder = (context) => PageB();
        break;
    }
    return MaterialPageRoute(builder: builder, settings: settings);
  },
)

initialRoute表示初始化路由,onGenerateRoute表示根据RouteSettings生成路由。

常见场景

那么在什么情况下需要使用Navigator?在需要局部页面跳转的地方使用Navigator,如下面的场景:

头条客户端举报场景
头条客户端每一个新闻下面都有一个“叉号”,点击弹出相关信息,点击其中的举报,会在当前小窗户内跳转到举报页面并不是全屏切换页面,而是仅仅在当前弹出的页面进行切换。

主页面

class _YcHomeBodyState extends State<YcHomeBody> {
  
  Widget build(BuildContext context) {
    return Center(
      child: SizedBox(
        height: 350,
        width: 300,
        child: Navigator(
          initialRoute: '/',
          onGenerateRoute: (RouteSettings settins) {
            // 设置默认值
            WidgetBuilder builder = (content) => const Center();
            switch (settins.name) {
              case '/':
                builder = (context) => const SecondPage();
                break;
            }
            return MaterialPageRoute(builder: builder);
          },
        ),
      ),
    );
  }
}

SecondPage页面

class SecondPage extends StatelessWidget {
  const SecondPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Center(
        child: SizedBox(
      height: 100,
      child: Card(
        child: Column(
          children: <Widget>[
            _buildItem(Icons.access_alarm, '举报', '标题夸张,内容质量差 等',
                showArrow: true, onPress: () {
              Navigator.of(context).push(MaterialPageRoute(builder: (context) {
                return const ThirdPage();
              }));
            }),
          ],
        ),
      ),
    ));
  }

  _buildItem(IconData iconData, String title, String content,
      {bool showArrow = false, required VoidCallback onPress}) {
    return Row(
      children: <Widget>[
        Icon(iconData),
        const SizedBox(
          width: 20,
        ),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text(
                title,
                style: const TextStyle(fontSize: 18),
              ),
              Text(
                content,
                style: TextStyle(
                    color: Colors.black.withOpacity(.5), fontSize: 14),
              )
            ],
          ),
        ),
        !showArrow
            ? Container()
            : IconButton(
                icon: const Icon(Icons.arrow_forward_ios),
                iconSize: 16,
                onPressed: onPress,
              ),
      ],
    );
  }
}

ThirdPage

class ThirdPage extends StatelessWidget {
  const ThirdPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      height: 50,
      width: 250,
      color: Colors.grey.withOpacity(.5),
      child: Column(
        children: <Widget>[
          Row(
            children: <Widget>[
              IconButton(
                icon: const Icon(Icons.arrow_back_ios),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
              const Text("抱歉系统升级中,暂时无法举报")
            ],
          ),
        ],
      ),
    );
  }
}

请添加图片描述

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

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

相关文章

哈希表--想彻底了解哈希表,看这一篇文章就可以了

为了一次存储便能得到所查记录&#xff0c;在记录的存储位置和它的关键字之间建立一个确定的对应关系H&#xff0c;已H&#xff08;key)作为关键字为key的记录在表中的位置&#xff0c;这个对应关系H为哈希&#xff08;Hash&#xff09;函数&#xff0c; 按这个思路建立的表为哈…

【Leetcode】 1071. 字符串的最大公因子

For two strings s and t, we say “t divides s” if and only if s t ... t (i.e., t is concatenated with itself one or more times). Given two strings str1 and str2, return the largest string x such that x divides both str1 and str2. Example 1: Input: s…

开发改了接口,经常忘通知测试的解决方案!

目录 前言&#xff1a; Apifox解决方案 Apifox对此给出的解决方案是&#xff1a; 用Apifox怎么处理接口变更 接口代码实现逻辑修改 接口参数修改 前言&#xff1a; 在开发过程中&#xff0c;接口变动十分频繁&#xff0c;测试人员没有及时获得相关通知的情况也很普遍。这…

这一次AI应该是真正的已经到来

渐渐感觉这一次AI的变革能真正的突破迷雾&#xff0c;迎来真正的人工智能时代的到来。所以写篇博文学习一下。经过半年的发酵与发展&#xff0c;不得不说AI已经成为了不可逆转的趋势。之所以说这一次AI应该是真正的已经到来&#xff0c;是因为人工智能的发展其实已经经历了几十…

【Python】一文带你了解 正则表达式 + 简单操作

作者主页&#xff1a;爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

通过第三方软件修改 MacOS 的键盘映射

通过第三方软件修改 MacOS 的键盘映射 由于文本编辑时大量使用word level的左移、右移&#xff0c;其中&#xff1a; OSX的单词级左右移为option Left/Right Arrow&#xff0c;整行级左右移为command Left/Right Arrow 单词级移动与进行编辑常用的command不同键位&#xff0c…

Chat2DB:阿里巴巴开源的聊天数据管理工具--实践

Chat2DB&#xff1a;阿里巴巴开源的聊天数据管理工具–实践 简介 ​ Chat2DB 是一款有开源免费的多数据库客户端工具&#xff0c;支持windows、mac本地安装&#xff0c;也支持服务器端部署&#xff0c;web网页访问。和传统的数据库客户端软件Navicat、DBeaver 相比Chat2DB集成了…

Jetpack Compose ——Row

当我们构建界面时&#xff0c;经常需要在Compose中使用Row布局来水平排列多个组件。Row提供了一种方便的方式来管理和定位子组件&#xff0c;使它们按照我们期望的方式呈现。 在Compose中&#xff0c;Row可以接受多个子组件作为参数&#xff0c;并根据指定的布局规则进行排列。…

桥梁监测方案-智慧桥梁监测管理系统

桥梁作为现代交通基础设施的重要组成部分&#xff0c;承载着大量的车辆和行人交通。然而&#xff0c;随着时间的推移&#xff0c;桥梁结构可能会受到自然环境、交通负荷和物质疲劳等因素的影响&#xff0c;从而导致潜在的风险和结构损坏。桥梁监测能够通过实时监测桥梁的结构状…

git一定要学会,加油

gitgit文档http://file:///F:/%E8%B5%84%E6%96%99%E5%A4%8D%E4%B9%A0/Git%E4%BC%98%E7%A7%80%E5%BC%80%E6%BA%90%E4%B9%A6%E7%B1%8D/Git%E5%BC%80%E6%BA%90%E4%B9%A6%E7%B1%8D/Pro%20Git%E4%B8%AD%E6%96%87PDF%E7%89%88.pdf init 初始化仓库 这个命令在当前目录下初始化一个 G…

安装Django 3.2的过程记录~

首先看下Django 3.2目前更新到哪些版本&#xff0c;用下面这条命令&#xff1a; pip install Django100.0.5与3.2有关的版本号有&#xff1a; 3.2a1, 3.2b1, 3.2rc1, 3.2, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.2.10, 3.2.11, 3.2.12, 3.2.13, …

MAC系统使用

查看端口占用情况 //如查看8080端口的占用情况 sudo lsof -i tcp:9901//比如我们想要释放Java占用的9901端口 PID 是 12420 kill -9 12420

C语言:写一个函数,实现一个整型有序数组的二分查找

题目&#xff1a; 写一个函数&#xff0c;实现一个整型有序数组的二分查找&#xff0c;找出要找的数字在数组中对应的下标。 思路&#xff1a; 总体思路&#xff1a; &#xff08;一&#xff09;自定义函数部分&#xff1a; &#xff08;1&#xff09;. 参数&#xff1a; int…

融云《社交泛娱乐出海作战地图》,手把手教你拿下出海“最后一公里”难题

社交泛娱乐出海&#xff0c;已然是当下互联网出海的主力。关注【融云全球互联网通信云】了解更多 不少国内互联网巨头都将社交泛娱乐作为海外市场重点布局赛道。从较为传统的音视频、短视频社交出海&#xff0c;到逐渐直播出海、元宇宙社交出海&#xff0c;社交泛娱乐出海版图…

微信小程序实现tab及瀑布流页面

一、效果 二、代码 复制代码可以直接用 1、json代码 {"usingComponents": {},"navigationStyle": "custom" } 2、xml代码 <!--pages/find_module/findPage.wxml--> <view class"container_serach"><!-- 搜索模块 -…

Java EE 企业级应用开发教程题库(第二版)

前言 Java EE这是一门偏向于实践的课&#xff0c;奈何考试理论居多。一学期想搞懂三个框架&#xff0c;嘿嘿&#xff0c;难哦&#xff01;如果你是大一大二的同学&#xff0c;认认真真学习&#xff0c;真的有用。如果你是大三的同学&#xff0c;像就业并且走这个方向的同学&…

VMware Horizon 8 运维系列(一)windows系统重启被禁用的ipv6功能

前言 Horizon 8 在创建桌面池之前,需要对模板虚拟机进行配置,其中会用到windows优化工具,通过禁用一些不必要的服务或功能,让系统达到优化的效果。 最近客户提出一个需求:需要对虚拟机开启ipv6功能,并同时使用ipv6地址。通过查看模板系统,ipv6已经被优化工具禁用,所以需…

SSM整合快速入门案例(二)

文章目录 前言一、表现层与前端数据传输协议定义二、表现层与前端数据传输协议实现三、异常处理器四、项目异常处理五、前后台协议联调实现CRUD- - 查询- - 添加- - 修改- - 删除 总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c…

Netty核心源码剖析(五)

1.Netty核心组件EventLoop源码剖析 1>.NioEventLoop继承图 说明: ①.ScheduledExecutorService接口表示是一个定时任务接口,EventLoop可以接受定时任务; ②.EventLoop接口:Netty 接口文档说明该接口作用:一旦Channel注册了,就处理该Channel对应的所有I/O操作; ③.Single…

学成在线----day7

1、 视频处理-技术方案 1. 作业分片方案 掌握了xxl-job的分片广播调度方式&#xff0c;下边思考如何分布式去执行学成在线平台中的视频处理任务。 任务添加成功后&#xff0c;对于要处理的任务会添加到待处理任务表中&#xff0c;现在启动多个执行器实例去查询这些待处理任务…