Flutter生命周期方法小技巧

news2024/11/20 3:33:22
需求
  • A界面跳转到B界面,暂停A界面的音乐或者视频
  • B界面返回到A界面,播放A界面的音乐或者视频
  • A界面切换到后台,暂停A界面的音乐或者视频
  • A界面从后台切换到前台,播放A界面的音乐或者视频

需求通过理解修改为:

  • 监听 StatefulWidget 的 onPause 方法
  • 监听 StatefulWidget 的 onResume 方法
背景

我们在使用Flutter开发界面时,很多时候是需要在当前界面切换到后台,或者说当前界面跳转到第二个页面时,需要处理一些事宜,比如说音乐暂停,视频暂停等等。但是 StatefulWidget 中的state并没有提供如Activity的onPause,onResume方法。所以我们需要自己搞一下,搞一下这2个方法出来,只要在这2个方法中处理便可以满足上面的需求。

效果

创建2个widget界面

1、LifecyclePage 简称 A界面

2、LifecycleNextPage 简称 B界面

  • 场景:A界面打开生命周期如下

image.png

  • 场景2:A界面点击按钮跳转到B界面
    image.png

  • 场景3:B界面点击返回到A界面
    image.png

  • 场景4:A界面切换到后台
    image.png

  • 场景5:A界面后台切换到前台
    image.png

  • 场景6:A界面弹出对话框
    image.png

  • 场景7:关闭对话框
    image.png

实现

新增方法

  • onCreate
  • onResume
  • onPause
  • onDestroy

onStart和onStop 这里就不做添加了,以上4个方法便可以完成需求。

代码

老样子,直接上代码,不讲原理,就是这么拽。

  • 创建NavigatorObserver
import 'dart:async';

import 'package:built_collection/built_collection.dart';
import 'package:flutter/widgets.dart';

class NavigationHistoryObserver extends NavigatorObserver {
  final List<Route<dynamic>?> _history = <Route<dynamic>?>[];

  BuiltList<Route<dynamic>> get history =>
      BuiltList<Route<dynamic>>.from(_history);

  Route<dynamic>? get top => _history.last;

  final List<Route<dynamic>?> _poppedRoutes = <Route<dynamic>?>[];

  BuiltList<Route<dynamic>> get poppedRoutes =>
      BuiltList<Route<dynamic>>.from(_poppedRoutes);

  Route<dynamic>? get next => _poppedRoutes.last;

  final StreamController _historyChangeStreamController =
      StreamController.broadcast();

  Stream<dynamic> get historyChangeStream =>
      _historyChangeStreamController.stream;

  static final NavigationHistoryObserver _singleton =
      NavigationHistoryObserver._internal();

  NavigationHistoryObserver._internal();

  factory NavigationHistoryObserver() {
    return _singleton;
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    _poppedRoutes.add(_history.last);
    _history.removeLast();
    _historyChangeStreamController.add(HistoryChange(
      action: NavigationStackAction.pop,
      newRoute: route,
      oldRoute: previousRoute,
    ));
  }

  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    _history.add(route);
    _poppedRoutes.remove(route);
    _historyChangeStreamController.add(HistoryChange(
      action: NavigationStackAction.push,
      newRoute: route,
      oldRoute: previousRoute,
    ));
  }

  @override
  void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
    _history.remove(route);
    _historyChangeStreamController.add(HistoryChange(
      action: NavigationStackAction.remove,
      newRoute: route,
      oldRoute: previousRoute,
    ));
  }

  @override
  void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
    int oldRouteIndex = _history.indexOf(oldRoute);
    _history.replaceRange(oldRouteIndex, oldRouteIndex + 1, [newRoute]);
    _historyChangeStreamController.add(HistoryChange(
      action: NavigationStackAction.replace,
      newRoute: newRoute,
      oldRoute: oldRoute,
    ));
  }
}

