riverpod_navigator 0.16.0
riverpod_navigator: ^0.16.0 copied to clipboard
Simple but powerfull Flutter navigation with riverpod, freezed and Navigator 2.0.
Navigator for Riverpod #
Simple but powerfull navigation library (based on Flutter Navigator 2.0, Riverpod, and Freezed) that solves the following problems: #
- Strictly typed navigation:
You can usenavigate([Home(), Books(), Book(id: bookId)]);
instead ofnavigate('home/books/$bookId');
in your code. - Easier coding:
The problem of navigation is reduced to manipulation an immutable collection. - Better separation of concerns: UI x Model (thanks to riverpod 👍):
Navigation logic can be developed and tested without typing a single flutter widget. - Asynchronous navigation:
Before starting navigation, prepare all necessary asynchronous operations, e.g.- loading data for new screen
- save data from the previous screen
- Dependence on external providers:
The navigation state may also depend on external providers, e.g. on login status - Possibility to configure many navigation parameters
The mission #
Take a look at the following terms related to url path home/books/book;id=2
- string-path:
final stringPath = 'home/books/book;id=2';
- string-segment - the string-path consists of three string-segments: 'home', 'books' and 'book;id=2'
- typed-segment - the typed-segment is immutable class that defines string-segment: HomeSegment(), BooksSegment() and BookSegment(id:2)
- typed-path: typed-path can be understood as List
- navigation-stack of Flutter Navigator 2.0 is a stack of screens, parameterized by typed-segment:
HomeScreen(HomeSegment())) => BooksScreen(BooksSegment()) => BookScreen(BookSegment(id:3))
The mission of navigation is to keep string-path <= typed-path => navigation-stack always in sync. With the typed-path as the source of the truth.
Note: There is a one-to-one relationship between the given segment and the screen (HomeSegment - HomeScreen, BookSegment - BookScreen). In the following text, I sometimes confuse the two terms..
Installation #
After clonning repository, go to examples\doc
subdirectory and execute:
flutter create .
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
Simple example #
Step1 - imutable classes for typed-segment #
We use freezed-package for generation immutable clasess (that defines typed-segment's).
It's a good idea to be familiar with the freezed-package (including support for JSON serialization).
From the following SegmentGrp class declaration, the freezed package generates two classes: HomeSegment and PageSegment.
@freezed
class SegmentGrp with _$SegmentGrp, TypedSegment {
SegmentGrp._();
factory SegmentGrp.home() = HomeSegment;
factory SegmentGrp.page({required String title}) = PageSegment;
factory SegmentGrp.fromJson(Map<String, dynamic> json) => _$SimpleSegmentFromJson(json);
}
Step2 - navigator parameterization #
Extends the RiverpodNavigator class as follows:
class AppNavigator extends RiverpodNavigator {
AppNavigator(Ref ref)
: super(
ref,
// which screen to run when the application starts
[HomeSegment()],
[
// JSON serialization of HomeSegment and PageSegment
RRoutes<SegmentGrp>(SegmentGrp.fromJson, [
// build a screen from segment
RRoute<HomeSegment>(HomeScreen.new),
RRoute<PageSegment>(PageScreen.new),
])
],
);
}
Step3 - use the RiverpodNavigator in MaterialApp.router #
If you are familiar with the Flutter Navigator 2.0 and the riverpod, the following code is clear:
class App extends ConsumerWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
// for all widgets with riverpod support, the navigator is available via riverpodNavigatorProvider
final navigator = ref.read(riverpodNavigatorProvider);
return MaterialApp.router(
title: 'Riverpod Navigator Example',
routerDelegate: navigator.routerDelegate,
routeInformationParser: navigator.routeInformationParser,
);
}
}
Step4 - runApp #
void main() => runApp(
ProviderScope(
overrides: [
riverpodNavigatorCreatorProvider.overrideWithValue(AppNavigator.new),
],
child: const App(),
),
);
Step5 - widgets for screens #
Creating screen widgets is probably an understandable part of the example.
Only the navigation to the new screen is interesting:
// create navigation stack [HomeScreen(HomeSegment()), PageScreen(PageSegment(title: 'Page title'))]
ref.read(riverpodNavigatorProvider).navigate([HomeSegment(), PageSegment(title: 'Page')]);
or
// create navigation stack [HomeScreen(HomeSegment())]
ref.read(riverpodNavigatorProvider).navigate([HomeSegment()]);
Code of the example
The full code is available here: simple.dart.
See Other features for other doc and samples #
See What's under the hood for riverpod_navigation principle #
Comparison with go_router #
This chapter is inspired by this riverpod issue: Examples of go_router using riverpod.
example | go_router | code lines | riverpod_navigator | code lines |
---|---|---|---|---|
main | source code | 70 | source code | 84 |
redirection | source code | 167 | source code | 149 |
If you are interested in preparing another go_router example, I will try to do it.
Roadmap #
I prepared this package for my new project. Its further development depends on whether it will be used by the community.
- proofreading because my English is not good. Community help is warmly welcomed.
- testing on mobile (tested so far for windows desktop and web)
- nested navigation flow
I think everything is ready, nested ProviderScope can solve nested navigation too. - BlockGUI widget (block the GUI while asynchronous navigation is waiting to complete)
- parameterization alowing cupertino