Flutter 的 fluro 路由

这里给你一套最常用的用法速查 + 代码模板,直接抄就能跑。

概念速览

  • fluro 是一个轻量的 Flutter 路由库,支持路径参数、查询参数、自定义转场动画、404 页面等。

  • v2 开始类名叫 FluroRouter(避免和 Flutter 自带 Router 混淆)。

安装

  • pubspec.yaml 增加依赖:fluro(版本请看 pub.dev 最新)

初始化(建议单独建个 router.dart)

import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
// import your pages here

class AppRouter {
  static final FluroRouter router = FluroRouter();

  static void setup() {
    // 404
    router.notFoundHandler = Handler(
      handlerFunc: (context, params) => const Scaffold(
        body: Center(child: Text('404 Not Found')),
      ),
    );

    // 首页
    router.define(
      '/',
      handler: Handler(handlerFunc: (_, __) => const HomePage()),
    );

    // 动态路径参数 + 可选查询参数
    router.define(
      '/user/:id',
      handler: Handler(handlerFunc: (context, params) {
        final id = params['id']?.first ?? '';
        final name = params['name']?.first; // ?name=xxx
        return UserPage(id: id, name: name);
      }),
      transitionType: TransitionType.cupertino,
    );

    // 传复杂字符串:先 encode,再在页面里 decode
    router.define(
      '/webview/:url',
      handler: Handler(handlerFunc: (context, params) {
        final url = Uri.decodeComponent(params['url']!.first);
        return WebViewPage(url: url);
      }),
    );

    // 用于演示返回值
    router.define(
      '/picker',
      handler: Handler(handlerFunc: (_, __) => const PickerPage()),
    );
  }
}

在 MaterialApp 中接管路由

void main() {
  AppRouter.setup();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fluro Demo',
      onGenerateRoute: AppRouter.router.generator,
    );
  }
}

常用操作

  • 跳转(路径参数 + 查询参数)

    // /user/:id + ?name=xxx
    AppRouter.router.navigateTo(
      context,
      '/user/123?name=${Uri.encodeComponent("张三")}',
    );
     

  • 自定义转场动画

    AppRouter.router.navigateTo(
      context,
      '/picker',
      transition: TransitionType.custom,
      transitionBuilder: (ctx, anim, sec, child) =>
          FadeTransition(opacity: anim, child: child),
      transitionDuration: const Duration(milliseconds: 250),
    );
     

  • 替换当前页 / 清空栈(比如登录完成进首页)

    AppRouter.router.navigateTo(
      context,
      '/home',
      replace: true,
      clearStack: true,
    );
     

  • 接收返回值

    final result = await AppRouter.router.navigateTo(context, '/picker');
    // 在 PickerPage 里用 Navigator.of(context).pop('选中的值');
     

  • 传 URL/中文等复杂字符串

    final url = Uri.encodeComponent('https://flutter.dev?q=中文');
    AppRouter.router.navigateTo(context, '/webview/$url');

下面按“概念 → 匹配机制 → API 细节 → 进阶用法/坑位”的顺序,把 fluro 路由再讲细一点,配上实用代码片段,拿去即用。

一、核心概念

  • FluroRouter:路由中枢,负责注册和匹配,挂到 MaterialApp.onGenerateRoute。

  • Handler:某条路由被匹配后怎么处理。handlerFunc 返回要展示的 Widget(或做函数式处理)。

  • params:传给 handlerFunc 的参数表,类型为 Map<String, List<String>>,包含路径参数和查询参数。

  • TransitionType:转场枚举(native/material/cupertino/inFromRight/inFromLeft/inFromBottom/fadeIn/custom 等)。

  • notFoundHandler:没有匹配到任何路由时的兜底页面。

二、路由匹配机制(怎么匹配到你的页面)

  • 以斜杠分段匹配:/user/123 会依次匹配 /、user、123 这三个段。

  • 动态段用 :param 表示,只匹配单段:

    • /user/:id 可以匹配 /user/123

    • 只能匹配单段值,不能直接吃掉多段(包含斜杠)——要用 Uri.encodeComponent 先编码。

  • 查询参数和路径参数合并进同一个 params:

    • /user/123?name=张三 → params['id']!.first '123',params['name']!.first '张三'

    • 同名多值:/search?tag=a&tag=b → params['tag'] == ['a','b']

  • 可选参数不内置语法。要么走查询参数,要么定义两条路由分别覆盖。

  • 404:没有任何定义匹配,就走 notFoundHandler。

三、定义与初始化 建议独立文件集中管理路由,便于全局检索与重构。

import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';

class AppRouter {
  static final FluroRouter router = FluroRouter();

  static void setup() {
    // 404
    router.notFoundHandler = Handler(
      handlerFunc: (_, __) => const Scaffold(
        body: Center(child: Text('404 Not Found')),
      ),
    );

    // 首页
    router.define(
      '/',
      handler: Handler(handlerFunc: (_, __) => const HomePage()),
      // transitionType: TransitionType.native, // 可在定义时设默认转场
    );

    // 动态参数 + 查询参数
    router.define(
      '/user/:id',
      handler: Handler(handlerFunc: (context, params) {
        final id = params['id']?.first ?? '';
        final name = params['name']?.first; // ?name=xxx
        return UserPage(id: id, name: name);
      }),
      transitionType: TransitionType.cupertino,
    );

    // 复杂字符串(需要 encode/decode)
    router.define(
      '/webview/:url',
      handler: Handler(handlerFunc: (_, params) {
        final url = Uri.decodeComponent(params['url']!.first);
        return WebViewPage(url: url);
      }),
    );
  }
}

