需求
- 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界面打开生命周期如下
- 场景2:A界面点击按钮跳转到B界面
- 场景3:B界面点击返回到A界面
- 场景4:A界面切换到后台
- 场景5:A界面后台切换到前台
- 场景6:A界面弹出对话框
- 场景7:关闭对话框
实现
新增方法
- 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