Mobile Development 10 min read

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.

Xianyu Technology
Xianyu Technology
Xianyu Technology
How to Build a Lightweight Flutter Router with Dart Annotations

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_builder

Collector 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.

Architecture diagram
Architecture diagram
Annotation processing flow
Annotation processing flow
Source_gen class diagram
Source_gen class diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DARTFlutterMobileCode Generationroutingannotationssource_gen
Xianyu Technology
Written by

Xianyu Technology

Official account of the Xianyu technology team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.