routing_path 0.0.1+3

  • Readme
  • Changelog
  • Example
  • Installing
  • 64

routing_path #

Build Status codecov

Description #

This package was designed to be a detached interface connecting multiple features without them depending on one another. This is particularly useful when dealing with an app with multiple feature packages, for example:

In an app structure where you have a main application at root level:

/
  pubspec.yaml # imports the packages in 'features'
  lib/ # main application
  test/
  features/
    feature_a/
    feature_b/
    ...

And you need a way for feature_a to start/open feature_b, you'd first need a common interface between them to make it work.

And this is the main objective of routing_path:

To be the common interface between features

Usage #

There are a few built-in classes and interfaces for convenience, but for this example we'll stick to the core ones to understand the bigger picture:

Registering a route #

First the features should create their own implementations of RouteHandler, which is nothing more than an entry point within that feature.

// A `RouteHandler` is the core interface, if you want to open a
// navigation Route for example, then you could use a `NavigationRouteHandler`.
class FeatureARouteHandler implements RouteHandler {
  @override
  bool canOpen(String path) => path == '/feature-a';

  @override
  Future<T> open<T>(String path, [RouteArguments arguments]) async {
    // do something

    // here you can use the arguments, that were given when
    // this route was opened. It is simply a class
    // wrapping a Map<String, dynamic>.
    // It is a class to allow custom `extensions` to be made if needed,
    // without being too broad, or too restrictive.
  }
}

Registering a Router #

Now that feature_a has a route, we need to register into the main application.

It is assumed that the main application knows (depends on) all features

So to register it, first we need to instantiate a Router, which is responsible for opening a route with a given string path.

import 'packages:feature_a/feature_a.dart';
import 'packages:feature_b/feature_b.dart';

// the mixin here is only to minimize a simple list based solution,
// it is entirely optional and customizable
class AppRouter extends Router with RouteRegistererMixin {
  AppRouter({Widget child}): super(child: child);

  @override
  final List<RouteHandler> routes = [
    // This is a simple list which will be checked when someone attempts
    // to open a route.
    //
    // Note that `Router` is also a `RouteHandler`, so there is a concept of
    // allowing nested `RouteHandlers`, you can even think of
    // them as nodes in a tree.
    FeatureARouteHandler(),
    FeatureBRouteHandler(),
  ];
}

Then just register it at some point in the application that will be reachable by the other features (i.e. closer to the main widget app).

Make sure the widget is reachable by BuildContext

AppRouter(
  child: MaterialApp(
    // ...
  ),
);

// or

MaterialApp(
  builder: (context, child) {
    // ...
    return AppRouter(child: child);
  },
);

It all depends on how your main application needs to be registered or customized.

Opening a route #

Then to open a route:

// somewhere inside a widget
Widget build(BuildContext context) {
  // (these lines could be inside a button tap callback for example)

  // This returns a Future which you can listen to, or just ignore.
  // For example, to show an loading indicator.
  Router.of(context).open('/feature-a');

  // Or if you want to also send some arguments
  Router.of(context).open('/feature-b', RouteArguments({'value': 123}));

  // ...
}

