Skip to content

caseycrogers/implicit_navigator

Repository files navigation

A navigator that manages the page stack for you! Just build your widget tree and Implicit Navigator will handle the rest.

Core Features

The following features set Implicit Navigator apart from the official Flutter solution(s):

  1. The navigator stack is constructed and appended to implicitly as your app's data models and widget tree change.
  2. "App-style" and "browser-style" navigation are both supported out of the box:
    • App-Style - back button goes "up" in a developer-defined navigation hierarchy, potentially undoing multiple state changes
    • Browser-Style - back button reverts the last state change
  3. Nesting navigators in the widget tree has first class support with the system back button always popping from the inner most navigator first.

Additionally, ImplicitNavigator.fromValueNotifier and ImplicitNavigator.selectFromListenable provide out-of-the-box ways to attach a navigator to a ValueNotifier or Listenable, respectively.

Getting Started

First decide if you want to use app or browser style navigation. Below are samples for each style, but this is just a starting off point! You can set the navigation style based on the current platform or mix and match styles in different navigators within the same app.

App-Style Navigation

The following implements app-style navigation:

class _AppStyleState extends State<AppStyle> {
  int? _index;

  @override
  Widget build(BuildContext context) {
    return ImplicitNavigator<int?>(
      value: _index,
      depth: _index == null ? 0 : 1,
      builder: (context, index, animation, secondaryAnimation) {
        return TextButton(
          onPressed: () => setState(() {
            _index = (index ?? -1) + 1;
          }),
          onLongPress: () => ImplicitNavigator.of<int?>(context).pop(),
          child: Text((index ?? 'Tap To Increment').toString()),
        );
      },
      onPop: (poppedValue, valueAfterPop) => _index = valueAfterPop,
    );
  }
}

ImplicitNavigator takes an optional depth parameter which represents where the user currently is in the app's navigation flow. When the back button is pressed, the app state is returned to the last value of less depth. ie, the user moves "up" in the navigation flow.

Using the above example code, imagine a user navigates through the pages as follows:

depth_0:_index=null > depth_1:_index=0 > depth_1:_index=1

If the user then presses back, they will go up in the navigation flow to depth 0: depth_0:_index=null, not depth_1:_index=0.

If Implicit Navigators are nested within each other in the widget tree, you should build each inner navigator with MaintainHistory set to true and a distinct PageStorageKey. Implicit Navigator will then cache and restore the history stack using page storage so that, if a user navigates away from it and then navigates back, it'll retain it's history stack.

Browser-Style Navigation

The following implements browser-style navigation:

class _BrowserStyleState extends State<BrowserStyle> {
  int? _index;

  @override
  Widget build(BuildContext context) {
    return ImplicitNavigator<int?>(
      value: _index,
      builder: (context, index, animation, secondaryAnimation) {
        return TextButton(
          onPressed: () => setState(() {
            _index = (_index ?? -1) + 1;
          }),
          onLongPress: () => ImplicitNavigator.of<int?>(context).pop(),
          child: Text((index ?? 'Tap To Increment').toString()),
        );
      },
      onPop: (poppedValue, valueAfterPop) => _index = valueAfterPop,
    );
  }
}

For browser-style navigation, simply leave depth null and do not set MaintainHistory to true on any nested implicit navigators.

Repeating the example from above, but with browser style navigation:

depth_null:_index=null > depth_null:_index=0 > depth_null:_index=1

If the user then presses back, they will go back to the previous page/app state: depth_null:_index=0.

How It Works

Implicit Navigator is built on top of the Flutter Navigator 2.0 API. Implicit Navigators operate similar to ValueListenableBuilder: each one takes in a changing value and a builder. Whenever a new value and/or depth is supplied, a new page is added to the internal navigator's page stack. When pop is called (by the system or programmatically), the topmost page is popped and the builder is called with the new topmost value. An onPop callback should be used to revert any state outside of the navigator.

For convenience, ImplicitNavigator.fromValueNotifier and ImplicitNavigator.selectFromListenable wrap ValueNotifier and ValueListenable, respectively. They push to the navigator stack when their respective notifier or listenable changes.

When the system back button is called (or pop is called programmatically), ImplicitNavigator attempts to pop from the deepest active navigator in the tree, working it's way up to the root navigator until it finds an active navigator that can handle the pop. A navigator is active if it is in the topmost route of the root navigator and it has not been manually disabled via ImplicitNavigator.of(context).canPop = false.

ImplicitNavigatorBackButton is also provided as a convenience widget. Use it in your app bar's leading argument to display a back button that is visible whenever any Implicit Navigator in the widget tree can pop.

Limitations

  1. Implicit navigator's page history is updated using the flutter build cycle so if a value changes repeatedly between builds, the page history will not include the intermediate values. This is not fixable for the default constructor but I am looking into fixing this bug for fromNotifier and selectFromListenable.
  2. Implicit Navigator does not provide any out of the box tools for routing (eg parsing URLs pushed by the browser or handling deep links). This is intentional. Routing is highly complex and, in my opinion, well outside of the separation of concerns for a navigator package. To handle routing, use the router of your choice to parse incoming routes and rebuild the widget tree with the new app state according to the incoming routes. Implicit Navigator will see any relevant state changes and push the appropriate pages in response.

Contributing

I highly appreciate any support on this project! If you find an issue or have a feature request feel free to file a bug or fork the repo and submit a PR.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages