Elegant abstraction for complete deep linking navigation in Flutter

Overview

Flutter Deep Link Navigation

pub.dev package Github stars Open source license Awesome Flutter

Provides an elegant abstraction for complete deep linking navigation in Flutter.

This package only provides deep linking for internal navigation. Any external platform-level deep linking solution can optionally be used in conjuction with this package.

The target audience of the documentation is experienced Flutter developers.

Motivation

There's nothing wrong with not using deep links for internal navigation.

Partially implementing deep links would either have limited benefits or be extremely complicated (not to mention confusing).

Hence why if you decide to use deep links, it makes sense to exclusively use deep links.

If you try to implement complete deep linking yourself here's some of the issues you'll run into:

  • Widgets become coupled with their place in the navigation hierarchy
    • Becomes an issue if you want to reuse a page
    • No amount of fancy iteration or recursion will help
  • It's difficult to 'pass down' deep link values used in higher levels
    • Given the hierarchy is Artist --> Song
    • Being able to do (artist) => ArtistPage(artist) and later (song) => SongPage(artist, song)
    • I actually published a rest-like router package while trying to solve this issue
  • How do you represent deep links internally?
    • How to keep configuration as terse as possible?
    • How to represent current route in a string-friendly format (eg. for analytics, debugging)
    • How to serialize route to be used with a platform-level deep linking solution
    • How to handle native navigator pops (eg. back button)?
    • How to handle a route that doesn't exist (or any other exception that occurs during dispatch)?
  • How do you integrate custom logic for validating deep link navigation?
    • Ideally provide context to be able to access state management
    • eg. certain subtrees of the navigation hierarchy are available only for subscribed or authenticated users

TL;DR

I separated the navigation system from Diet Driven (shameless plug, please hire me) into its own package and published it.

This package provides a solution for all the aforementioned difficulties.

Examples

Single base route