// 在 MaterialApp 中接管
class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fluro Demo',
      onGenerateRoute: AppRouter.router.generator,
    );
  }
}

四、跳转 API 详解(navigateTo 的常用参数)

// 基本跳转
await AppRouter.router.navigateTo(
  context,
  '/user/123?name=${Uri.encodeComponent("张三")}',
  // 是否替换当前页(pushReplacement)
  replace: false,
  // 是否清空栈(pushAndRemoveUntil)
  clearStack: false,
  // 跳转时覆盖转场(优先级高于定义时的 transitionType)
  transition: TransitionType.fadeIn,
  transitionDuration: const Duration(milliseconds: 250),
  // 自定义转场
  transitionBuilder: (ctx, anim, secAnim, child) =>
      FadeTransition(opacity: anim, child: child),
  // 透传 RouteSettings(可用 ModalRoute.of(context)?.settings.arguments 拿)
  routeSettings: const RouteSettings(arguments: {'from': 'home'}),
);

// 替换当前页 + 清空返回栈(典型:登录完成进首页)
AppRouter.router.navigateTo(context, '/home', replace: true, clearStack: true);

五、参数传递/获取的细节与技巧

  • 路径参数:params['id']?.first

  • 查询参数:params['keyword']?.first

  • 多值查询:params['tag'] // List<String>

  • 复杂/包含斜杠或中文的值:务必在拼接 URL 前用 Uri.encodeComponent,页面内再 decode。

  • 也可通过 RouteSettings.arguments 传递对象:

    AppRouter.router.navigateTo(
      context,
      '/detail/42',
      routeSettings: RouteSettings(arguments: MyObj(...)),
    );
    // 页面里:
    final args = ModalRoute.of(context)?.settings.arguments as MyObj?;

六、转场动画的几种用法

  • 路由层面默认转场:define 时的 transitionType

  • 调用层面覆盖转场:navigateTo 的 transition/transitionBuilder

  • 自定义动画建议统一封装函数,避免在业务里散落重复代码

七、404 与重定向

  • 统一兜底页:

    router.notFoundHandler = Handler(
      handlerFunc: (_, __) => const NotFoundPage(),
    );

  • 重定向(两种常见姿势,二选一):
    1) 在调用方判断:如果老地址就跳新地址
    2) 在老路由的 handler 里重定向

    • 如果你的 fluro 版本支持 HandlerType.function:

      router.define(
        '/old',
        handler: Handler(
          type: HandlerType.function,
          handlerFunc: (context, params) {
            AppRouter.router.navigateTo(context!, '/new', replace: true);
            return null;
          },
        ),
      );

    • 如果不支持,使用微任务避免首帧闪烁:

      router.define('/old', handler: Handler(handlerFunc: (context, params) {
        Future.microtask(() =>
            AppRouter.router.navigateTo(context!, '/new', replace: true));
        return const SizedBox.shrink();
      }));

八、鉴权/路由守卫的常见实现

  • 调用方拦截(简单稳定,推荐):

    Future<T?> guardGo<T>(BuildContext ctx, String path) {
      final authed = ctx.read<Auth>().isLoggedIn;
      return AppRouter.router.navigateTo(ctx, authed ? path : '/login');
    }

  • 页面级守卫(在 handler 内判断):

    router.define('/profile', handler: Handler(handlerFunc: (ctx, params) {
      final authed = ctx!.read<Auth>().isLoggedIn;
      return authed ? const ProfilePage() : const LoginPage();
    }));

  • 函数路由(HandlerType.function)做“先验证再跳转”。

九、嵌套路由/多 Navigator(Tab/底部导航)

  • 每个子 Navigator 仍可使用同一个 FluroRouter 的 generator:

    Navigator(
      key: _tabNavKey,
      onGenerateRoute: AppRouter.router.generator,
    );

  • 注意使用对应子 Navigator 的 context 跳转,这样返回栈不会串。

十、与 Navigator 2.0/Web 的关系

  • fluro 基于 Navigator 1.0(onGenerateRoute)。需要完全声明式/URL 同步的项目可以考虑 go_router/beamer。

  • Flutter Web 直接在地址栏输入已定义的路径能命中(onGenerateRoute 生效)。刷新不会丢失路径,但你的页面需要自行做状态恢复。

十一、常见坑位

  • BuildContext 在 handler 中是可空(BuildContext?),有些路径解析阶段可能为 null,使用前判断或尽量少依赖 context。

  • 忘记对中文/带斜杠参数做 Uri.encodeComponent → 解析失败或 404。

  • 只定义 :param 想匹配多段值(含 /)→ 不行,改走 encode 或改用查询参数。

  • 可选参数不支持语法糖 → 用查询参数或额外定义一条路由。

  • 多终端统一转场:iOS 用 cupertino,Android 用 material/native,可按平台分支或统一用自定义动画。

十二、项目结构建议

  • routes.dart:字符串常量/构造器,避免魔法字串

  • app_router.dart:集中注册 define、notFoundHandler

  • 业务模块各自 pages/ 子目录

  • 可选:route helpers 封装强类型跳转

    class Routes {
      static const home = '/';
      static String user(String id, {String? name}) =>
          '/user/$id${name == null ? '' : '?name=${Uri.encodeComponent(name)}'}';
    }
    
    extension RouteX on BuildContext {
      Future goUser(String id, {String? name}) =>
          AppRouter.router.navigateTo(this, Routes.user(id, name: name));
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值