Special RouteHandlers #

  • NavigationRouteHandler: Is a RouteHandler that attempts to open a navigation Route<T>.

    class MyNavigationRoute extends NavigationRouteHandler {
      @override
      bool canOpen(String path) => path == '/feature-a';
    
      @override
      // Notice that the returning `Route` can have a definitive type if desired.
      // Otherwise you can maintain it as a `Route` of dynamic type.
      Route<void> buildRoute(String path, [RouteArguments arguments]) {
        return MaterialPageRoute<void>(
          builder: (context) {
            return MyFeatureAScreen();
          }
        );
      }
    }
    
  • PathRouteHandler: Is a RouteHandler that has a RegExp path matcher, able to replace the variables as needed.

    class MyPathRoute extends PathRouteHandler {
      MyPathRoute() : super('/path/to/:id');
    
      // we don't need to implement `canOpen`, it is already being done on
      // `PathRouteHandler`. In this case, it'll match anything similar to
      // `/path/to/:id`, where `:id` is a path variable
    
      @override
      Future<T> open<T>(String path, [RouteArguments arguments]) {
        // here we're updating  the arguments variable with the path variables
        // (if any).
        // If there were any values previously stored in arguments,
        // they are still accessible, unless the name conflicts with the path
        // variable (which will be overridden).
        arguments = replaceMatches(path, arguments);
    
        // do something ...
      }
    }
    
  • Mixed RouteHandlers: It is possible to mix the behavior from both NavigationRouteHandler and PathRouteHandler, by their mixins and/or interfaces.

    // Here we're using the `PathRouteHandler` interface
    // with the `NavigationRouteHandlerMixin` to extend its functionality.
    class MixedPathRoute extends PathRouteHandler with NavigationRouteHandlerMixin {
      // the path matching behaves just like a `PathRouteHandler`
      MixedPathRoute() : super('/path/to/:id');
    
      @override
      // and it is expected to return a navigation `Route`
      Route buildRoute(String path, [RouteArguments arguments]) {
        arguments = replaceMatches(path, arguments);
    
        // do something ...
      }
    }
    

[0.0.1+3] #

  • Removes warnings for flutter stable 1.17

[0.0.1+2] #

  • Adds example app

[0.0.1+1] #

  • Splits NavigationRouteHandler with a mixin
  • Splits PathRouteHandler with a mixin
  • Removes UnmatchedPathException

[0.0.1] #

  • Adds core implementation
  • Adds regexp paths
  • Adds README

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:routing_path/routing_path.dart';

import 'feature_a/route.dart';
import 'feature_b/route.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'routing_path demo',
      // The default key for the root (or main) navigator that you want all the
      // `NavigationRouteHandler`'s routes to start from.
      navigatorKey: NavigationRouteHandler.rootNavigatorKey,
      builder: (context, child) {
        // it could be declared here in `builder`, or it could wrap the whole
        // `MaterialApp` widget. It must be accessible for all descendant
        // widgets on the widget tree (`BuildContext`)
        return MainAppRouter(child: child);
      },
      home: MainHomePage(),
    );
  }
}

/// This is the main `Router` instance that will be inserted into the
/// widget tree. It is responsible for registering and opening [RouteHandler]s
/// when a `Router.of(context).open(...)` is called.
class MainAppRouter extends Router with RouteRegistererMixin {
  MainAppRouter({Widget child}) : super(child: child);

  @override
  final List<RouteHandler> routes = [
    // Ideally, these feature routes are completely isolated in a separate
    // directory or even package.
    FeatureARoute(),
    FeatureBRoute(),
  ];
}

class MainHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('HOME'),
      ),
      body: Container(
        alignment: Alignment.center,
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'This is a simple recursive example of a main application '
              'registering two separate features (A and B) and allowing '
              'them to be opened from anywhere using the `Route` interface.'
              '\n\n'
              'Tap a button to open a feature flow (they are visually '
              'identical)',
              textAlign: TextAlign.justify,
              style: theme.textTheme.bodyText2,
            ),
            const SizedBox(height: 24),
            OutlineButton(
              textColor: Colors.deepPurpleAccent,
              onPressed: () => Router.of(context).open(
                '/feature-a',
                RouteArguments({'opened_by': 'MAIN HOME'}),
              ),
              child: const Text(
                'OPEN FEATURE A',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
            ),
            OutlineButton(
              textColor: Colors.indigo,
              onPressed: () => Router.of(context).open(
                '/feature-b',
                RouteArguments({'opened_by': 'MAIN HOME'}),
              ),
              child: const Text(
                'OPEN FEATURE B',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  routing_path: ^0.0.1+3

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:routing_path/routing_path.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
41
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
70
Overall:
Weighted score of the above. [more]
64
Learn more about scoring.

We analyzed this package on Jul 8, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • routing_path that is a package requiring null.

Maintenance suggestions

The package description is too short. (-20 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.6.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.1
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8
Dev dependencies
flutter_test
matcher ^0.12.0
mockito ^4.1.0