完整代码
需求
在开发中,关于页面路由,咱们一般会遇到这样的一个需求:顺次翻开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"),
),
],
),
),
);
}
}