Flutter路由框架-Fluro的使用和封装
依赖
fluro: ^2.0.3
Fluro使用步骤
构建FluroRouter路由实例,单例
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);
-
back 返回
void pop<T>(BuildContext context, [T? result]) => Navigator.of(context).pop(result);
AppRouter.appRouter.pop(context)
Fluro优缺点
-
优点
- 使用简单
- 支持大量的路由动画
- 支持路由拦截
- 支持Web 路由格式
-
缺点
- 不支持
Navigator2
- pop方式单一,复杂场景需要依靠
Navigator
- 传递大量参数麻烦,需要依靠
RouteSettings
- 路由拦截需要自己实现
- 不支持
封装
- 支持复杂参数
- pop支持复杂场景(使用
Navigator
实现) - 自定义重定向(路由拦截)
- github源码
-
复杂参数的封装
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); }),
\