How to Build a Lightweight Flutter Router with Dart Annotations
This article explains how to replace cumbersome if‑else or manual map‑based routing in Flutter with a lightweight, annotation‑driven solution that automatically generates a page‑to‑URL mapping at compile time, using Dart's source_gen, build, and analyser packages.
Background
In Flutter, routing maps a URL to a widget. Traditional implementations use stacked if‑else or manual map tables, which are fragile, hard to maintain, and cannot be linked directly to page definitions.
Problems with Traditional Approaches
Every change requires updating many branches, reducing stability.
Page constructors are scattered, no uniform abstraction.
Mapping configuration is detached from the page, unclear ownership.
Annotation‑Based Routing
Use Dart annotations processed at compile time to generate a centralized routing table without runtime reflection. Two annotation types are defined: @ARoute – placed on a page class to declare one or more URL patterns. @ARouteRoot – placed on a class that provides the router entry point.
Key Packages
build – defines Builder classes that read source files and write generated assets.
analyzer – supplies a complete AST for each Dart file.
source_gen – wraps Builder with Generator and GeneratorForAnnotation to intercept annotated elements.
Generation Workflow
Two builders are declared in build.yaml: routeBuilder runs first, uses RouteGenerator (a GeneratorForAnnotation<ARoute>) to collect all @ARoute metadata and store it in a static Collector. No file is emitted at this stage. routeWriteBuilder runs after all source files have been scanned. It invokes RouteWriteGenerator, reads the collected metadata, and writes a single routing file (e.g., router.g.dart) that contains the mapping table and helper classes.
Before Annotation
class Router {
Widget route(String url, Map params) {
if (url == 'myapp://apage') {
return PageA(url);
} else if (url == 'myapp://bpage') {
return PageB(url, params);
}
// … more branches
return null;
}
}After Annotation
import 'package:annotation_route/route.dart';
class MyPageOption {
String url;
Map<String, dynamic> query;
MyPageOption(this.url, this.query);
}
class Router {
ARouteInternal internal = ARouteInternalImpl();
Widget pageFromUrlAndQuery(String urlString, Map<String, dynamic> query) {
ARouteResult routeResult = internal.findPage(
ARouteOption(url: urlString, params: query),
MyPageOption(urlString, query),
);
if (routeResult.state == ARouteResultState.FOUND) {
return routeResult.widget;
}
return DefaultWidget;
}
}Builder Configuration (build.yaml)
targets:
$default:
builders:
annotation_route|route_builder:
generate_for:
- lib/**/*.dart
options:
# builder‑specific options
annotation_route|route_write_builder:
generate_for:
- lib/**/*.dart
options:
# ensure this runs after route_builderCollector and Writer
The Collector is a singleton that accumulates a list of ARouteInfo objects (URL, page type, option class). RouteWriteGenerator iterates over this list and produces code similar to:
final Map<String, ARouteInfo> _routeTable = {
'myapp://apage': ARouteInfo(PageA, MyPageOption),
'myapp://bpage': ARouteInfo(PageB, MyPageOption),
// …
};Runtime Usage
Application code only needs to import the generated router and call pageFromUrlAndQuery. The router looks up the URL in the generated table, constructs the page using the stored option class, and returns the widget. If the URL is not found, a default widget is returned.
Advantages
Mapping is generated automatically from annotations, eliminating manual if‑else or map tables.
Page‑level ownership is clear because the annotation lives next to the page class.
Only one generated file is produced, reducing build‑time overhead.
Supports multiple URLs per page by allowing multiple @ARoute annotations on the same class.
Repository
Source code, installation instructions and examples are available at https://github.com/alibaba-flutter/annotation_route.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
