需求
- 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