RouteTree

A generic library for parsing URI paths.

RouteTree is a generic routing library that makes it easy to implement routing logic based on the path segments of a URI. This is done by nesting Segment nodes to build a tree not unlike the Widget trees you see in Flutter apps.

Quick example

final router = Segment.root<String>(
  create: (context) => "home",
  createError: (context) => "Error!",
  children: [
    Segment.path(
        name: "first_path",
        create: (context) => "routed to first_path",
    ),
    Segment.path(
      name: "second_path",
      create: (context) => "routed to second_path",
      children: [
        Segment.param(
            parser: const UintParser("id"),
            create: (context) => "routed to /second_path/${context["id"]}",
        ),
      ],
    ),
  ],
);

assert(router.route(Uri.parse("/second_path/12")) == "routed to second_path/12");

Note the template parameter that denotes what data type RootSegment.route will return. This makes route_tree so flexible.

How it works

The basic building blocks of this library are Segments, SegmentParsers and the ParseContext.

Segments

Segments are to route_tree what Widgets are to Flutter. Each Segment represents a possible path segment that can be part of a route. Since it's templated it can be used in several ways, like routing in a Flutter app or choosing the correct function to handle incoming HTTP requests in a backend. The abstract class Segment contains several factory constructors to make finding the right one easier.

RootSegment or Segment.root

The root node of any route_tree.

Defines the "/"-route, the root error-handler as well as a list of SegmentVerifiers that can be used to assert certain properties of the route_tree.

By default RootSegments contain findConflictingParamKeys, which returns an error if your route_tree contains duplicate parameter keys like /users/{id}/edit/{id}.

/// Matches "/"
final router = Segment.root<String>(
  create: (context) => "home",
  createError: (context) => "root error",
  children: [
    ...
  ],
);

PathSegment or Segment.path

The Segment you're probably gonna use most often. It defines a simple literal path segment. Internally children of a Segment with a LiteralParser (which by default only applies to PathSegments) are contained in a Map<String, PathSegment> for constant time lookup of corresponding path segments.

Segment.root<String>(
  ...
  children: [
    /// Matches "/about"
    Segment.path(
      name: "about",
      create: (context) => "about",
    ),
    /// Matches "/settings/*", but not "/settings" itself, since [create] was
    /// left null
    Segment.path(
      name: "settings",
      children: [
        /// Matches "/settings/privacy"
        Segment.path(
          name: "privacy",
          create: (context) => "privacy",
        ),
      ],
    ),
  ],
);

RegExpPathSegment or Segment.regExpPath

Used to define one or (usually) more paths that should be matched by one Segment using a regular expression.

Segment.root<String>(
  ...
  children: [
    /// Matches any path segment consisting of letters
    Segment.regExpPath(
      regExp: RegExp(r"[a-zA-Z]+"),
      create: (context) => "letter path",
    ),
  ],
);

ParamSegment or Segment.param

Used to define a path parameter. If the segment of a URI is matched, the matched value is added to the ParseContext, from which it can be queried using ParseContext.operator[]

Segment.root<String>(
  create: (context) => "home",
  createError: (context) => "error!",
  children: [
    /// Matches "/users/*", since [create] was left null
    Segment.path(
      name: "users",
      children: [
        /// Matches "/users/{id}", where {id} is any non-negative integer as defined
        /// by [UintParser].
        Segment.param(
          parser: const UintParser("id"),
          create: (context) => context["id"] as String,
        ),
      ],
    ),
  ],
);

RegExpParamSegment or Segment.regExpParam

Consider URLs used by Twitter where a URL like https://twitter.com/{name}, where name can be any word and a hypothetical Twitter client obviously couldn't hardcode all possible names. This is where this Segment comes into play. It can be used to define a path parameter that matches the provided regular expression and injects it into the ParseContext, from which it can be queried using ParseContext.operator[].

final router = Segment.root<String>(
  create: (context) => "home",
  createError: (context) => "error!",
  children: [
    /// Matches any path segment consisting of letters
    Segment.regExpParam(
      parser: RegExpParamParser.forward(
        key: "name",
        regExp: RegExp(r"\w+"),
      ),
      create: (context) => context["name"] as String,
    ),
  ],
);

assert(router.route(Uri.parse("/flutterdev")) == "flutterdev");

SegmentParsers

The actual parsing logic for looking up correct segments is delegated to instances of SegmentParser. The predefined parsers are:

ParseContext

The ParseContext holds the initial URI as well as any path parameters in the current route. It is passed to the create-function when they're called after a path has been matched.

Libraries

route_tree
route_tree provides a declarative way of defining routers.