class HistoryChange {
  HistoryChange({this.action, this.newRoute, this.oldRoute});

  final NavigationStackAction? action;
  final Route<dynamic>? newRoute;
  final Route<dynamic>? oldRoute;
}

enum NavigationStackAction { push, pop, remove, replace }

  • 设置navigatorObservers
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Android小样',
      ...
      navigatorObservers: [NavigationHistoryObserver()],
      ...
    );
  }
}
  • 创建PageState

abstract class PageState<T extends StatefulWidget> extends State<T>
    with PageStateMixin {
  static final List<BuildContext> _contextList = [];

  @override
  void initState() {
    super.initState();
    onCreate();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _addToContextList();
    });
  }

  @override
  void dispose() {
    onDestroy();
    _removeFromContextList();
    super.dispose();
  }

  @override
  void setState(VoidCallback fn) {
    if (mounted) {
      super.setState(fn);
    }
  }

  void _addToContextList() {
    if (!mounted) return;
    if (!_contextList.contains(context)) {
      _contextList.add(context);
    }
  }

  void _removeFromContextList() {
    if (_contextList.isEmpty) return;
    _contextList.removeWhere((element) => element == context);
  }
}

mixin PageStateMixin<T extends StatefulWidget> on State<T> {
  Route? _route;

  @override
  void didChangeDependencies() {
    _route ??= ModalRoute.of(context);
    if (_route != null) {
      RouteHistoryObserver.addResumeCallback(_route!, onResume);
      RouteHistoryObserver.addPauseCallback(_route!, onPause);
    }
    super.didChangeDependencies();
  }

  @override
  void dispose() {
    _route ??= ModalRoute.of(context);
    if (_route != null) {
      RouteHistoryObserver.removeResumeCallback(_route!, onResume);
      RouteHistoryObserver.removePauseCallback(_route!, onPause);
    }
    super.dispose();
  }

  void onCreate() {}

  void onResume() {}

  void onPause() {}

  void onDestroy() {}
}

class RouteHistoryObserver with WidgetsBindingObserver {
  static final Map<Route, Set<VoidCallback>> _resumeCallbacks = {};
  static final Map<Route, Set<VoidCallback>> _pauseCallbacks = {};
  static bool _initialized = false;
  static Route? _currTopRoute;

  static Route<dynamic>? get topRoute => _currTopRoute;

  static void init() {
    if (_initialized) return;
    _initialized = true;
    NavigationHistoryObserver()
        .historyChangeStream
        .listen(_appRouteHistoryChange);
    WidgetsBinding.instance.addObserver(RouteHistoryObserver());
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      _appResume();
    } else if (state == AppLifecycleState.paused) {
      _appPause();
    }
  }

  static void _appRouteHistoryChange(dynamic historyChange) {
    if (NavigationHistoryObserver().history.isEmpty) return;
    var topRoute = NavigationHistoryObserver().top;
    if (historyChange.action == NavigationStackAction.push) {
      var preRoute = (historyChange as HistoryChange).oldRoute;
      var pauseCallbackList = _pauseCallbacks[preRoute];
      if (pauseCallbackList != null) {
        for (var callback in pauseCallbackList) {
          callback.call();
        }
      }
    } else if (historyChange.action == NavigationStackAction.pop) {
      var pauseCallbackList = _pauseCallbacks[_currTopRoute];
      if (pauseCallbackList != null) {
        for (var callback in pauseCallbackList) {
          callback.call();
        }
      }
    }

    if (topRoute != _currTopRoute) {
      _currTopRoute = topRoute;
      var resumeCallbackList = _resumeCallbacks[topRoute];
      if (resumeCallbackList == null) return;
      for (var callback in resumeCallbackList) {
        callback.call();
      }
    }
  }

  static void addResumeCallback(Route route, VoidCallback callback) {
    var callbackList = _resumeCallbacks[route];
    if (callbackList == null) {
      callbackList = {};
      _resumeCallbacks[route] = callbackList;
    }
    if (callbackList.add(callback) && _currTopRoute == route) {
      callback.call();
    }
  }

  static void removeResumeCallback(Route route, VoidCallback callback) {
    var callbackList = _resumeCallbacks[route];
    if (callbackList == null) return;
    callbackList.remove(callback);
  }

  static void addPauseCallback(Route route, VoidCallback callback) {
    var callbackList = _pauseCallbacks[route];
    if (callbackList == null) {
      callbackList = {};
      _pauseCallbacks[route] = callbackList;
    }
    callbackList.add(callback);
  }

  static void removePauseCallback(Route route, VoidCallback callback) {
    var callbackList = _pauseCallbacks[route];
    if (callbackList == null) return;
    callbackList.remove(callback);
  }

  static void _appResume() {
    if (_currTopRoute == null) return;
    var callbackList = _resumeCallbacks[_currTopRoute];
    if (callbackList == null) return;
    for (var callback in callbackList) {
      callback.call();
    }
  }

  static void _appPause() {
    if (_currTopRoute == null) return;
    var pauseCallbackList = _pauseCallbacks[_currTopRoute];
    if (pauseCallbackList == null) return;
    for (var callback in pauseCallbackList) {
      callback.call();
    }
  }
}


