flutter页面实现onPause,onResume

503 阅读2分钟

开发安卓的朋友都知道在activity的切换时是有 onPauseonResume 监听的,而flutter没有提供类似的功能,下面我们在flutter中实现一个类似android中的 onPauseonResume

lifecycle_demo – LifePage.dart 2023-10-20 10-36-06.gif

1.既然我们是在state中监听页面的状态,那么我们首先在state中混入我们的生命周期方法,其中 StateLifecycleManager 是我们自定义的,用来管理和存储 state 的单例类。我们在state中initState(),dispose()添加或移除监听。

mixin LifecycleMixin<T extends StatefulWidget> on State<T> {
  @mustCallSuper
  @override
  void initState() {
    super.initState();
    StateLifecycleManager.instance.addLifecycle(this);
  }

  @mustCallSuper
  @override
  void dispose() {
    StateLifecycleManager.instance.removeLifecycle(this);
    super.dispose();
  }

  ///页面回到正在展示状态
  @protected
  void onResume();

  ///页面处于非正在展示中
  @protected
  void onPause();
}

2.实现 StateLifecycleManager 类,内部存储了一个Map,Map中的键:state持有的widget的类名,值:第一步实现的LifecycleMixin。这样我们就可以在页面监听器中通过widget小部件的名称进行对应的操作。

class StateLifecycleManager {
  factory StateLifecycleManager() {
    return _getInstance();
  }

  static final StateLifecycleManager _instance = StateLifecycleManager._();

  static StateLifecycleManager get instance => _instance;

  static StateLifecycleManager _getInstance() {
    return _instance;
  }
  final Map<String,LifecycleMixin> _map = {};

  StateLifecycleManager._();

  ///添加
  addLifecycle(LifecycleMixin lifecycleMixin) {
    if (!_map.containsValue(lifecycleMixin)) {
      _map[lifecycleMixin.widget.runtimeType.toString()] = lifecycleMixin;
    }
  }

  ///移除
  removeLifecycle(LifecycleMixin lifecycleMixin) {
    if (_map.containsValue(lifecycleMixin)) {
      _map.remove(lifecycleMixin.widget.runtimeType.toString());
    }
  }

  onResume(String routerName) {
    if(_map.containsKey(routerName)){
      _map[routerName]?.onResume();
    }
  }
  onPause(String routerName) {
    if(_map.containsKey(routerName)){
      _map[routerName]?.onPause();
    }
  }
}

3.实现 StateNavigatorObserver 类,重写didPop方法,此方法是在某个页面关闭后调用(页面关闭正是我们可以实现onResume的时机)。同理,重写didPush方法,此方法是在某个页面打开后调用(实现onPause的时机)

class StateNavigatorObserver extends NavigatorObserver {
  StateNavigatorObserver();

  @override
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    if((route is CupertinoPageRoute || route is MaterialPageRoute)){
      String? routerName = previousRoute?.settings.name;
      if(routerName != null){
        StateLifecycleManager.instance.onResume(routerName);
      }
    }
  }

  @override
  void didPush(Route route, Route? previousRoute) {
    super.didPush(route, previousRoute);
    if((route is CupertinoPageRoute || route is MaterialPageRoute)){
      String? routerName = previousRoute?.settings.name;
      if(routerName != null){
        StateLifecycleManager.instance.onPause(routerName);
      }
    }
  }
}
  1. 添加 navigatorObservers
MaterialApp(
  ...
  navigatorObservers: [
      ///自定义的 StateNavigatorObserver 类
    StateNavigatorObserver(),
  ],
  ...
)
  1. 使用,在需要监听的页面上实现 LifecycleMixin 就可以了。 前面提到StateLifecycleManager中Map存储Key的是(widget的类名),那这名称是怎么设置的呢? 这里要注意 Navi.push(context, const NextPage()) 打开新页面的方法。
class Navi {

  /// ios样式
  static push(BuildContext context, Widget page,
      {String? routeString}) {
    routeString ??= page.runtimeType.toString();
    Navigator.push(
      context,
      CupertinoPageRoute(
        builder: (context) => page,
        /// 设置RouteSettings后在 StateNavigatorObserver 就可以获取到对应的page名称
        settings: RouteSettings(name: routeString),
      ),
    );
  }

}

class LifePage extends StatefulWidget {
  const LifePage({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return LifePageState();
  }
}

class LifePageState extends State<LifePage> with LifecycleMixin {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: const AppbarTitle(title: "生命周期测试",),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const SizedBox(
            height: 5,
            width: double.infinity,
          ),
          TextButton(
              onPressed: () {
                Navi.push(context, const NextPage());
              },
              style: TextButton.styleFrom(
                backgroundColor: Colors.blue,
                minimumSize: const Size(0, 40)
              ),
              child: const Text(
                '打开下一个页面',
                style: TextStyle(color: Colors.white),
              )),
        ],
      ),
    );
  }

  @override
  void onPause() {
    Printer.error('onPause --> ${runtimeType.toString()}', stackTrace: StackTrace.current);
  }

  @override
  void onResume() {
    Printer.error('onResume --> ${runtimeType.toString()}', stackTrace: StackTrace.current);
  }
}

好了,经过前面的一顿操作之后App就可以监听onPause和onResume了!!!

不足之处,欢迎大家指正。