由于Flutter没有页面的概念,而平时开发很多地方需要有页面维度,如以下场景:
- 页面埋点
- 感知页面生命周期,回到页面、页面退出前台、退出等
- 页面通用方法等
直接上逻辑,三句话搞定~ ~嘿嘿,
1.借用Flutter自带的widget push 层级的感知监听 NavigatorObserver.为了方便自定义我们自己实现NavigatorObserver
/// app_route_observer.dart
///
/// Created by iotjin on 2020/11/07.
/// description: 通过路由监听页面出现或消失
import 'package:flutter/material.dart';
class AppRouteObserver {
/// 创建路由监听(这是实际上的路由监听器)
static final CustomRouteObserver<ModalRoute<void>> _routeObserver = CustomRouteObserver<ModalRoute<void>>();
/// 这是个单例
static final AppRouteObserver _appRouteObserver = AppRouteObserver._internal();
AppRouteObserver._internal();
/// 通过单例的get方法获取路由监听器
CustomRouteObserver<ModalRoute<void>> get routeObserver {
return _routeObserver;
}
factory AppRouteObserver() {
return _appRouteObserver;
}
}
class CustomRouteObserver<R extends Route<dynamic>> extends NavigatorObserver {
final Map<R, Set<CustomRouteAware>> _listeners = <R, Set<CustomRouteAware>>{};
@visibleForTesting
bool debugObservingRoute(R route) {
late bool contained;
assert(() {
contained = _listeners.containsKey(route);
return true;
}());
return contained;
}
void subscribe(CustomRouteAware routeAware, R route) {
assert(routeAware != null);
assert(route != null);
final Set<CustomRouteAware> subscribers = _listeners.putIfAbsent(route, () => <CustomRouteAware>{});
if (subscribers.add(routeAware)) {
routeAware.didPush();
}
}
void unsubscribe(CustomRouteAware routeAware) {
assert(routeAware != null);
final List<R> routes = _listeners.keys.toList();
for (final R route in routes) {
final Set<CustomRouteAware>? subscribers = _listeners[route];
if (subscribers != null) {
subscribers.remove(routeAware);
if (subscribers.isEmpty) {
_listeners.remove(route);
}
}
}
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
if (route is R && previousRoute is R) {
final List<CustomRouteAware>? previousSubscribers = _listeners[previousRoute]?.toList();
if (previousSubscribers != null) {
for (final CustomRouteAware routeAware in previousSubscribers) {
routeAware.didPopPrevious(route,previousRoute);
}
}
final List<CustomRouteAware>? subscribers = _listeners[route]?.toList();
if (subscribers != null) {
for (final CustomRouteAware routeAware in subscribers) {
routeAware.didPop(route,previousRoute);
}
}
}
}
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
if (route is R && previousRoute is R) {
final Set<CustomRouteAware>? previousSubscribers = _listeners[previousRoute];
if (previousSubscribers != null) {
for (final CustomRouteAware routeAware in previousSubscribers) {
routeAware.didPushNext(route,previousRoute);
}
}
}
}
}
//自定义生命周期方法
abstract class CustomRouteAware {
void didPopPrevious(Route<dynamic> route, Route<dynamic>? previousRoute) { }
void didPush() { }
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) { }
void didPushNext(Route<dynamic> route, Route<dynamic>? previousRoute) { }
}
- 添加到 MaterialApp 中
MaterialApp(
...
navigatorObservers: [
AppRouteObserver().routeObserver
],
);
- 然后编辑CommPage,实现CustomRouteObserver
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../support/yu_library.dart';
import '../../utils/app_route_observer.dart';
import '../../utils/storageHelper.dart';
abstract class CommonPage extends StatefulWidget {
static BuildContext? commTopContext;
const CommonPage({super.key});
@override
CommonPageState createState() => getState();
///子类实现
CommonPageState getState();
}
abstract class CommonPageState<T extends CommonPage> extends State<T> with CustomRouteAware {
BuildContext? commContext; //当前页面上下文 为空时表明页面已经销毁
late String commPageName; //页面路径
dynamic commPageParams; //页面参数
Map? commPageSettings; // 页面配置信息
List<StreamSubscription> commEventSubscriptions = []; //页面事件订阅监听,销毁页面时自动取消订阅
bool commPageClosed = false; // 页面是否已关闭
bool commNeedSetState = false; // 返回页面时是否需要刷新,一般用于刷新时被mounted阻塞
//页面打开
void onStart(){}
//从上一个页面退回
void onPopPrevious(Route<dynamic> route, Route<dynamic>? currentRoute){}
// 打开一个新页面
void onPushNext(Route<dynamic> route, Route<dynamic>? currentRoute){}
//页面关闭
void onClose(Route<dynamic> currentRoute, Route<dynamic>? previousRoute){}
//页面销毁
void onDispose(){}
//页面初始化
@override
void initState() {
super.initState();
commContext=context;
commPageClosed=false;
commNeedSetState = false;
CommonPage.commTopContext=commContext;
}
//页面显示
@override
void didPush() {
commPageName = ModalRoute.of(commContext!)?.settings.name ??'';
commPageParams = ModalRoute.of(commContext!)?.settings.arguments ?? {};
commPageSettings = AppSettingHelper.getSettingsByPageRoute(commPageName);
CommonPage.commTopContext=commContext;
onStart();
if (kDebugMode) print('=======**=======页面打开-onStart commPageName:$commPageName commPageClosed:$commPageClosed commNeedSetState:$commNeedSetState commEventSubscriptions:${commEventSubscriptions.length} $commPageParams $commPageSettings =======**=======');
}
//页面依赖关系变化
@override
void didChangeDependencies() {
super.didChangeDependencies();
/// 路由订阅
AppRouteObserver().routeObserver.subscribe(this, ModalRoute.of(commContext!)!);
}
//返回当前页面
@override
void didPopPrevious(Route<dynamic> route, Route<dynamic>? previousRoute) {
//目标widget不是页面不做处理
if(route.settings.name?.isNotEmpty == true){
CommonPage.commTopContext=commContext;
onPopPrevious(route,previousRoute);
print('=======**=======返回当前页面-onPopNext commNeedSetState:$commNeedSetState commPageName:$commPageName currentPage:${previousRoute?.settings.name} ${route.settings.name} =======**=======');
if(commNeedSetState){
commNeedSetState=false;
commSetState(() { });
}
}
}
//打开其他页面
@override
void didPushNext(Route<dynamic> route, Route<dynamic>? previousRoute) {
//目标widget不是页面不做处理
if(route.settings.name?.isNotEmpty == true) {
onPushNext(route,previousRoute);
print('=======**=======打开其他页面-onPushNext commPageName:$commPageName currentPage:${previousRoute?.settings.name} ${route.settings.name} =======**=======');
}
}
//页面退出
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
//目标widget不是页面不做处理
if(route.settings.name?.isNotEmpty == true) {
commPageClosed = true;
onClose(route,previousRoute);
print('=======**=======页面退出-onDetach commPageName:$commPageName currentPage: ${route.settings.name} ${previousRoute?.settings.name} =======**=======');
}
}
//页面销毁
@override
void dispose() {
/// 取消路由订阅
for (var element in commEventSubscriptions) {
element.cancel();
}
commContext=null;
super.dispose();
onDispose();
print('=======**=======页面销毁-onDispose commPageName:$commPageName =======**=======');
AppRouteObserver().routeObserver.unsubscribe(this);
}
//当前页面是否位于前台
bool commCurrentIsTop(){
return CommonPage.commTopContext == commContext;
}
//page 上所有的setState可以走这里,防止异步调用报错
void commSetState(VoidCallback fn){
print("commSetState mounted:$mounted commPageClosed:$commPageClosed ${DateTime.now().microsecondsSinceEpoch}");
if(mounted) {
setState(fn);
}else{
commNeedSetState=true;
}
}
}
可以看出我们区分页面的判断 route.settings.name ,所以我们页面push的时候务必要设置这个值,可以封装一个通用的打开页面的方法,例如
//打开新页面
static Future<T?> push<T>(BuildContext context, String routeName, {Map<dynamic, dynamic>? arguments, bool isReplaced = false, bool pushAndRemoveUntil = false}) async {
final Function? buildPage = routes[routeName];
LogHelper.d("push: $routeName $arguments ");
if (buildPage == null) {
return await Navigator.push(context, CupertinoPageRoute(builder: (_) => const NotFoundPage()));
}
late Widget page ;
try{
page = (arguments == null)? buildPage(context) : buildPage(context, arguments: arguments) ;
} catch(e){
page= buildPage(context);
}
if (page is! CommonPage) {
LogHelper.e("!!!!! $routeName页面需继承CommPage,请及时处理 !!!!");
if(AppConfig.isSitEnv) ToastHelper.showToast("$routeName 没有继承CommPage,请及时处理!");
}
if (isReplaced) {
//替换页面
return await Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (context) => page,
settings: RouteSettings(name: routeName,arguments:arguments)));
} else if (pushAndRemoveUntil) {
//跳转并关闭所有页面
return await Navigator.pushAndRemoveUntil(
context,
CupertinoPageRoute(
builder: (context) => page,
settings: RouteSettings(name: routeName,arguments:arguments)),
(route) => false);
} else {
return await Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => page,
settings: RouteSettings(name: routeName,arguments:arguments)));
}
}
到这里大功告成,只需要在页面的widget种继承我们的Commpage即可。
class LoginPage extends CommonPage {
const LoginPage({super.key});
@override
CommonPageState<CommonPage> getState() => _LoginPageState();
}
class _LoginPageState extends CommonPageState<LoginPage> {
@override
void onPopPrevious(Route route, Route? currentRoute) {
// TODO: implement onPopPrevious
super.onPopPrevious(route, currentRoute);
}
@override
void onPushNext(Route route, Route? currentRoute) {
// TODO: implement onPushNext
super.onPushNext(route, currentRoute);
}
}
网友评论