// 需要在app启动时初始化
RouteHistoryObserver.init();

  • 创建2个界面Widget

// 第一个界面
class LifecyclePage extends StatefulWidget {
  const LifecyclePage({super.key});

  @override
  State<LifecyclePage> createState() => _LifecyclePageState();
}

class _LifecyclePageState extends PageState<LifecyclePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: XYAppBar(
        title: "Flutter生命周期",
        onBack: () {
          Navigator.pop(context);
        },
      ),
      body: SafeArea(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  Navigator.push(context, MaterialPageRoute(
                    builder: (BuildContext context) {
                      return const LifecycleNextPage();
                    },
                  ));
                },
                child: const Text("跳转"),
              ),
              ElevatedButton(
                onPressed: () {
                  _showDialog(context);
                },
                child: const Text("弹出框"),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _showDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Dialog Title'),
          content: const Text('This is the content of the dialog.'),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(); // Close the dialog
              },
              child: const Text('Close'),
            ),
          ],
        );
      },
    );
  }

  @override
  void onCreate() {
    super.onCreate();
    logger.d("LifecyclePage----onCreate");
  }

  @override
  void onPause() {
    super.onPause();
    logger.d("LifecyclePage----onPause");
  }

  @override
  void onResume() {
    super.onResume();
    logger.d("LifecyclePage----onResume");
  }

  @override
  void onDestroy() {
    logger.d("LifecyclePage----onDestroy");
    super.onDestroy();
  }
}


//第二个界面
import 'package:flutter/material.dart';
import 'package:flutter_xy/widgets/xy_app_bar.dart';
import 'package:flutter_xy/xydemo/lifecycle/core/page_state.dart';

import '../../utils/log_utils.dart';

class LifecycleNextPage extends StatefulWidget {
  const LifecycleNextPage({super.key});

  @override
  State<LifecycleNextPage> createState() => _LifecycleNextPageState();
}

class _LifecycleNextPageState extends PageState<LifecycleNextPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: XYAppBar(
        title: "Flutter生命周期第二页",
        onBack: () {
          Navigator.pop(context);
        },
      ),
      body: const SafeArea(
        child: Center(
          child: Text(
            "第二页",
            style: TextStyle(fontSize: 30),
          ),
        ),
      ),
    );
  }

  @override
  void onCreate() {
    super.onCreate();
    logger.d("LifecycleNextPage----onCreate");
  }

  @override
  void onPause() {
    super.onPause();
    logger.d("LifecycleNextPage----onPause");
  }

  @override
  void onResume() {
    super.onResume();
    logger.d("LifecycleNextPage----onResume");
  }

  @override
  void onDestroy() {
    logger.d("LifecycleNextPage----onDestroy");
    super.onDestroy();
  }
}


