下面有一段演示代码:
class PageA extends StatelessWidget {
test() {
demo1();
// demo2();
}
showDialog1(String text, {Function()? onTap}) async {
await Get.dialog(
TestDialog(
text: text,
onTapLog: () {
clickLog("Dialog1--打印");
},
onTap: onTap,
),
routeSettings: RouteSettings(
name: "dialog1",
),
);
}
showDialog2() async {
await Get.dialog(
TestDialog(
text: "Dialog2",
onTapLog: () {
clickLog("Dialog2--打印");
},
onTap: () {},
),
routeSettings: RouteSettings(
name: "dialog2",
),
);
}
//弹框上在显示弹框
demo1() {
showDialog1("显示弹框2", onTap: () {
showDialog2();
});
}
//弹框之后跳转路由
demo2() {
showDialog1("跳转到PageB", onTap: () {
Get.to(PageB());
});
}
clickLog(String title) {
Log.d("$title---Get.history---${Get.history}");
Log.d("$title---Get.currentRoute---${Get.currentRoute}");
Log.d("$title---Get.previousRoute---${Get.previousRoute}");
// Log.d("$title---Get.currentName---${Get.currentName}");
// Log.d("$title---Get.previousName---${Get.previousName}");
// Log.d("$title---Get.currentPage---${Get.currentPage}");
// Log.d("$title---Get.previousPage---${Get.previousPage}");
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.grey,
floatingActionButton: FloatingActionButton(
onPressed: test,
),
body: Column(
children: [
H(100.w),
Tap(
onTap: () {
clickLog('PageA--打印');
},
child: Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text("打印"),
),
),
),
],
),
);
}
}
class TestDialog extends StatelessWidget {
final String text;
final void Function()? onTap;
final void Function()? onTapLog;
TestDialog({
super.key,
required this.text,
this.onTap,
this.onTapLog,
});
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 300,
height: 300,
color: Colors.pink,
child: Column(
children: [
Tap(
onTap: onTapLog,
child: Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text("打印"),
),
),
),
Spacer(),
Tap(
onTap: onTap,
child: Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text(text),
),
),
),
],
),
),
);
}
}
class PageB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page B'),
),
body: Column(
children: [
Tap(
onTap: () {
Log.d("PageB---Get.history---${Get.history}");
Log.d("PageB---Get.currentRoute---${Get.currentRoute}");
Log.d("PagB---Get.previousRoute---${Get.previousRoute}");
},
child: Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text("打印"),
),
),
),
Spacer(),
Tap(
onTap: () {
Get.to(PageC());
},
child: Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text("jump Page C"),
),
),
),
],
),
);
}
}
class PageC extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PageC'),
),
body: Column(
children: [
Tap(
onTap: () {
Log.d("PageC---Get.history---${Get.history}");
Log.d("PageC---Get.currentRoute---${Get.currentRoute}");
Log.d("PageC---Get.previousRoute---${Get.previousRoute}");
},
child: Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text("打印"),
),
),
),
Spacer(),
Container(
width: 300,
height: 40,
color: Colors.lightBlue,
child: Center(
child: Text("PageC"),
),
),
],
),
);
}
}
tip:打印里的前一个是"当前路由",后一个是”上一个路由“
场景1: 显示弹框之后直接返回
页面A打印:/PageA /Main
弹框1打印:/PageA /PageA
返回页面A打印:/PageA /PageA
场景2: 依次显示两个弹框再依次返回
页面A打印:/PageA /Main
弹框1打印:/PageA /PageA
弹框2打印:/PageA Dialog1
返回弹框1打印:Dialog1 Dialog1
返回页面A打印:/PageA /PageA
场景3: 显示弹框之后在跳转一个页面
页面A打印:/PageA /Main
弹框1打印:/PageA /PageA
页面B打印:/PageB Dialog1
返回弹框1打印:Dialog1 Dialog1
返回页面A打印:/PageA /PageA场景1~3问题:获取路由错乱
解决方法:自己实现路由监听,扩展get写法获取当前路由和上一个路由
场景4: 显示弹框之后再依次跳转两个页面
页面A打印:/PageA /Main
弹框1打印:/PageA /PageA
页面B打印:/PageB Dialog1
页面C打印:/PageC /PageB
返回页面B打印:/PageB /PageB
返回弹框1打印:/PageB /PageB
返回页面A打印:/PageA /PageA场景4问题:当通过弹框按钮跳转两个页面后,依次返回至弹框时,再次点击跳转按钮无效。 因为在返回弹框后,路由状态未正确更新,导致无法再次跳转。打印信息可以看到此时的当前路由显示的是/PageB,然后要跳转的路由也是/PageB,所以跳转是无法生效的。
这是GetX 路由栈(Route Stack)与弹框(Dialog)叠加层管理 之间的一个经典坑点。
我来帮你详细拆解一下成因和解决方案👇
成因:
1、Get.dialog实际上就是开了一个Route
2、我们来看一下GetX的源代码,这里只贴了关键的两段代码:
@override
void didPush(Route route, Route? previousRoute) {
super.didPush(route, previousRoute);
final newRoute = _RouteData.ofRoute(route);
if (newRoute.isBottomSheet || newRoute.isDialog) {
Get.log("OPEN ${newRoute.name}");
} else if (newRoute.isGetPageRoute) {
Get.log("GOING TO ROUTE ${newRoute.name}");
}
RouterReportManager.reportCurrentRoute(route);
_routeSend?.update((value) {
if (route is PageRoute) {
value.current = newRoute.name ?? '';
}
final previousRouteName = _extractRouteName(previousRoute);
if (previousRouteName != null) {
value.previous = previousRouteName;
}
value.args = route.settings.arguments;
value.route = route;
value.isBack = false;
value.removed = '';
value.isBottomSheet =
newRoute.isBottomSheet ? true : value.isBottomSheet ?? false;
value.isDialog = newRoute.isDialog ? true : value.isDialog ?? false;
});
if (routing != null) {
routing!(_routeSend);
}
}
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
final currentRoute = _RouteData.ofRoute(route);
final newRoute = _RouteData.ofRoute(previousRoute);
if (currentRoute.isBottomSheet || currentRoute.isDialog) {
Get.log("CLOSE ${currentRoute.name}");
} else if (currentRoute.isGetPageRoute) {
Get.log("CLOSE TO ROUTE ${currentRoute.name}");
}
if (previousRoute != null) {
RouterReportManager.reportCurrentRoute(previousRoute);
}
_routeSend?.update((value) {
if (previousRoute is PageRoute) {
value.current = _extractRouteName(previousRoute) ?? '';
value.previous = newRoute.name ?? '';
} else if (value.previous.isNotEmpty) {
value.current = value.previous;
}
value.args = previousRoute?.settings.arguments;
value.route = previousRoute;
value.isBack = true;
value.removed = '';
value.isBottomSheet = newRoute.isBottomSheet;
value.isDialog = newRoute.isDialog;
});
routing?.call(_routeSend);
}
第一部分是打开新的路由是触发,第二部分是返回路由的时候触发
第一段代码主要影响部分是这一段:
if (route is PageRoute) {
value.current = newRoute.name ?? '';
}
final previousRouteName = _extractRouteName(previousRoute);
if (previousRouteName != null) {
value.previous = previousRouteName;
}
意思是当打开一个新的页面时,只有当前路由是PageRoute时才更新当前路由的值,前一个路由则是直接获取的previousRoute的route!.settings.name属性。由于Get.dialog实际上就是开了一个Route, 所有当有弹框显示的时候这里获取到的currentRoute,previousRoute都是同一个。
第二段代码主要影响部分是这一段:
if (previousRoute is PageRoute) {
value.current = _extractRouteName(previousRoute) ?? '';
value.previous = newRoute.name ?? '';
} else if (value.previous.isNotEmpty) {
value.current = value.previous;
}
意思是当返回路由的时候,当上个路由是PageRoute时把currentRoute路由设置为上一个路由的名称,value.previous不为空的时候就直接设置为value.previous。
1、当有弹框弹出的情况下获取当前路由(Get.currentRoute)以及上个路由(Get.previousRoute)结果错误
解决方案:自己监听路由。
//历史路由栈记录
class HistoryRouteObserver extends RouteObserver<PageRoute> {
List<Route<dynamic>> history = <Route<dynamic>>[];
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPop(route, previousRoute);
history.remove(route);
//调用Navigator.of(context).pop() 出栈时回调
}
@override
void didPush(Route route, Route? previousRoute) {
super.didPush(route, previousRoute);
history.add(route);
//调用Navigator.of(context).push(Route()) 进栈时回调
}
@override
void didRemove(Route route, Route? previousRoute) {
super.didRemove(route, previousRoute);
history.remove(route);
//调用Navigator.of(context).removeRoute(Route()) 移除某个路由回调
}
@override
void didReplace({Route? newRoute, Route? oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
if (oldRoute != null) {
history.remove(oldRoute);
}
if (newRoute != null) {
history.add(newRoute);
}
//调用Navigator.of(context).replace( oldRoute:Route("old"),newRoute:Route("new")) 替换路由时回调
}
}
在main.dart里使用自定义的路由监听方法
HistoryRouteObserver routeObserver = HistoryRouteObserver();
GetMaterialApp(
navigatorObservers: [
routeObserver,
],
....省略
);
///对get拓展
extension GetExtension on GetInterface {
///路由历史List,包含dialog
List<Route<dynamic>> get history => routeObserver.history;
///获取当前的路由名,包含dialog
String? get currentName => history.last.settings.name;
///获取上一个的路由名,包含dialog
String? get previousName {
if (history.length >= 2) {
return history[history.length - 2].settings.name;
}
return null;
}
///获取当前页面名称,不包含dialog
///为了修复Get.currentRoute的bug,打开两个dialog,然后关闭一个dialog,这时Get.currentRoute的值为dialog
String? get currentPage {
for (int i = history.length - 1; i >= 0; i--) {
var route = history[i];
if (route is PageRoute) {
return route.settings.name;
}
}
return null;
}
///获取上一个页面的名称,不包含dialog
String? get previousPage {
// List<Route> filtered = history.where((element) => element is PageRoute).toList();
List<PageRoute> filtered = history.whereType<PageRoute>().toList();
if (filtered.length >= 2) {
return filtered[filtered.length - 2].settings.name;
}
return null;
}
///是否已打开该页面
bool containName(String name) {
return getRouteByName(name) != null;
}
///通过name获取route,从栈顶开始查找
Route? getRouteByName(String name) {
var index =
history.lastIndexWhere((element) => element.settings.name == name);
if (index != -1) {
return history[index];
}
return null;
}
///通过name获取route
List<Route> getRoutesByName(String name) {
return history.where((element) => element.settings.name == name).toList();
}
//获取所有路由名称
List<String> getAllRouteNameList() {
List<String> routeNames = [];
for (var route in Get.routeTree.routes) {
routeNames.add(route.name);
}
return routeNames;
}
///移除指定的页面,一次
void removeName(String name) {
var route = getRouteByName(name);
if (route != null) {
if (history.last == route) {
//移除当前页面,直接返回
Get.back();
} else {
Get.removeRoute(route);
}
}
}
///移除所有指定的页面
void removeAllName(String name) {
var routes = getRoutesByName(name);
for (var o in routes) {
Get.removeRoute(o);
}
}
}
开放演示案例里的注释部分的打印会发现,获取路由正常。
后续获取当前路由,上一路由可以使用一下四个扩展方法。
Get.currentName
Get.previousName
Get.currentPage
Get.previousPage
2、当从弹框跳转至两个页面后依次返回,再次点击弹框跳转失效。
原因分析:弹框获取的当前路由与目标路由相同,导致跳转逻辑被拦截。
解决方法:
1、在跳转页面的时候先关闭弹框 推荐
Get.back(); // 关闭弹框
Future.delayed(const Duration(milliseconds: 100), () {
Get.to(() => BPage());
});
2、使用原始方法跳转、(虽然能够解决跳转无反应的问题,但是返回弹框后通过Get.currentRoute、Get.previousRoute获取路由依旧有问题)
Get.key.currentState?.push(MaterialPageRoute(builder: (_) => PageB(),settings:RouteSettings(
name: "/PageB",
)));
Navigator.of(Get.overlayContext!).push(MaterialPageRoute(builder: (_) => PageB(),settings:RouteSettings(
name: "/PageB",
)));
Get.key.currentState?.pushNamed("/PageB");
Navigator.of(Get.overlayContext!).pushNamed("/PageB");

被折叠的 条评论
为什么被折叠?



