Flutter 路由移除指定页面
需求
在开发中,对于页面路由,我们通常会遇到这样的一个需求:依次打开A页面→B页面→C页面,然后在某个业务场景下需要关闭中间的B页面,C页面点击返回时回到A页面。本文将探讨如何在Flutter中实现这种页面导航模式,以及如何提供更好的用户体验。
探索
在Flutter
中我们通常使用路由表进行开发,通过配置路由名称
和对应的页面
MaterialApp(
title: 'Flutter Demo',
initialRoute:"/", //名为"/"的路由作为应用的home(首页)
//注册路由表
routes:{
"/":(context) => MyHomePage(title: 'Flutter Demo Home Page'), //注册首页路由
"new_page":(context) => NewRoute(),
}
);
我们需要定义一个方法通过页面路由名称
来移除指定页面,像这样void removeName(String name)
但实际上Flutter
中并没有这样的方法,只有这样一个方法Navigator.of(context).removeRoute(route)
去移除页面。
我们看一下Route
这个类,
abstract class Route<T> {
/// Initialize the [Route].
///
/// If the [settings] are not provided, an empty [RouteSettings] object is
/// used instead.
Route({ RouteSettings? settings }) : _settings = settings ?? const RouteSettings();
}
有RouteSettings
属性,其中的name
就是我们定义的路由名称
class RouteSettings {
/// Creates data used to construct routes.
const RouteSettings({
this.name,
this.arguments,
});
但我们并不能获取到这个route
,所以我们需要记录一个路由栈
,然后通过路由名
获取对应的route
,再调用Navigator.of(context).removeRoute(route)
去移除页面
代码实现
路由配置
这里是使用GetX来进行路由管理,并通过navigatorObservers
记录路由。如果你不喜欢GetX
,使用Flutter
默认的方式也是可以的
class MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
initialRoute: RouteConfig.home,
getPages: RouteConfig.getPages,
navigatorObservers: [routeHistoryObserver],//记录路由栈
);
}
配置路由表
///路由配置类
class RouteConfig {
static const String home = "/home";
static const String a = "/a";
static const String b = "/b";
static const String c = "/c";
static final List<GetPage> getPages = [
GetPage(name: home, page: () => const MyHomePage()),
GetPage(name: a, page: () => const APage()),
GetPage(name: b, page: () => const BPage()),
GetPage(name: c, page: () => const CPage()),
];
}
记录路由栈
这里我们使用RouteObserver
记录路由栈,通过一个列表history
记录页面打开和关闭
HistoryRouteObserver routeHistoryObserver = HistoryRouteObserver();
///记录路由历史
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")) 替换路由时回调
}
}
删除指定页面
这里我们对GetInterface
写一个拓展类,通过name
找到指定的Route
进行删除
extension GetExtension on GetInterface {
///路由历史
List<Route<dynamic>> get history => routeHistoryObserver.history;
///是否已打开该页面
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();
}
///移除指定的页面,一次
void removeName(String name) {
var route = getRouteByName(name);
if (route != null) {
Get.removeRoute(route);
}
}
///移除所有指定的页面
void removeAllName(String name) {
var routes = getRoutesByName(name);
for (var o in routes) {
Get.removeRoute(o);
}
}
}
使用
使用拓展方法Get.removeName(RouteConfig.b);
即可移除置顶页面
class CPage extends StatelessWidget {
const CPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('CPage'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: () {
Get.removeName(RouteConfig.a);
},
child: const Text("Remove A"),
),
ElevatedButton(
onPressed: () {
Get.removeName(RouteConfig.b);
},
child: const Text("Remove B"),
),
],
),
),
);
}
}
转载自:https://juejin.cn/post/7283315133140107304