fluro_router_generate 1.1.0 copy "fluro_router_generate: ^1.1.0" to clipboard
fluro_router_generate: ^1.1.0 copied to clipboard

A powerful Flutter library for routing based on Fluro, featuring annotations and code generation, with animated transitions, route matching, and support for custom navigation.

中文 | English


fluro_router_generate #

Fluro-based routing for Flutter with annotations and code generation. Register routes automatically via @RouterAnnotation, with path params, query params, and RouteSettings.arguments. Supports animated transitions and custom navigation.


1. Dependencies #

dependencies:
  fluro_router_generate:
    path: ../  # or a version from pub.dev

dev_dependencies:
  build_runner: ^2.10.5

2. Create route entry #

In lib/xxxx.dart:

import 'package:fluro_router_generate/fluro_router_generate.dart';
export 'router_config.g.dart';

@EntranceAnnotation()
class RouteConfig extends FluroConfig {
  RouteConfig._();
  static final RouteConfig instance = RouteConfig._();
}

3. Annotate your pages #

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

@RouterAnnotation(path: '/home/:id', description: 'Home', defaultParams: {'id': '-'})
class HomePage extends StatelessWidget {
  const HomePage({super.key, required this.id});
  final String id;

  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text('$id')), body: const SizedBox());
  }
}

4. Configure build.yaml (required) #

Your app project must have a root build.yaml, and only the file with @EntranceAnnotation may trigger the builder:

# Only the route entry file triggers generation, e.g. lib/router/router_config.g.dart
targets:
  $default:
    builders:
      fluro_router_generate|router_library:
        generate_for:
          include:
            # Path must match your route entry file
            # - lib/router/router_config.dart
  • If you don’t set this or omit the entry from include: build_runner will run the builder on all .dart files. For any file without @EntranceAnnotation, the builder returns empty output and fails with a message to configure build.yaml. The whole build will fail.
  • So you must list only the route entry file (the one with @EntranceAnnotation) in include.
  • If the entry file has no @EntranceAnnotation or build.yaml is wrong, build_runner will also fail.

5. Generate route table #

dart run build_runner build --delete-conflicting-outputs

This generates router_config.g.dart with generatedHandlers and initAllHandlers().


6. Initialize in main and use #

import 'package:fluro_router_generate/fluro_router_generate.dart';
import 'package:example/router/router_config.dart';

void main() {
  RouteConfig.instance.initAllHandlers();
  runApp(const MyApp());
}

// MaterialApp
onGenerateRoute: FluroConfig.router.generator,

// Navigate
FluroConfig.router.navigateTo(context, '/home/1');

7. Route guards (optional) #

Guards run before each Navigator.push, so you can allow, redirect, or cancel navigation. This is different from NavigatorObserver, which only runs after push/pop.

import 'package:fluro_router_generate/fluro_router.dart';

// e.g. redirect to login when visiting /admin without auth
FluroConfig.addGuard((ctx) async {
  if (ctx.path.startsWith('/admin') && !isLoggedIn()) {
    return GuardResult.redirect('/login');
  }
  return GuardResult.allow;
});

// Remove or clear when needed
FluroConfig.removeGuard(myGuard);
FluroConfig.clearGuards();
API Description
addGuard(guard) Append a guard (runs in order).
insertGuard(index, guard) Insert at index.
removeGuard(guard) Remove first matching guard by reference.
clearGuards() Remove all guards.

Guard returns: GuardResult.allow, GuardResult.redirect(newPath), GuardResult.cancel. Redirect is limited to 5 hops to avoid loops.


Annotation reference #

Field Description
path Route path, e.g. /home/:id, /search?keyword=
description Optional, comment in generated list
defaultParams Optional, default values, e.g. {'id': '-', 'page': 1}
constructorParams Optional: pathParams / queryParams / routeSettingsArguments / none — how params are passed to the constructor
module Optional, module name for grouping/splitting; with build.yaml split_modules can emit a separate .g.dart

See example/ for more.


Examples #

1. Parameter types #

Scenario path example constructorParams Notes
No params /home none No arguments
Single path param /detail/:id pathParams Matches /detail/99, param id
Multiple path params /user/:userId/post/:postId pathParams Matches /user/1/post/2, params userId, postId
Query params /search?keyword=&page=1 queryParams Params from query string
routeSettings with defaultParams /pass-args routeSettingsArguments + defaultParams: {'title': 'Default', 'count': 0} Via RouteSettings.arguments, fallback to defaults
routeSettings without defaultParams /pass-args-no-defaults routeSettingsArguments (no defaultParams) Param names from constructor, for ad-hoc args

No params:

@RouterAnnotation(path: '/home', constructorParams: HandlerConstructorParams.none)
class HomePage extends StatelessWidget {
  const HomePage({super.key});
  // ...
}

