Path

Path

The Path Module is designed to streamline navigation and data passing in Flutter Web projects or APIs. By enabling the definition of routes that associate specific URLs with corresponding widgets or tasks, the Path Module ensures that users are directed to the right part of the application based on the URL accessed. This module is invaluable for developers looking to create well-structured navigation systems and enhance the dynamic nature of their applications.

Route

A Route is an abstract class that you can extend to define your application's routes. It serves three primary purposes:

  1. Define fields: You can define both segment properties and query properties which will be set based on a path. Segment properties are based on an actual segment of the path, such as [userId] in /user/[userId], whereas query properties are additional properties in addition to the segments themselves, such as view in /user/[userId]?view=public.

  2. Define a PathDefinition: The PathDefinition specifies what the path will look like and determines whether the Route matches a given path. It can include predefined strings or properties.

  3. Define how to instantiate the class: This is necessary because every time you navigate to a Route, you are returned a copy of the Route with all the fields filled in with the correct values.

Here's an example of a Route that takes a name from the path itself and consumes a repeat query property.

example/lib/pages/greet_page.dart
class GreetRoute with IsRoute<GreetRoute> {
  late final nameProperty = field<String>(name: 'name').required(); // Contains the non-nullable `name` passed into the route.
  late final repeatProperty = field<int>(name: 'repeat').withFallback(() => 1); // If no `repeat` is given as a query parameter, it will default the value to `1`.
 
  @override
  PathDefinition get pathDefinition => PathDefinition.string('greet').property(nameProperty); // The path this handles is `/greet/[name]`.
 
  @override
  List<RouteProperty> get queryProperties => [repeatProperty]; // Consumes and casts `repeat` from the query parameters, such as `?repeat=3`.
 
  @override
  GreetRoute copy() {
    return GreetRoute();
  }
}

Fields

You can create fields in a Route by using a late field with the field helper. Define the type of the field in the generic and pass along the name of the field. The name is used to determine where to pull values from the query parameters.

The field value will be coerced from the path, so you can define int and double fields and get their type-coerced values. If the value from the path cannot be coerced to the specified type, an exception is thrown.

You can add modifiers to a field, such as .required(), to extend their functionality. Here are the modifiers currently available:

  • .required(): Makes the field non-nullable. If the field is null when the Route is created, it will throw an exception.
  • .withFallback(): If the field is null when the Route is created, it will use a fallback value instead.

PathDefinition

A PathDefinition defines what the path will look like and determines whether a given URL matches the path. It can include predefined strings or properties. For example:

You can use .string() to define a new string segment and .property() to define an arbitrary value that fills in a property.

For example:

PathDefinition.string('first').string('second') // matches `/first/second`
PathDefinition.string('user').property(userIdProperty) // matches `/user/[userId]` where `userIdProperty` is a field in the Route itself.
PathDefinition.string('user').property(userIdProperty).string('edit') // matches `/user/[userId]/edit` where `userIdProperty` is a field in the Route itself.

Query Properties

For optional properties you want to retrieve from a path, such as ?myProperty=hi, add the property to the queryProperties getter, and the Route will find all query properties from the path and fill them in.

class TestRoute with IsRoute<TestRoute> {
  late final myProperty = field<String>(name: 'myProperty');
 
  @override
  List<RouteProperty> get queryProperties => [myProperty];
 
  ...
}

Hidden Properties

If you want to instantiate a Route but provide it with more dynamic field values without inserting them into the path itself, you can add those properties to the hiddenProperties getter. The Route will fill them in accordingly.

class TestRoute with IsRoute<TestRoute> {
  late final passwordProperty = field<String>(name: 'password');
 
  @override
  List<RouteProperty> get hiddenProperties => [passwordProperty];
 
  ...
}
 
context.navigateTo(TestRoute()..passwordProperty.set('hunter2'));

Usage

Routes are used in the Flutter AppPondContext to define the URLs that can be navigated to. The AutomatePondContext uses AutomatePathDefinition, which is heavily inspired by the Path Module, to define the routes of the CLI commands that can be used. Eventually, the Path Module will be used to define the routes for backend APIs as well.