path_stack 0.2.0+1 icon indicating copy to clipboard operation
path_stack: ^0.2.0+1 copied to clipboard

A Stack that shows a single child route from a map of child routes. Similar to IndexedStack, but it uses Strings as keys and has a number of extra features like child nesting and path arguments.

path_stack #

A Stack that shows a single child from a list of children. Similar in concept to IndexedStack, but it uses Strings as keys and has a number of extra features like child nesting, custom animations and path arguments.

🔨 Installation #

dependencies:
  path_stack: ^0.2.0+1

⚙ Import #

import 'package:path_stack/path_stack.dart';

🕹ī¸ Usage #

To configure a PathStack provide it with a routing table by settings the .routes value. Declare the paths that you want to support and their corresponding views:

String currentPath = "page1";
...
return PathStack(
  path: currentPath,
  unknownPathBuilder: (_) => Custom404Page(),
  routes: {
      ["page1"]: (_) => Page1(),
      ["page2"]: (_) => Page2(),
    });
  }

A route can match multiple path values. For example this will match when the path value is / or /home:

   ["home", "/"]: (_) => Home(),

Relative paths #

path_stack also allows you to easily declare relative paths by wrapping one PathStack within another.

For example, you could define a simple nested structure like:

return PathStack(
  path: path,
  routes: {
    ["home"]: (_) => HomePage(),
    ["news"]: (_) => NewsPage(),
    ["settings/"]: (_) => PathStack(
          routes: {
            ["profile", ""]: (_) => ProfilePage(),
            ["notifications"]: (_) => NotificationsPage(),
          },
        ),
  },
);

Which results in the following potential routes:

  • /home
  • /news
  • /settings/profile
  • /settings/notifications

Scaffold Builder / Nesting #

To support the common use case of shared navigation or app chrome there is scaffoldBuilder which lets you wrap any Widget around the current PathStack child.

This means you can create a basic tab-scaffold like this:

    return PathStack(
      path: currentPath,
      scaffoldBuilder: (_, child) =>
         TabScaffold(
           child: child,
           onTabPressed: (value){
             setState(() => _currentPath = value)
         }})),
      routes: {
        ["/home"]: (_) => HomePage(),
        ["/settings"]: (_) => SettingsPage()
        ["/explore"]: (_) => ExplorePage(),
      },
    );
  }

Exact vs Partial Matches

Each route can be configured to accept exact or partial matches. For convenience, PathStack makes some assumptions based on the presence of a trailing slash in your paths:

Routes with no trailing slash in the are assumed to be an exact match:

  ["details"]: (_) => Details(), // matches only `/details`

Routes with trailing slash are assumed be a partial match:

  ["details/"]: (_) => Details(), // matches any of `/details/`, `/details/12`, `/details/id=12&foo=99` etc

If multiple route paths are defined, only the first entry in the path list is considered for this default behavior:

["details", "/"]: (_) => Details(), // matches only `/details` or '/' exactly

You can disable this default behavior by wrapping your view in a RouteConfig widget (shown below).

Using RouteConfig to set optional params #

To configure additional options on a route, you can wrap it in a RouteConfig widget:

  ["/admin"]: (_) => RouteConfig(
    maintainState: false, // Tell this route not to maintain state
    exactMatch: false,  // Tell this route it does not need an exact match (it can be matched as a prefix)
    onBeforeEnter: (newRoute) => return isLoggedIn == true, // Return false here to prevent the path from changing
    child: AdminPage())

Arguments #

You can pass arguments to your routes using either path based or query string args.

To use path based args you embed the name of the params in the route itself as: "details/:foo/:bar", then extract them from stack.args with the same names:

PathStack(
  path: "details/10/20",
  routes: {
    ["details/:foo/:bar"]: (stack) => DetailsPage(foo: stack.args["foo"], bar: stack.args["bar"]),
  }
)

With query string style, the parsing is the same, but the names are embedded in the path rather than the route:

PathStack(
  path: "/details?foo=10&bar=20",
  routes: {
    ["details/"]: (stack) => DetailsPage(foo: stack.args["foo"], bar: stack.args["bar"]),
  }
)

Full Example #

There are several other features not mentioned here, including custom route transitions, support for unknown paths (404) and a caseInsensitive setting.

For a full API tour, you can view the following code sample:

return PathStack(
    // Path is the source of truth for each stack, usually this is shared by all child stacks
    path: currentPath,
    // Optional: Provide custom widget for unknown paths
    unknownPathBuilder: (_) => Center(child: Text("Custom 404 Page")),
    // Optional: Provide custom animationIn (default is no animation)
    transitionBuilder: (_, stack, anim1) => FadeTransition(opacity: anim1, child: stack),
    // Optional: Provide duration for animationIn
    transitionDuration: Duration(milliseconds: 200),
    // By default pages are case insensitive, like a web server. But you can turn this off
    caseSensitive: true,
    // Define all matching routes for this stack
    routes: {
      ["home", "/"]: (_) => HomePage(),
      // Example of a route guard, you can return false to block the change
      ["admin"]: (_) => RouteConfig(
            child: AdminPage(),
            onBeforeEnter: (_){
                if (isLoggedIn) return true;
                scheduleMicrotask(() => setState(() => currentPath = "home"));
                return false;
            },
          ),
      // Adding a "/" at the end of any path indicates it can match as a prefix but you can override this
      ["settings/"]: (_) => PathStack(
            // Wrap all settings routes in a shared scaffold
            scaffoldBuilder: (_, stack) => Column(children: [
              Padding(padding: EdgeInsets.all(20), child: Text("Shared Settings App Bar")), // Settings Header
              Expanded(child: stack),
            ]),
            // Define child routes for settings section
            routes: {
              // By adding a "" alias, the first route will match both `/settings/alerts` and `/settings/`
              ["alerts", ""]: (_) => AlertsPage(),
              ["profile"]: (_) => ProfilePage(),
              ["billing/:foo/:bar"]: (stack) => BillingPage(id: "${stack.args["foo"]}_${stack.args["bar"]}"),
            },
          ),
    },
  );

🐞 Bugs/Requests #

If you encounter any problems please open an issue. If you feel the library is missing a feature, please raise a ticket on Github and we'll look into it. Pull request are welcome.

📃 License #

MIT License

7
likes
120
pub points
35%
popularity

Publisher

unverified uploader

A Stack that shows a single child route from a map of child routes. Similar to IndexedStack, but it uses Strings as keys and has a number of extra features like child nesting and path arguments.

Documentation

API reference

License

Icon for licenses.MIT (LICENSE)

Dependencies

flutter, path_to_regexp

More

Packages that depend on path_stack