Single path param:

@RouterAnnotation(
  path: '/detail/:id',
  defaultParams: {'id': '0'},
  constructorParams: HandlerConstructorParams.pathParams,
)
class DetailPage extends StatelessWidget {
  const DetailPage({super.key, required this.id});
  final String id;
  // ...
}

Multiple path params:

@RouterAnnotation(
  path: '/user/:userId/post/:postId', 
  defaultParams: {'userId': '0', 'postId': '0'},
  constructorParams: HandlerConstructorParams.pathParams,
)
class PostPage extends StatelessWidget {
  const PostPage({super.key, required this.userId, required this.postId});
  final String userId;
  final String postId;
  // ...
}

Query params:

@RouterAnnotation(
  path: '/search?keyword=&page=1',
  defaultParams: {'keyword': '', 'page': '1'},
  constructorParams: HandlerConstructorParams.queryParams,
)
class SearchPage extends StatelessWidget {
  const SearchPage({super.key, required this.keyword, required this.page});
  final String keyword;
  final String page;
  // ...
}

routeSettings.arguments (with defaultParams):

@RouterAnnotation(
  path: '/pass-args',
  defaultParams: {'title': 'Default title', 'count': 0},
  constructorParams: HandlerConstructorParams.routeSettingsArguments,
)
class PassArgsPage extends StatelessWidget {
  const PassArgsPage({super.key, required this.title, required this.count});
  final String title;
  final int count;
  // ...
}

2. Navigation and passing arguments #

// No params
FluroConfig.push('/home', context: context);

// Path params
FluroConfig.push('/detail/99', context: context);
FluroConfig.push('/user/1/post/2', context: context);

// Query params
FluroConfig.push('/search?keyword=test&page=1', context: context);

// routeSettings.arguments
FluroConfig.push(
  '/pass-args',
  context: context,
  routeSettings: RouteSettings(
    name: '/pass-args',
    arguments: {'title': 'My title', 'count': 42},
  ),
);

3. Modules and split_modules #

Add module to annotations; same module name is grouped (or written to a separate file):

@RouterAnnotation(
  path: '/home',
  module: 'main',
  constructorParams: HandlerConstructorParams.none,
)
class HomePage extends StatelessWidget { ... }

@RouterAnnotation(
  path: '/payment/:orderId',
  module: 'payment',
  defaultParams: {'orderId': ''},
  constructorParams: HandlerConstructorParams.pathParams,
)
class PaymentPage extends StatelessWidget { ... }

To emit a separate file e.g. router_config_payment.g.dart for the payment module, add split_modules in your project root build.yaml:

targets:
  $default:
    builders:
      fluro_router_generate|router_library:
        generate_for:
          include:
            - lib/router/router_config.dart
        options:
          split_modules:
            - payment
            - admin

Modules not in split_modules are inlined into the main generated file. See example/ for a full sample.


FAQ #

defaultParams vs constructor: which decides parameter names? #

  • Parameter set and defaults: defaultParams wins. Only when defaultParams is omitted (or {}) are names inferred from the constructor.
  • Types: After the set is fixed, types always come from the constructor.
  • So defaultParams has priority for which params and defaults; the constructor is used when defaultParams is missing and always for types.

Constructor type is A, defaultParams value looks like type B. Which type is used? #

  • Type follows the constructor type A. The literal in defaultParams (B) only affects the default value, not how the value is parsed.
  • If A is an object type, generated code uses argsMap?['key'] as A, not B for int/double etc.
  • Conclusion: Constructor type A wins; defaultParams’ type B does not override A.

Troubleshooting #

Issue What to check
build_runner fails with a message about build.yaml Ensure the only file in generate_for.include is your route entry file (the one with @EntranceAnnotation). Do not include every .dart file.
Route not found at runtime Call RouteConfig.instance.initAllHandlers() before runApp(), and use onGenerateRoute: FluroConfig.router.generator in MaterialApp.
Wrong or missing params on the page Match constructorParams to how you pass data: pathParams for /path/:id, queryParams for ?key=, routeSettingsArguments for RouteSettings.arguments. Use defaultParams when you need defaults.
Generated file is empty or outdated Run dart run build_runner build --delete-conflicting-outputs again; ensure the entry file is the one listed in build.yaml and has @EntranceAnnotation().
1
likes
0
points
28
downloads

Publisher

unverified uploader

Weekly Downloads

A powerful Flutter library for routing based on Fluro, featuring annotations and code generation, with animated transitions, route matching, and support for custom navigation.

Repository

License

unknown (license)

Dependencies

analyzer, build, build_runner, flutter, flutter_lints, glob, source_gen

More

Packages that depend on fluro_router_generate