flutter开发实战-GoRouter路由go与push区别实践
GoRouter是一个flutter的声明性路由包,使用路由器API提供一个方便的、基于url的API,用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场景。
之前使用了GoRouter,介绍了GoRouter相关使用细节。这里来测试一下GoRouter路由go与push区别。
在Flutter Web项目中,usePathUrlStrategy是一个用于设置URL策略的方法。默认情况下,Flutter Web使用的是Hash(#)URL,这意味着路由信息会被包含在URL的#后面,这样对SEO不是很友好。
如果你希望使用Path URL Strategy,即example.com/path而不是example.com/#/path,你可以在main函数中调用usePathUrlStrategy()。
一、使用页面跳转
配置路由home->detail->comment
final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
),
GoRoute(
path: 'comment/:id',
builder: (BuildContext context, GoRouterState state) {
String? id = state.pathParameters['id'];
print("id:${id}"); // Get "id" param from URL
return CommentScreen(id: id);
},
)
],
),
],
);
- 在使用GoRouterHelper的go方法进行路由跳转的情况。
home->details
ElevatedButton(
onPressed: () => context.go('/details'),
child: const Text('Go to the Details screen'),
),
details->comment
ElevatedButton(
onPressed: () => context.go('/comment/55555'),
child: const Text('Go to the comment screen'),
),
使用go跳转效果如下
- 在使用GoRouterHelper的push方法进行路由跳转的情况。
home->details
ElevatedButton(
onPressed: () => context.push('/details'),
child: const Text('Go to the Details screen'),
),
details->comment
ElevatedButton(
onPressed: () => context.push('/comment/55555'),
child: const Text('Go to the comment screen'),
),
使用push跳转效果如下
二、go与push区别
通过以上测试,我们可以看到区别,如图
GoRouter中的go与push区别
go:使用Go时候,会在主页顶部显示模态页面
push:使用情况,会在上一级页面显示模态页面
在使用Go,go通过丢弃之前的路由(/detail)跳转到目标路由(/comment),因为(/comment)不是(/detail)的路由
在使用push,push总是将目标路由添加到现有路由之上,保留导航堆栈。
例如 A->B->C->D页面跳转逻辑
如果使用go的情况下
A->B->C->D,从C或者D页面返回pop的情况下,会直接返回到A页面
如果使用push的情况下
A->B->C->D,从D页面返回会回到C页面,C页面返回会回到B页面,B页面返回会回到A页面。
在flutter的web项目跳转的时候的区别
在使用Go情况下,setUrlStrategy(PathUrlStrategy());设置后,浏览器会显示跳转的路径如http://localhost:55478/details
在使用push的情况下,setUrlStrategy(PathUrlStrategy());设置后,浏览器不会显示。没有遵循PathUrlStrategy的path url策略
查看源码GoRoute源码
可以看出
/// 当我们查看GoRoute源码时候,会发现一个GoRouteInformationProvider类,实现系统RouteInformationProvider类的方法:routerReportsNewRouteInformation
/// push:Pushes the location
as a new route on top of base
.
/// push 将location推入页面stack的顶部
///
/// go:Replace the current route matches with the location
.
/// go 将location替换当前路线
///
/// 可以查看NavigatingType可以看到相应的区别
enum NavigatingType {
/// Push new location on top of the [RouteInformationState.baseRouteMatchList].
push,
///
/// Push new location and remove top-most [RouteMatch] of the
/// [RouteInformationState.baseRouteMatchList].
pushReplacement,
///
/// Push new location and replace top-most [RouteMatch] of the
/// [RouteInformationState.baseRouteMatchList].
replace,
///
/// Replace the entire [RouteMatchList] with the new location.
go,
/// }
GoRouter是实现系统API类RouteInformationProvider与SystemNavigator使用,
在routerReportsNewRouteInformation中调用SystemNavigator的selectMultiEntryHistory();与SystemNavigator.routeInformationUpdated方法。
完整代码如下
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
setUrlStrategy(PathUrlStrategy());
runApp(const MyApp());
}
class MyNavigatorObserver extends NavigatorObserver {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
print('did push route');
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
print('did pop route');
}
}
final GoRouter _router = GoRouter(
observers: [MyNavigatorObserver()],
redirect: (BuildContext context, GoRouterState state) {
final isLogin = true; // your logic to check if user is authenticated
if (!isLogin) {
return '/login';
} else {
return null; // return "null" to display the intended route without redirecting
}
},
errorBuilder: (
BuildContext context,
GoRouterState state,
) {
// ErrorPage
return ErrorScreen();
},
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details',
builder: (BuildContext context, GoRouterState state) {
return const DetailsScreen();
},
routes: <RouteBase>[
// Add child routes
GoRoute(
path: 'sub-details',
// NOTE: Don't need to specify "/" character for router’s parents
builder: (context, state) {
return SubDetailsScreen();
},
pageBuilder: (context, state) {
return CustomTransitionPage(
child: SubDetailsScreen(),
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return FadeTransition(
opacity:
Tween(begin: 0.1, end: 1.0).animate(CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
)),
child: child,
);
},
);
},
),
],
),
GoRoute(
path: 'comment/:id',
builder: (BuildContext context, GoRouterState state) {
String? id = state.pathParameters['id'];
print("id:${id}"); // Get "id" param from URL
return CommentScreen(id: id);
},
),
GoRoute(
path: 'search',
// builder: (BuildContext context, GoRouterState state) {
// String? keyword = state.queryParameters['keyword'];
// print("keyword:${keyword}"); // Get "id" param from URL
// return SearchScreen(keyword: keyword);
// },
pageBuilder: (context, state) {
String? keyword = state.queryParameters['keyword'];
print("keyword:${keyword}"); // Get "id" param from URL
return CustomTransitionPage(
child: SearchScreen(keyword: keyword),
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
return FadeTransition(
opacity:
Tween(begin: 0.1, end: 1.0).animate(CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
)),
child: child,
);
},
);
},
),
],
),
],
);
/// The main app.
class MyApp extends StatelessWidget {
/// Constructs a [MyApp]
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
);
}
}
/// The home screen
class HomeScreen extends StatelessWidget {
/// Constructs a [HomeScreen]
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => context.go('/details'),
child: const Text('Go to the Details screen'),
),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.go('/details/sub-details'),
child: const Text('Go to the SubDetails screen'),
),
],
),
),
);
}
}
/// The details screen
class DetailsScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => context.push('/comment/55555'),
child: const Text('Go to the comment screen'),
),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.push('/search?keyword=myName'),
child: const Text('Go to the Search screen'),
),
],
),
),
);
}
}
/// The details screen
class SubDetailsScreen extends StatelessWidget {
/// Constructs a [SubDetailsScreen]
const SubDetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('SubDetailsScreen Screen')),
body: Center(
child: ElevatedButton(
onPressed: () => context.push('/details'),
child: const Text('Go back to the Details screen'),
),
),
);
}
}
/// The details screen
class CommentScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const CommentScreen({super.key, this.id});
final String? id;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('CommentScreen Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("param id:${id}"),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.pop(),
child: const Text('Go back to the Details screen'),
),
],
)),
);
}
}
/// The Search screen
class SearchScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const SearchScreen({super.key, this.keyword});
final String? keyword;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('SearchScreen Screen')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("param keyword:${keyword}"),
SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () => context.push('/'),
child: const Text('Go back to the details screen'),
),
],
)),
);
}
}
/// The Search screen
class ErrorScreen extends StatelessWidget {
/// Constructs a [DetailsScreen]
const ErrorScreen();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ErrorScreen Screen')),
body: Center(
child: Container(
child: const Text('Error info'),
),
),
);
}
}
三、小结
flutter开发实战-GoRouter路由go与push区别实践
学习记录,每天不停进步。