OK了,自己去测试测试吧。
详情见Github:github.com/yixiaolunhui/flutter_xy

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

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

相关文章

基于高斯模型的运动目标检测(车辆检测),Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

机器学习 --- 模型评估、选择与验证

Java实训代码、答案&#xff0c;如果能够帮到您&#xff0c;希望可以点个赞&#xff01;&#xff01;&#xff01; 如果有问题可以csdn私聊或评论&#xff01;&#xff01;&#xff01;感谢您的支持 第1关&#xff1a;为什么要有训练集与测试集 1、下面正确的是&#xff1f;&…

(每日持续更新)jdk api之StringReader基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

LeetCode 面试经典150题 121.买卖股票的最佳时机

题目&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易…

数学建模——蒙特卡洛法

目录 1.介绍2.可以做的题型3.实战3.1求pi的值3.2求定积分x^2 的值 参加了大大小小很多场比赛了&#xff0c;但是都是混子&#xff0c;但还是打算记录一下吧&#xff0c;系统认真过一遍。后续功力深厚&#xff0c;会拓展写的文章&#xff0c;目前是干货&#xff0c;一些背景啥的…

蓝桥杯刷题(十)

1.翻转 代码 输入数据&#xff0c;每组数据进行比较&#xff0c;j的范围掐头去尾&#xff0c;若a[j]b[j]&#xff0c;继续&#xff0c;若出现010,101子串则改成000,111&#xff0c;遍历完后比较a是否等于b&#xff0c;相同则输出次数&#xff0c;不同则输出-1。 for _ in ran…

CSS概念及入门

文章目录 1. CSS 概念及入门1.1. 简介1.2. 组成1.2.1. 选择器1.2.2. 属性 1.3. 区别 2. CSS 引入方式2.1. 行内样式2.1.1. 语法2.1.2. 特点 2.2. 内部样式2.2.1. 语法2.2.2. 特点 2.3. 外部样式2.3.1. 特点 2.4. 三种引入优先级 1. CSS 概念及入门 1.1. 简介 CSS 的全称为&am…

打破传统,拥抱未来:解锁企业数字化转型成功的11把金钥匙

数字化转型是一个持续的过程&#xff0c;需要企业不断地适应新技术和市场变化。企业如何提高转型成功的可能性&#xff0c;并在竞争激烈的市场中保持领先地位。今天我们来解锁企业数字化转型成功的11把金钥匙。 清晰的战略目标&#xff1a; 首先&#xff0c;企业需要明确数字化…

Python基础(八)之流程控制

Python基础&#xff08;八&#xff09;之流程控制 Python控制流程分为三种接口&#xff1a; 顺序结构选择结构循环结构 1、顺序结构 程序代码自上而下运行&#xff0c;逐条执行每一条Python代码&#xff0c;不重复执行任何代码&#xff0c;也不会跳过任何代码。 当语句与语…

基于JavaWeb+SSM+Vue“鼻护灵”微信小程序系统的设计和实现

基于JavaWebSSMVue“鼻护灵”微信小程序系统的设计和实现 滑到文末获取源码Lun文目录前言主要技术系统设计功能截图 滑到文末获取源码 Lun文目录 摘 要 3 Abstract 1 1 绪 论 1 1.1研究背景 1 工作的效率。 1 1.2 研究意义 1 1.3研究现状 1 1.4本文组织结构 2 2 技术介绍 3 2…

揭秘Facebook:数字世界的引领者

在当今数字化社会中&#xff0c;Facebook作为全球最大的社交媒体平台之一&#xff0c;扮演着引领者的角色。它不仅改变了人们的社交方式&#xff0c;还深刻影响着信息传播、商业模式和社会责任。本文将深入揭秘Facebook背后的故事&#xff0c;探索其在数字世界中的引领地位和影…

