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:
- SegmentParser.withFunction, which takes a Parser function and can be used to quickly define custom SegmentParses
- LiteralParser, which is primarily used by PathSegment (& Segment.path) and matches a path segment literally
- RegExpParser, which matches a path segment against a regular expression
- ParamParser, subclass of SegmentParser and base class of parsers that inject matches into the ParseContext
- ParamParser.withFunction, which can be used to quickly define custom ParamParsers
- RegExpPararamParser, similar to RegExpParser, but also injects a successful match into the ParseContext
- IntParser & UintParser, which match integers and non-negative integers respectively. The latter is useful for for
id
path parameters.
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.