This example demonstrates:

  • Dispatchers with path-only deep links
  • Dispatchers with value deep links (ArtistDL, SongDL)
  • Exception handling (RouteNotFoundDL)
  • Cross-branch navigation (from favorite's song page to artist page) Navigation diagram for multiple base routes example

Multiple base routes

This example demonstrates:

  • Everything from single base route example
  • Bottom navigation (library, favorites, user pages) persists across navigation
  • Login and error pages are full screen (hide bottom navigation)
  • Using the asynchronous result of push (AuthenticationDL from UserDL)
  • Custom Authenticated mixin ensures user is authenticated (LibraryDL, FavoritesDL, UserDL) Navigation diagram for multiple base routes example

Configuration

Deep links

DeepLink is base unit of routes, a deep link is mapped to a Widget by a Dispatcher.

Deep links may be reused in different levels of the navigation hierarchy.

A route is the full location of a page, represented by List<DeepLink>.

path is the string representation of the route aka route.join("/").

Path

class LibraryDL extends DeepLink {
    LibraryDL() : super("library");
}

Value

Deep links can also store data, value dispatchers are strongly typed.

class SongDL extends ValueDeepLink<Song> {
    SongDL(Song song) : super("song", song);
}

class SongDL extends ValueDeepLink<Song> {
    // Override toString
    SongDL(Song song) : super("song", song, toString: (song) => song.id);
}

Mixin for sake of inheritence

This could also be achieved by implementing an abstract class.

See use in Child builder section.

mixin FullScreen on DeepLink {}

class LoginDL extends DeepLink with FullScreen {
    LoginDL() : super("login");
}

Mixin with logic

mixin Authenticated on DeepLink {
    @override
    void onDispatch(BuildContext context) {
        // Get state from context or global/static variable
        final isAuthenticated = Provider.of<AuthenticationService>(context, listen: false).authenticated;

        // Throw custom exception
        if (!isAuthenticated) {
            throw Unauthenticated();
        }
    }
}

// ...

navigation: (context) => Dispatcher()
    // Unauthenticated login page
    ..exception<Unauthenticated>((exception, route) => [LoginDL()])
    ..path<LoginDL>((route) => LoginPage()),

Application

Use DeepLinkMaterialApp instead of using Flutter's MaterialApp.

This replaces native navigation options with their deep link counterparts.

At most one deep link of a type can exist on a dispatcher.

Deep link material app

DeepLinkMaterialApp(
    navigation: (context) => Dispatcher() // see next section ...
    defaultRoute: [LibraryDL()], // if ommited, the splash screen is shown until explicit navigation
    splashScreen: SplashPage(),
    childBuilder: // see child builder section ...

    // Non-navigation related fields are still available
    themeMode: ThemeMode.light,
    // ...
);

Path dispatcher

..path<LoginDL>((route) => LoginPage()),

Value dispatcher

..value<Song, SongDL>((song, route) => SongPage(song: song)),

Sub navigation

..path<LibraryDL>(
    (route) => LibraryPage(),
    subNavigation: Dispatcher() // ...
),

..value<Song, SongDL>(
    (song, route) => SongPage(song: song),
    subNavigation: (song) => Dispatcher() // song may be used from this point onward
),

Exception mapping

Exceptions that are thrown while running through the navigation hierarchy are mapped to routes.

..exception<RouteNotFound> MUST be defined on the base-level dispatcher.

If multiple mappings of the same type are found thoughout the hierarchy, the deep-most mapping is used.

..exception<RouteNotFound>((exception, route) => [ErrorDL<RouteNotFound>(exception)])

Child builder

The widget specified in childBuilder is rebuilt when the route in deepLinkNavigator changes.

/// [DeepLink]s associated with the bottom navigation.
final bottomNavigationDeepLinks = [LibraryDL(), FavoritesDL(), UserDL()];

/// Current index of bottom navigation based on [currentRoute].
int currentIndex(List<DeepLink> currentRoute) {
    final index = bottomNavigationDeepLinks.indexOf(currentRoute?.first);
    return index != -1 ? index : 0;
}

/// ...

childBuilder: (BuildContext context, DeepLinkNavigator deepLinkNavigator, Widget child) => Scaffold(
    body: child,
    // Don't show bottom navigation while [currentRoute] is null, or any deep list is [FullScreen]
    bottomNavigationBar: deepLinkNavigator.currentRoute?.any((dl) => dl is FullScreen) ?? true ? null : BottomNavigationBar(
        currentIndex: currentIndex(deepLinkNavigator.currentRoute),
        onTap: (int index) => deepLinkNavigator.navigateTo([bottomNavigationDeepLinks[index]]),
        items: [
            BottomNavigationBarItem(title: Text("Library"), icon: Icon(Icons.queue_music)),
            BottomNavigationBarItem(title: Text("Favorites"), icon: Icon(Icons.favorite)),
            BottomNavigationBarItem(title: Text("User"), icon: Icon(Icons.person)),
        ],
    ),
)

In-app navigation

DeepLinkNavigator mirrors Navigator's interface as much as possible (including push and pop futures).

All methods internally orchestrate a native flutter navigator.

Push a deep link

await DeepLinkNavigator.of(context).push(ArtistDL(...));

Pop a value

DeepLinkNavigator.of(context).pop(...);

// or

Navigator.of(context).pop(...);

Navigate to specific route

DeepLinkNavigator.of(context).navigateTo([
    LibraryDL(),
    ArtistDL(...),
]);

Return to default route (if any)

DeepLinkNavigator.of(context).replaceWithDefault();

TODO: Throw exception to be caught by mapper

// TODO DeepLinkNavigator.of(context).throw(Exception(...));

TODO: Access deep link navigator from anywhere

// TODO DeepLinkNavigator()...

TODO: Page transitions

// await DeepLinkNavigator.of(context).push(
//   ArtistDL(...),
//   transition: ...,
//   duration: ...,
// );

// Possibly:
// await DeepLinkNavigator.of(context).fadeIn(...);

Platform deep links

// TODO: serialize List<DeepLink>

Limitations

  • Must FULLY specify ALL generic types since this is how deep links are matched internally
    • Please look very carefully when debugging
    • Fails by throwing RouteNotFound if the route doesn't exist
    • Fails by entering infinite recursion if RouteNotFound maps to a route that doesn't exist
  • Can't currently define arbitrarily deep navigation hierarchies (think Spotify)
  • Can't store separate persisted navigation states for a multi-base route application (think Instagram)

What's left to do

[ ] Custom/predefined page transitions

[ ] Access deep link navigator from anywhere using static method and factory pattern

[ ] Assert RouteNotFound dispatcher exists by running through navigation tree

[ ] Unit test deep link navigator logic

[ ] Cupertino and Widget apps

[ ] Explicit exception throwing

[ ] Platform deep links example + documentation

[ ] Route changed callback for analytics, etc

You might also like...

Flutter custom BottomBar Navigation Widget

Flutter custom BottomBar Navigation Widget

bottom_bar_with_sheet 💥 Non-standard way to use more space of screens in your application 💥 😳 Custom bottom Sheet under Bottom Navigation Bar 😳 😩

Dec 23, 2022

Transparent Android system navigation bar with Flutter and FlexColorScheme package.

Transparent Android system navigation bar with Flutter and FlexColorScheme package.

Sysnavbar with FlexColorScheme Transparent Android system navigation bar with Flutter and FlexColorScheme. FlexColorScheme V4 Notice If you are using

Oct 21, 2022

A Flutter package for easy implementation of curved navigation bar

A Flutter package for easy implementation of curved navigation bar

curved_navigation_bar pub package A Flutter package for easy implementation of curved navigation bar. Add dependency dependencies: curved_navigation

Dec 9, 2022

Simple but powerfull Flutter navigation with riverpod and Navigator 2.0

Simple but powerfull Flutter navigation with riverpod and Navigator 2.0

Riverpod navigation If you are interested in the motivation why the package was created and a detailed description of what problems it solves, read th

Dec 13, 2022

Easy-to-use Navigator 2.0 router for web, mobile and desktop. URL-based routing, simple navigation of tabs and nested routes.

Easy-to-use Navigator 2.0 router for web, mobile and desktop. URL-based routing, simple navigation of tabs and nested routes.

Routemaster Hello! Routemaster is an easy-to-use router for Flutter, which wraps over Navigator 2.0... and has a silly name. Features Simple declarati

Jan 3, 2023

Persistent Bottom Navigation Bar

Persistent Bottom Navigation Bar

Persistent Bottom Navigation Bar A persistent/static bottom navigation bar for Flutter. NOTE: Those migrating from pre 2.0.0 version should check the

Dec 20, 2022

A bottom navigation bar that you can customize with the options you need, without any limits.

A bottom navigation bar that you can customize with the options you need, without any limits.

Bottom Personalized Dot Bar A bottom navigation bar that you can customize with the options you need, without any limits. You can also customize the a

Oct 20, 2022

A Custom Extended Scaffold with Expandable and Floating Navigation Bar

A Custom Extended Scaffold with Expandable and Floating Navigation Bar

Custom Extended Scaffold Custom Flutter widgets that makes Bottom Navigation Floating and can be expanded with much cleaner a

Dec 10, 2022

Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

Fluro is a Flutter routing library that adds flexible routing options like wildcards, named parameters and clear route definitions.

English | Português The brightest, hippest, coolest router for Flutter. Features Simple route navigation Function handlers (map to a function instead

Jan 4, 2023
Comments
  • redo configuration syntax

    redo configuration syntax

    • Increment library version to 1.0.1
    • Completely overhauled configuration syntax
    • Simplified deep link processing logic
    • Generalized exception handling by mapping exceptions to routes
    • Remove unnecessary exception dispatchers
    • Updated examples with new configuration syntax
    • Fixed examples' deep link typing
    • Checked in multiple_base_route example configuration files
    opened by Dennis-Krasnov 0
  • Navigation animations

    Navigation animations

    Hello Dennis,

    Is there an easy way to work around the temporary limitation of your library regarding route transitions ?

    Do you you have any news about the progress of implementation of the route animations ?

    Thank you very much

    Best regards

    opened by adrianaxente 3
  • How can I create a deep link list from external url

    How can I create a deep link list from external url

    Hello Dennis

    I am try to integrate your library with external deep links (string url style). I there an easy generic way to map an string url to a list of deep links ?

    Thank you very much an keep up your good work on this package.

    opened by adrianaxente 0
Flutter Flows made easy! A Flutter package which simplifies navigation flows with a flexible, declarative API.

Flutter Flows made easy! Usage Define a Flow State The flow state will be the state which drives the flow. Each time this state changes, a new navigat

Felix Angelov 337 Dec 31, 2022
A simple and easy to learn declarative navigation framework for Flutter, based on Navigator 2.0.

A simple and easy to learn declarative navigation framework for Flutter, based on Navigator 2.0 (Router). If you love Flutter, you would love declarat

Zeno Nine 20 Jun 28, 2022
A Flutter implementation of a customizable navigation bar with animations.

A heavily customizable bottom navigation bar with some animation modifiers.

null 1 Jun 17, 2022
flutter bottom navigation bat project

bottom_navigation A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you starte

Danushan Ravendran 3 Sep 23, 2021
Fast code and awesome design-ui for flutter navigation bar

Flutter-awesome-bottom-navigation-bar ??‍?? Fast code and awesome design-ui for flutter navigation bar ?? Getting Started # First you need to add flas

Hmida 20 Nov 22, 2022
Custom Bottom navigation bar on Flutter.

Intro Custom Bottom navigation bar on Flutter. The updated one Support : Null safety & Support 9 items on Tabs & Some Color, Size, Effects and font cu

Ihab Zaidi 2 Oct 8, 2021
A flutter navigation package

Create By Me(Agalaba Ifeanyi Precious) go Navigate Navigate Like a pro from one Screen to another Using go navigate. go_Navigate provide you the abili

Agalaba Ifeanyi Precious 2 Oct 11, 2021
Flutter Material Design Navigation Drawer Menu

navigation_drawer_menu Flutter Material Design Navigation Drawer Menu Navigation drawer is a common UI pattern for adaptive menus. The Material Design

Christian Findlay 9 Dec 12, 2022
A Flutter package for easily implementing Material Design navigation transitions.

Morpheus A Flutter package for easily implementing Material Design navigation transitions. Examples Parent-child transition You can use MorpheusPageRo

Sander R. D. Larsen 186 Jan 7, 2023
Customized 🚀 Bottom Navigation Bar Using Flutter 🐦

Customized ?? Bottom Navigation Bar Using Flutter ??

AmirHossein Bayat 3 Dec 7, 2022