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优缺点

  • 优点
    1. 使用简单
    2. 支持大量的路由动画
    3. 支持路由拦截
    4. 支持Web 路由格式
  • 缺点
    1. 不支持Navigator2
    2. pop方式单一,复杂场景需要依靠Navigator
    3. 传递大量参数麻烦,需要依靠RouteSettings
    4. 路由拦截需要自己实现

封装

  1. 支持复杂参数
  2. pop支持复杂场景(使用Navigator实现)
  3. 自定义重定向(路由拦截)
  4. 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);
}),
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值