腾轩科技传媒教你如何精准制定营销策划方案,网络营销八法攻略!

整合营销是一种利用各种营销手法和渠道&#xff0c;将企业产品或服务全方位推广给目标消费者的营销方式。整合营销的目的是通过协调不同的营销手段和渠道&#xff0c;实现更高效的品牌传播和营销效果。腾轩科技传媒教你如何精准制定营销策划方案&#xff0c;网络营销八法攻略&a…

关 于 重 燃 学 习 的 热 情

3月1日是我回学校的第一天。经历了长达8个月在家的昏暗时刻&#xff0c;我这10天的感觉和在家的感觉发生了翻天覆地的变化&#xff0c;最明显的莫过于学习状态的改变。 倒不是说在家学的不好&#xff0c;而是说在学校&#xff0c;我对学习的整体感觉&#xff0c;以及专注程度&…

PyTorch深度学习实战(39)——小样本学习

PyTorch深度学习实战&#xff08;39&#xff09;——小样本学习 0. 前言1. 小样本学习简介2. 孪生网络2.1 模型分析2.2 数据集分析2.3 构建孪生网络 3. 原型网络3. 关系网络小结系列链接 0. 前言 小样本学习 (Few-shot Learning) 旨在解决在训练集中只有很少样本的情况下进行分…

边缘计算+WEB端应用融合:AI行为识别智能监控系统搭建指南 -- 云端系统数据库设计(五)

专栏目录 边缘计算WEB端应用融合&#xff1a;AI行为识别智能监控系统搭建指南 – 整体介绍&#xff08;一&#xff09; 边缘计算WEB端应用融合&#xff1a;AI行为识别智能监控系统搭建指南 – 边缘设备图像识别及部署&#xff08;二&#xff09; 边缘计算WEB端应用融合&#xf…

不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问

今天新建共享发现了这个问题&#xff0c;用了网上的组策略之后还是不能访问共享 最后发现原因是共享设置中选择了无密码登录 选择有密码保护的共享&#xff0c;客户端输入ip后&#xff0c;用户名输入Administrator&#xff0c;密码为空&#xff08;我的文件服务器是联想的win1…

LabVIEW飞机液压基础试验台测试系统

LabVIEW飞机液压基础试验台测试系统 为解决飞机液压基础实验台人工控制操作复杂、测试时间长、测试流程易出错等问题&#xff0c;开发了一套基于LabVIEW的飞机液压基础试验台测试系统。该系统通过计算机控制&#xff0c;实现了高度自动化的测试流程&#xff0c;有效提高了测试…

【tls招新web部分题解】

emowebshell (php7.4.21版本漏洞) 非预期 题目提示webshell&#xff0c;就直接尝试一下常见的后门命名的规则 如 shell.php这里运气比较好&#xff0c;可以直接shell.php就出来 要是不想这样尝试的话&#xff0c;也可以直接dirsearch进行目录爆破 然后在phpinfo中直接搜素c…

【Spring 篇】SpringMVC拦截器:给你的应用增添色彩

嗨&#xff0c;亲爱的小伙伴们&#xff01;欢迎来到这段关于SpringMVC拦截器的奇妙之旅。今天我们要一探究竟&#xff0c;深入挖掘拦截器的神秘面纱&#xff0c;看看它是如何在你的应用中悄然发挥作用的。别怕&#xff0c;我会用最通俗易懂的语言&#xff0c;一步一步带你走进这…

Python-GEE绘制DEM精美图片

目录 上传矢量和DEM获取添加颜色条参考文章 先连接上GEE的自己的项目 import ee import geemap geemap.set_proxy(port33210) ee.Authenticate() ee.Initialize(projecta-flyllf0313)上传矢量和DEM获取 使用Google Earth Engine&#xff08;GEE&#xff09;和Google Earth Eng…