一、Flutter生命周期问题
在日常的 Flutter 项目开发的过程中,经常会遇到一些奇怪的问题,比如在一个页面中对 SystemUI进行修改之后,返回上一页结果发现修改的结果会保存下来,不会恢复,具体来说就是如果修改了状态栏为全屏状态,结果返回上一个页面之后还是全屏状态,那么就有问题,需要考虑何时才能修改的问题,即如何拥有类似 Android 中的 onCreate、onResume、onPause 等生命周期功能,还有一些比如日志打点的问题。
二、自定义生命周期实现原理
经过查询网络资料和其他博客内容,知道有两个接口比较重要,分别WidgetsBindingObserver和RouteAware,WidgetsBindingObserver和App导航有关,用于监听App是否进入 resumed、active、inactive、paused、detach等状态,看起来是和App生命周期有关的,而 RouteAware 是用于监听路由变化的。
/// Interface for classes that register with the Widgets layer binding.
///
/// When used as a mixin, provides no-op method implementations.
///
/// See [WidgetsBinding.addObserver] and [WidgetsBinding.removeObserver].
///
/// This class can be extended directly, to get default behaviors for all of the
/// handlers, or can used with the `implements` keyword, in which case all the
/// handlers must be implemented (and the analyzer will list those that have
/// been omitted).
///
/// {@tool snippet}
///
/// This [StatefulWidget] implements the parts of the [State] and
/// [WidgetsBindingObserver] protocols necessary to react to application
/// lifecycle messages. See [didChangeAppLifecycleState].
///
abstract class WidgetsBindingObserver {}
从上面的注释可以看出 State 可以通过实现 WidgetsBindingObserver 的协议来响应应用程秀的生命周期消息,具体看 didChangeAppLifecycleState 。
/// Called when the system puts the app in the background or returns
/// the app to the foreground.
///
/// An example of implementing this method is provided in the class-level
/// documentation for the [WidgetsBindingObserver] class.
///
/// This method exposes notifications from [SystemChannels.lifecycle].
void didChangeAppLifecycleState(AppLifecycleState state) { }
如上注释所说,该方法是在应用程序在进入后台或者返回前台的时候调用。
/// States that an application can be in.
///
/// The values below describe notifications from the operating system.
/// Applications should not expect to always receive all possible
/// notifications. For example, if the users pulls out the battery from the
/// device, no notification will be sent before the application is suddenly
/// terminated, along with the rest of the operating system.
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism to observe the lifecycle state
/// from the widgets layer.
enum AppLifecycleState {
/// The application is visible and responding to user input.
resumed,
/// The application is in an inactive state and is not receiving user input.
///
/// On iOS, this state corresponds to an app or the Flutter host view running
/// in the foreground inactive state. Apps transition to this state when in
/// a phone call, responding to a TouchID request, when entering the app
/// switcher or the control center, or when the UIViewController hosting the
/// Flutter app is transitioning.
///
/// On Android, this corresponds to an app or the Flutter host view running
/// in the foreground inactive state. Apps transition to this state when
/// another activity is focused, such as a split-screen app, a phone call,
/// a picture-in-picture app, a system dialog, or another window.
///
/// Apps in this state should assume that they may be [paused] at any time.
inactive,
/// The application is not currently visible to the user, not responding to
/// user input, and running in the background.
///
/// When the application is in this state, the engine will not call the
/// [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame]
/// callbacks.
paused,
/// The application is still hosted on a flutter engine but is detached from
/// any host views.
///
/// When the application is in this state, the engine is running without
/// a view. It can either be in the progress of attaching a view when engine
/// was first initializes, or after the view being destroyed due to a Navigator
/// pop.
detached,
}
上面的枚举对应着不同的应用程序的状态,具体的解释看注释内容。
接着看下 RouteAware
/// An interface for objects that are aware of their current [Route].
///
/// This is used with [RouteObserver] to make a widget aware of changes to the
/// [Navigator]'s session history.
abstract class RouteAware {
/// Called when the top route has been popped off, and the current route
/// shows up.
void didPopNext() { }
/// Called when the current route has been pushed.
void didPush() { }
/// Called when the current route has been popped off.
void didPop() { }
/// Called when a new route has been pushed, and the current route is no
/// longer visible.
void didPushNext() { }
}
如上的大概意思就是说如果想要知道路由发生了改变,那么就需要实现该接口。比如想知道路由当前是处于什么情况,是从其他页面跳转过来的呢,还是已经跳转到其他页面,或者当前页面被pop或者当前页面已经由其他页面pop过来了。
三、代码实现
import 'package:flutter/material.dart';
//生命周期
RouteObserver routeObserver = RouteObserver();
/ An interface for objects that are aware of their current [Route].
// ///
// /// This is used with [RouteObserver] to make a widget aware of changes to the
// /// [Navigator]'s session history.
// abstract class RouteAware {}
//RouteAware 是记录导航变化的一个接口
//WidgetsBindingObserver 感知App生命周期变化的接口
abstract class LifeCycleState<T extends StatefulWidget> extends State<T>
with WidgetsBindingObserver, RouteAware {
//Widget 的名称??
String tag = T.toString();
T get widget => super.widget;
//是否在栈顶
bool _isTop = false;
@override
void initState() {
super.initState();
onCreate();
onResume();
WidgetsBinding.instance!.addObserver(this);
}
@override
void deactivate() {
super.deactivate();
}
//这个didChangeAppLifecycleState 是混入WidgetsBindingObserver获得的方法
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch(state){
case AppLifecycleState.resumed:
if (_isTop){
onForeground();
onResume();
}
break;
case AppLifecycleState.inactive:
break;
case AppLifecycleState.paused:
if (_isTop){
onBackground();
}
break;
case AppLifecycleState.detached:
break;
default:
break;
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//这里和WidgetsBindingObserver.instance.addObserver(this) 不是一个对象
routeObserver.subscribe(this, ModalRoute.of(context)!);
}
@override
void dispose() {
onDestroy();
WidgetsBinding.instance!.removeObserver(this);
routeObserver.unsubscribe(this);
super.dispose();
}
@override
void didPush() {
super.didPush(); //从其他页面跳转到当前页面
log("didPush");
_isTop = true;
}
@override
void didPushNext() { //从当前页面跳转到下一页之后才会调用?
super.didPushNext();
log("didPushNext");
onPause(); //在本页面执行onPause()
_isTop = false;
}
@override
void didPop() { //从当前页面退回当上一页面
super.didPop(); //pop
onPause();//在当前页执行onPause()
}
@override
void didPopNext() {
super.didPopNext();//从其他页面Pop之后,进入到当前页面
onResume();//在当前页面执行onResume()
_isTop = true;
}
void onForeground() {
}
void onBackground() {
}
void onDestroy() {
}
void onPause() {
}
void onCreate() {
}
void onResume() {
}
log(String message){
debugPrint('$tag-->$message');
}
}
RouteObserver 是一个配合RouteAware的一个类,通过这个类可以通知到当前页面应该执行那种生命周期方法,否则只混入RouteAware是不能执行的。另外还有RouteObserver需要注册在MaterialApp中,这样才能在导航的过程中执行到对应的生命周期方法。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FijkPlayerDemo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'FijkPlayerDemoPage'),
navigatorObservers: [
routeObserver
],
);
}
}
下面看下 RouterObserver 的源码
class RouteObserver<R extends Route<dynamic>> extends NavigatorObserver {
final Map<R, Set<RouteAware>> _listeners = <R, Set<RouteAware>>{};
/// Subscribe [routeAware] to be informed about changes to [route].
///
/// Going forward, [routeAware] will be informed about qualifying changes
/// to [route], e.g. when [route] is covered by another route or when [route]
/// is popped off the [Navigator] stack.
void subscribe(RouteAware routeAware, R route) {
assert(routeAware != null);
assert(route != null);
final Set<RouteAware> subscribers = _listeners.putIfAbsent(route, () => <RouteAware>{});
if (subscribers.add(routeAware)) {
routeAware.didPush();
}
}
/// Unsubscribe [routeAware].
///
/// [routeAware] is no longer informed about changes to its route. If the given argument was
/// subscribed to multiple types, this will unregister it (once) from each type.
void unsubscribe(RouteAware routeAware) {
assert(routeAware != null);
for (final R route in _listeners.keys) {
final Set<RouteAware>? subscribers = _listeners[route];
subscribers?.remove(routeAware);
}
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
if (route is R && previousRoute is R) {
final List<RouteAware>? previousSubscribers = _listeners[previousRoute]?.toList();
if (previousSubscribers != null) {
for (final RouteAware routeAware in previousSubscribers) {
routeAware.didPopNext();
}
}
final List<RouteAware>? subscribers = _listeners[route]?.toList();
if (subscribers != null) {
for (final RouteAware routeAware in subscribers) {
routeAware.didPop();
}
}
}
}
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
if (route is R && previousRoute is R) {
final Set<RouteAware>? previousSubscribers = _listeners[previousRoute];
if (previousSubscribers != null) {
for (final RouteAware routeAware in previousSubscribers) {
routeAware.didPushNext();
}
}
}
}
}
四、LifecycleState的使用
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends LifeCycleState<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MaterialButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return SimpleVideoPage();
}));
},
child: Text('SimpleVideoPlayer'),),
MaterialButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return ComplexVideoPage();
}));
},
child: Text('ComplexVideoPlayer'),),
MaterialButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return ListVideoPage();
}));
},
child: Text('VideoPlayer'),),
],
),
),
);
}
@override
void onCreate() {
super.onCreate();
log("主页 onCreate");
}
@override
void onResume() {
super.onResume();
log("主页 onResume");
}
@override
void onPause() {
super.onPause();
log("主页 onPause");
}
@override
void onDestroy() {
super.onDestroy();
log("主页 onDestroy");
}
}
五、PageView的生命周期方法
前面介绍的生命周期方法实现只能在普通 Route之间使用,如果想要在 PageView 之间实现该功能,需要实现 LifeCycleInnerState,该生命周期需要借助EventBus来实现切换的监听和消息发送。
import 'dart:developer';
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_ijkplayer/lifecycle/route_name_util.dart';
//用于PageView
abstract class LifeCycleInnerState<T extends StatefulWidget>
extends State<T> with AutomaticKeepAliveClientMixin{
bool _isResume = false;
bool _isPause = false;
int _position =0 ;
late String _tag;
late String _className;
String get className => _className;
String setTag();
@override
void initState() {
super.initState();
_className = RouteNameUtil.getRouteNameWithId(context);//
_position = setPosition();
onCreate();
onResume();
bus.on<LifecycleInnerEvent>().listen((event) { //页面切换过来的时候会调用
if (event.tag == setTag()) { //是同一个页面
if (_position == event.currentPage){
if(!_isResume){
onResume();
}
}
}else {
//不是同一个pageView页面
if(!_isPause){
onPause();
}
}
});
}
@override
void dispose() {
onDestroy();
super.dispose();
}
int setPosition();
//初始化一些变量
void onCreate() {}
//onResume 只要页面切换到栈顶,都会调用此方法
void onResume() {
_isResume = true;
_isPause = false;
}
//页面被覆盖,暂停
void onPause() {
_isResume = false;
_isPause = true;
}
void onDestroy() {}
@override
bool get wantKeepAlive => keepAlive();
//子页面可以自己决定是否销毁
bool keepAlive(){
return true;
}
}
class LifecycleInnerEvent extends Notification {
final int currentPage;
final String tag;
LifecycleInnerEvent(this.currentPage, this.tag);
}
EventBus bus = EventBus();
onPageChanged(index, tag){
bus.fire(LifecycleInnerEvent(index, tag));
}
import 'package:flutter/cupertino.dart';
class RouteNameUtil {
static String getRouteNameWithId(BuildContext context) {
String name = context.widget.toString();
String contextString = context.toString();
late String id;
try {
id = contextString.substring(contextString.indexOf("#"), contextString.indexOf("(lifecycle"));
} catch (e) {
id = "";
}
if (id == "") {
return name;
}
return name + id;
}
static String getRouteName(BuildContext context){
String name = context.widget.toString();
return name;
}
}
因为用到了 EventBus 插件,所以需要添加依赖:
dependencies:
event_bus: ^2.0.0
LifeCycleInnerState 的使用
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_ijkplayer/lifecycle/lifecycle_inner_state.dart';
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> with TickerProviderStateMixin{
late PageController _pageController;
final _defaultColor = Colors.grey;
final _activeColor = Colors.blue;
int _currentIndex = 0;
var titles = ['首页', '搜索','旅拍','我的'];
@override
void initState() {
super.initState();
_pageController = PageController(initialPage: 0);
_pageController.addListener(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PageView+TabBar'),
),
body: PageView(
controller: _pageController,
physics: NeverScrollableScrollPhysics(),
children: [
KeepAlivePage(title: '首页', position: 0,),
KeepAlivePage(title: '搜索', position: 1,),
KeepAlivePage(title: '旅拍', position: 2,),
KeepAlivePage(title: '我的', position: 3,),
],
),
bottomNavigationBar: _getBottomTabBar(),
);
}
_getBottomTabBar() {
return BottomNavigationBar(
items: [
bottomNavigationBarItem(Icons.home, '首页'),
bottomNavigationBarItem(Icons.search, '搜索'),
bottomNavigationBarItem(Icons.camera_alt, '旅拍'),
bottomNavigationBarItem(Icons.account_circle, '我的'),
],
currentIndex: _currentIndex,
selectedItemColor: _activeColor,
onTap: (index) {
_pageController.jumpToPage(index);
onPageChanged(index, titles[index]);
setState(() {
_currentIndex = index;
});
},
type: BottomNavigationBarType.fixed,
);
}
BottomNavigationBarItem bottomNavigationBarItem(IconData icon, String title) {
return BottomNavigationBarItem(
icon: Icon(
icon,
color: _defaultColor,
),
activeIcon: Icon(
icon,
color: _activeColor,
),
label: title);
}
}
class KeepAlivePage extends StatefulWidget {
final String title;
final int position;
const KeepAlivePage({Key? key, required this.title, required this.position}) : super(key: key);
@override
_KeepAlivePageState createState() => _KeepAlivePageState();
}
class _KeepAlivePageState extends LifeCycleInnerState<KeepAlivePage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Container(
child: Center(
child: Text('${widget.title}'),
),
);
}
@override
int setPosition() {
return widget.position;
}
@override
String setTag() {
return widget.title;
}
@override
void onCreate() {
super.onCreate();
log("onCreate ${widget.title}");
}
@override
void onResume() {
super.onResume();
log("onResume ${widget.title}");
}
@override
void onPause() {
super.onPause();
log("onPause ${widget.title}");
}
@override
void onDestroy() {
super.onDestroy();
log("onDestroy ${widget.title}");
}
}
六、总结
Flutter中实现类似Android生命周期方法可以借助 WigetsBindingObserver 和 RouteAware 来实现。 WidgetsBindingObserver 用来观察App的生命周期的变化,而 RouteAware 是用来观察 Route 的进入和退出 push 或者 pop。