Flutter入门系列-Flutter实现自定义生命周期

一、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。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值