fluro: ^2.0.3
FluroRouter
本身已经是单例模式
/// The static / singleton instance of [FluroRouter]
///
/// {@macro fluro_router}
static final appRouter = FluroRouter();
Handler
)用于匹配不同路由路径的处理方法
static Map<String, Handler> pageRoutes = {
//一般‘/’作为首页路由
'/': Handler(
handlerFunc: (context, Map<String, List<String>> parameters) =>
const HomePage()),
'/category/:id':
Handler(handlerFunc: (context, Map<String, List<String>> parameters) {
String id = parameters['id']?.first ?? '0';
return const CategoryPage(id);
}),
'/detail/:id':
Handler(handlerFunc: (context, Map<String, List<String>> parameters) {
return const DetailPage();
}),
'/login':
Handler(handlerFunc: (context, Map<String, List<String>> parameters) {
return const LoginPage();
})
格式说明:
HandlerFunc
接收上下文 context
,以及携带了路由参数,这个参数是一个Map
,对应路由路径的多个路由参数。例如/dynamic/:id
路由,如果实际路由为/dynamic/1?event=a&event=b
,则 parameters
的格式如下:
{
"id": ["1"],
"event": ["a", "b"]
}
这样其实也只是传递一些简单数据,如果传递复杂数据需要依靠RouteSettings
,后面会封装
在 MaterialApp
中把 onGenerateRoute
设置为 FluroRouter.generator
方法来构建系统路由
class AppRoutes {
static final route = FluroRouter();
static setUp() {
//设置路由未找到的页面
route.notFoundHandler =
Handler(handlerFunc: (context, parameters) => const NotFoundPage());
AppPageHandler.pageRoutes.forEach((path, handler) {
route.define(path,
handler: handler, transitionType: TransitionType.inFromRight);
});
}
}
void main() {
AppRoutes.setUp();
runApp(const MyApp());
}
MaterialApp(onGenerateRoute: AppRoutes.route.generator)
/// Similar to [Navigator.push] but with a few extra features.
Future navigateTo(BuildContext context, String path,
{bool replace = false,//是否替换当前页面
bool clearStack = false,//是否清除当前栈中的页面
bool maintainState = true,//是否保持状态
bool rootNavigator = false,//是否是跟路由
TransitionType? transition,//动画方式
Duration? transitionDuration,//动画执行时间
RouteTransitionsBuilder? transitionBuilder,
RouteSettings? routeSettings}) {//可传递数据
AppRoutes.route.navigateTo(context, 'category/$index')
AppRoutes.route.navigateTo(
context, 'category/$index',
routeSettings: const RouteSettings(arguments: category)),
CategoryPage
接收category
参数
@override
Widget build(BuildContext context) {
var arguments = ModalRoute.of(context)?.settings.arguments;
Category? category;
if (arguments != null && arguments is Category) {
category = arguments;
}
...
}
注意不要在initState中获取参数,因为此时页面树还未创建完成
AppRoutes.router.navigateTo(context, nextPath, clearStack: true);
AppRoutes.router.navigateTo(context, homePath, replace: true);
void pop<T>(BuildContext context, [T? result]) =>
Navigator.of(context).pop(result);
AppRouter.appRouter.pop(context)
Navigator2
Navigator
RouteSettings
Navigator
实现)Bundle
class Bundle {
final Map<String, dynamic> _map = {};
putInt(String k, int v) => _map[k] = v;
int getInt(String k) => _map[k] ?? 0;
putString(String k, String v) => _map[k] = v;
String getString(String k) => _map[k] ?? '';
putBool(String k, bool v) => _map[k] = v;
bool getBool(String k) => _map[k] ?? false;
putList<T>(String k, List<T> v) => _map[k] = v;
List<T> getList<T>(String k) => _map[k] ?? [];
putMap(String k, Map v) => _map[k] = v;
Map getMap(String k) => _map[k] ?? {};
put<T>(String k, T t) => _map[k] = t;
T? get<T>(String k) => _map[k];
@override
String toString() {
return _map.toString();
}
}
FluroRouter
进行扩展class _AppRouter extends FluroRouter {
///bundle and routeSettings only one
///Both can use bundle.=>>routeSettings.arguments=bundle
@override
Future navigateTo(BuildContext context, String path,
{bool replace = false,
bool clearStack = false,
bool maintainState = true,
bool rootNavigator = false,
RedirectInterceptor? interceptor,
String? name,
Bundle? bundle,
TransitionType? transition,
Duration? transitionDuration,
RouteTransitionsBuilder? transitionBuilder,
RouteSettings? routeSettings}) {
if (interceptor?.needRedirect() ?? false) {
return interceptor!.navigateTo(context);
}
if (bundle != null) {
routeSettings = RouteSettings(arguments: bundle, name: name ?? path);
}
return super.navigateTo(context, path,
replace: replace,
clearStack: clearStack,
maintainState: maintainState,
rootNavigator: rootNavigator,
transition: transition,
transitionDuration: transitionDuration,
transitionBuilder: transitionBuilder,
routeSettings: routeSettings);
}
///back home and clear other page
///example:Home ->A->B->C:popToHome ->Home
void popToHome(BuildContext context) {
popUntil(context, (route) => route.isFirst);
}
@override
void pop<T>(BuildContext context, [T? result]) {
if (canPop(context)) {
super.pop(context, result);
}
}
void popUntil(BuildContext context, RoutePredicate predicate) {
Navigator.popUntil(context, predicate);
}
bool canPop(BuildContext context) {
return Navigator.canPop(context);
}
}
final AppRouter = _AppRouter();
abstract class RedirectInterceptor {
//是否需要重定向
bool needRedirect();
//重定向页面导航
Future navigateTo(BuildContext context);
}
class LoginInterceptor extends RedirectInterceptor {
@override
Future navigateTo(BuildContext context) {
return AppRouter.navigateTo(context, AppPagePath.login);
}
@override
bool needRedirect() {
return !AppManager.getInstance().isLogin;
}
}
onPressed: () => AppRouter.navigateTo(context,AppPagePath.settingMessage,
interceptor: LoginInterceptor()),
onTap: () => AppRouter.appRouter.navigateTo(context, AppPagePath.detail,
bundle: Bundle()..put('detail', model)),
复杂场景下的pop
AppRouter.pop(context))
AppRouter.popToHome(context))
手动控制保留那些page
只保留首页以及category
页面
AppRouter.popUntil(context, (route) => route.isFirst || route.settings.name == 'category');
返回至首页(底部Tab
的第一个page
)
需要使用状态管理比如:Provider
onPressed: () {
context.read<AppTabModel>().curIndex = 0;
AppRouter.popToHome(context);
}),