flutter

Flutter生命周期方法小技巧

2023-08-08  本文已影响0人  一笑轮回吧

需求

需求通过理解修改为:

背景

我们在使用Flutter开发界面时,很多时候是需要在当前界面切换到后台,或者说当前界面跳转到第二个页面时,需要处理一些事宜,比如说音乐暂停,视频暂停等等。但是 StatefulWidget 中的state并没有提供如Activity的onPause,onResume方法。所以我们需要自己搞一下,搞一下这2个方法出来,只要在这2个方法中处理便可以满足上面的需求。

效果

创建2个widget界面

1、LifecyclePage 简称 A界面

2、LifecycleNextPage 简称 B界面

image.png

实现

新增方法

onStart和onStop 这里就不做添加了,以上4个方法便可以完成需求。

代码

老样子,直接上代码,不讲原理,就是这么拽。

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 }

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()],
      ...
    );
  }
}

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();


// 第一个界面
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

上一篇下一篇

猜你喜欢

热点阅读