path_stack 0.0.2+6 path_stack: ^0.0.2+6 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 route from a map of child routes. 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.0.2
â Import #
import 'package:path_stack/path_stack.dart';
đšī¸ Usage #
In it's simplest form, it is just a list of route names, and their associated Widgets:
return PathStack(path: path, routes: {
["page1"]: Container(color: Colors.red).buildStackRoute(),
["page2"]: Container(color: Colors.green).buildStackRoute(),
// Nesting allows you to type relative paths, and to also wrap sub-sections in their own menus/scaffold
["page3/"]: PathStack(
path: path,
basePath: "page3/",
routes: {
// Paths can have multiple entries, allowing aliases, here "" allows this route to match "page3/"
["subPage1", ""]: Container(color: Colors.orange).buildStackRoute(),
["subPage2"]: Container(color: Colors.purple).buildStackRoute(), //matches: /page3/subPage2
},
).buildStackRoute(),
});
}
If you're wondering .buildStackRoute()
is an extension that converts any widget into a StackRouteBuilder
. This is just to make the tree more readable. You can use this in cases where the view does not need to parse any path params. If you do need to parse params, you can use the full builder:
["subPage2/"]: StackRouteBuilder(builder: (_, args) => Container(child: Text(args["id"]))),
// This would show "99" for a path of: "/page3/?id=99"
Maintain State #
Like IndexedStack
, one of the core features of PathStack
is that all child routes are kept-alive. When building routes you can pass a maintainState
value, which will tell the stack whether you'd like the route to keep it's state or not. By default this is set to true.
What this means in practice, is that children in the stack will maintain their scroll positions, text entry, search filters, list sorting, animation positions and any other state that you have! If you'd like a route to not maintainState
, just set this value to false.
Scaffold Builder #
To support the common use case of shared navigation or app chrome, there is scaffoldBuilder
which lets you wrap any Widget around the stack. This means you can create a basic tab-scaffold like this:
class _SimpleTabExampleState extends State<SimpleTabExample> {
PageType _currentPage = PageType.Home;
@override
Widget build(BuildContext context) {
String currentPath = describeEnum(_currentPage);
return PathStack(
path: currentPath,
// Optionally you can define a custom animation when paths are changed
transitionBuilder: (_, stack, anim) => FadeTransition(opacity: anim),
// TabScaffold can be anything you want. Here it's just a column w/ 3 btns, and dispatches a pressed event
scaffoldBuilder: (_, stack) => TabScaffold(_currentPage, child: stack, onTabPressed: _handleTabPressed),
routes: {
["${describeEnum(PageType.Home)}"]: HomePage().buildStackRoute(),
["${describeEnum(PageType.Settings)}"]: SettingsPage().buildStackRoute(),
["${describeEnum(PageType.Explore)}"]: ExplorePage().buildStackRoute(maintainState: false),
},
);
}
// Change page type and rebuild the parent view
void _handleTabPressed(PageType value) => setState(() => _currentPage = value);
}
In the example above you can see some of the other core API's:
PathStack.transitionBuilder
- Allows you to provide a custom animation when path is changedPathStack.routes
- A list of available paths for this stack, can contain otherPathStack
s to nest paths and scaffoldsbuldStackRoute(bool maintainState)
- Set this to false if you do not want a path to remember it's State.
Defining paths #
path_stack
supports both path based args (/details/83
) or queryString args (/details?id=83
). Under the hood we use path_to_reg_exp
so you can look there for details on path parsing.
Path Parsing Rules:
- Paths with no trailing slash must an exact match:
- eg,
/details
matches only/details
not/details/12
or/details/?id=12
- eg,
- Paths with a trailing slash, will accept a suffix,
- eg,
/details/
matches any of/details/
,/details/12
,/details/id=12&foo=99
etc
- eg,
Path Params
- To accept path params you must name them
/details/:foo/:bar
, which will matchdetails/10/20
- This can be accessed in the
StackRouteBuilder.builder
usingargs["foo"]
andargs["bar"]
Query Params
- To accept a query parameter you can use a trailing slash
/details/
which will match any appended value - This can be accessed in the
StackRouteBuilder.builder
usingargs["paramName"]
Full Api Example #
For a full API tour, you can view the following code sample:
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().buildStackRoute(),
// Adding a "/" at the end of any path indicates it can match as a prefix
["/settings/"]: PathStack(
path: currentPath,
// Set a basePath that will be combined with all routes in this child stack
basePath: "/settings/",
// Use scaffoldBuilder to add a shared scaffold to all of this stacks child routes
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().buildStackRoute(),
["profile"]: ProfilePage().buildStackRoute(),
// When you need the args use the full builder. You can use path format, or query string format to pass args.
// Path Format needs to define how many arguments it wants, and their names.
["billing_alt/:foo/:bar"]:
StackRouteBuilder(builder: (BuildContext context, Map<String, String> args) {
return BillingPage(id: "${args["foo"]}_${args["bar"]}");
}),
// Query string format does not need to define arg names up front.
// This will catch billing/ + queryParams, and by adding a "billing" alias, we'll also catch links without the trailing slash
["billing/", "billing"]: StackRouteBuilder(builder: (BuildContext context, Map<String, String> args) {
return BillingPage(id: "${args["foo"]}_${args["bar"]}");
}),
},
).buildStackRoute(),
},
)
Complex Nesting Example #
For an example that shows complex nesting scenarios, including multiple scaffolds, full screen routes check out the advanced tab example: https://github.com/gskinnerTeam/flutter_path_stack/blob/master/example/lib/advanced_tab_example.dart
đ 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