riverpod_navigator 0.2.0 copy "riverpod_navigator: ^0.2.0" to clipboard
riverpod_navigator: ^0.2.0 copied to clipboard

outdated

Simple Flutter navigation with riverpod, freezed and Navigator 2.0.

Riverpod_Navigator #

  • Strictly typed navigation:
    You can use navigate([Home(), Books(), Book(id: bookId)]); instead of navigate('home/books/$bookId'); in your code.
  • Easier coding:
    The problem of navigation is reduced to manipulation of the immutable collection.
  • Better separation of concerns: UI x Model (thanks to riverpod 👍):
    Navigation logic can be developed and tested in the Dart environment, without typing a single flutter widget.
  • Small codebase with a lot of extensions:
    The core engine consists of two small .dart files (riverpod_navigator.dart and riverpod_navigator_dart.dart). Additional features (such as better URL parser, asynchronous navigation, possibility to use routes etc.) are included (and can be created) as configurable extensions.

Index #

Explanation on examples #

For a better understanding, everything is explained on the classic 3-screens example: [Home] => [Books] => [Book*]

Lesson01: simple example #

Example file is available here: lesson01.dart

1. Classes for typed "url path segments" (TypedSegment)

The Freezed package generates three immutable classes used for writing typed navigation path, e.g
TypedPath newPath = <TypedSegment>[HomeSegment (), BooksSegment (), BookSegment (id: 3)];

@freezed
class AppSegments with _$AppSegments, TypedSegment {
  AppSegments._();
  factory AppSegments.home() = HomeSegment;
  factory AppSegments.books() = BooksSegment;
  factory AppSegments.book({required int id}) = BookSegment;
  factory AppSegments.splash() = SplashSegment;

  factory AppSegments.fromJson(Map<String, dynamic> json) => _$AppSegmentsFromJson(json);
}

2. Dart part of app configuration

Tell the application how to convert TypedSegments from JSON.

final config4DartCreator = () => Config4Dart(initPath: [HomeSegment()], json2Segment: (json, _) => AppSegments.fromJson(json));

3. app specific navigator with navigation aware actions

... actions are then used in app widgets.

const booksLen = 5;

class AppNavigator extends RiverpodNavigator {
  AppNavigator(Ref ref, Config4Dart config) : super(ref, config);

  /// navigate to home page
  void toHome() => navigate([HomeSegment()]);
  /// navigate to books page
  void toBooks() => navigate([HomeSegment(), BooksSegment()]);
  /// navigate to book;id=3 page
  void toBook({required int id}) => navigate([HomeSegment(), BooksSegment(), BookSegment(id: id)]);
  /// cyclic book's navigation (Prev and Next buttons)
  void bookNextPrevButton({bool? isPrev}) {
    assert(getActualTypedPath().last is BookSegment);
    var id = (getActualTypedPath().last as BookSegment).id;
    if (isPrev == true)
      id = id == 0 ? booksLen - 1 : id - 1;
    else
      id = booksLen - 1 > id ? id + 1 : 0;
    toBook(id: id);
  }
}

4. providers

final appNavigatorProvider = 
  Provider<AppNavigator>((ref) => AppNavigator(ref, ref.watch(config4DartProvider)));

/// Provider for Flutter 2.0 RouterDelegate
final appRouterDelegateProvider =
    Provider<RiverpodRouterDelegate>((ref) => 
      RiverpodRouterDelegate(ref, ref.watch(configProvider), ref.watch(appNavigatorProvider)));

5. Flutter-part of app configuration

final configCreator = (Config4Dart config4Dart) => Config(
      /// Which widget will be builded for which [TypedSegment].
      /// Used in [RiverpodRouterDelegate] to build pages from [TypedSegment]'s
      screenBuilder: (segment) => (segment as AppSegments).map(
        home: (home) => HomeScreen(home),
        books: (books) => BooksScreen(books),
        book: (book) => BookScreen(book),
      ),
      config4Dart: config4Dart,
    );

6. root widget for app

Using functional_widget package to be less verbose. Package generates "class BooksExampleApp extends ConsumerWidget...", see *.g.dart. "functional_widget" is not a mandatory app dependency.

@cwidget
Widget booksExampleApp(WidgetRef ref) => MaterialApp.router(
      title: 'Books App',
      routerDelegate: ref.watch(appRouterDelegateProvider),
      routeInformationParser: RouteInformationParserImpl(ref.watch(config4DartProvider)),
    );

7. app entry point...

... with ProviderScope and ProviderScope.overrides

void main() {
  runApp(ProviderScope(
    // initialize providers with the configurations defined above
    overrides: [
      config4DartProvider.overrideWithValue(config4DartCreator()),
      configProvider.overrideWithValue(configCreator(config4DartCreator())),
    ],
    child: const BooksExampleApp(),
  ));
}

8. app screens

File with screen widgets is available here: screens.dart


Lesson02: example with Dart testing #

An example that allows flutter-independent testing.

to be done


Lesson03: asynchronous navigation #

See changes against Example01 (added part 1.1, parts 2. and 3. are modified).

Example file is available here: lesson03.dart.

1.1 async screen actions

Add new code for async screen actions.

AsyncScreenActions? segment2AsyncScreenActions(TypedSegment segment) {
  Future<String> simulateAsyncResult(String title, int msec) async {
    await Future.delayed(Duration(milliseconds: msec));
    return title;
  }

  return (segment as AppSegments).maybeMap(
    book: (_) => AsyncScreenActions<BookSegment>(
      // for every Book screen: creating takes some time
      creating: (newSegment) async => simulateAsyncResult('Book creating async result after 1 sec', 1000),
      // for every Book screen with odd id: changing to another Book screen takes some time
      merging: (_, newSegment) async => 
        newSegment.id.isOdd ? simulateAsyncResult('Book merging async result after 500 msec', 500) : null,
      // for every Book screen with even id: creating takes some time
      deactivating: (oldSegment) => 
        oldSegment.id.isEven ? Future.delayed(Duration(milliseconds: 500)) : null,
    ),
    home: (_) => AsyncScreenActions<HomeSegment>(
        // Home screen takes some timefor creating
        creating: (_) async => simulateAsyncResult('Home creating async result after 1 sec', 1000)),
    orElse: () => null,
  );
}}

2. Configure dart-part of app

Add segment2AsyncScreenActions to config

final config4DartCreator = () => Config4Dart(
      json2Segment: (json, _) => AppSegments.fromJson(json),
      initPath: [HomeSegment()],
      segment2AsyncScreenActions: segment2AsyncScreenActions,
    );

3. app-specific navigator with navigation aware actions

AppNavigator extends AsyncRiverpodNavigator instead of RiverpodNavigator

class AppNavigator extends AsyncRiverpodNavigator {
  ...

Lesson03.1: splash screen #

1. Classes for typed "url path segments" (TypedSegment)

Add SplashSegment definition

@freezed
class AppSegments with _$AppSegments, TypedSegment {
  AppSegments._();
  factory AppSegments.home() = HomeSegment;
  factory AppSegments.books() = BooksSegment;
  factory AppSegments.book({required int id}) = BookSegment;
  factory AppSegments.splash() = SplashSegment; // <========

  factory AppSegments.fromJson(Map<String, dynamic> json) => _$AppSegmentsFromJson(json);
}

2. Dart part of app configuration

Specify splashPath.

final config4DartCreator = () => Config4Dart(
      json2Segment: (json, _) => AppSegments.fromJson(json),
      segment2AsyncScreenActions: segment2AsyncScreenActions,
      initPath: [HomeSegment()],
      splashPath: [SplashSegment()], // <========
    );

5. Flutter-part of app configuration

Specify splash screen builder.

final configCreator = (Config4Dart config4Dart) => Config(
      /// Which widget will be builded for which [TypedSegment].
      /// Used in [RiverpodRouterDelegate] to build pages from [TypedSegment]'s
      screenBuilder: (segment) => (segment as AppSegments).map(
        home: (s) => HomeScreen(s),
        books: (s) => BooksScreen(s),
        book: (s) => BookScreen(s),
        splash: (s) => SplashScreen(s), // <========
      ),
      config4Dart: config4Dart,
    );

8. app screens

Add SplashScreen widget to screens.dart


Lesson04: app logic, more TypedSegment classes per app #

Simple Login navigation flow

to be done


Lesson05: using the Route concept #

to be done


Lesson06: splash screen #

to be done


Lesson07: waiting indicator, navigatorWidgetBuilder #

to be done


Lesson08: screenBuilder #

to be done

Install and run examples #

to be done

Roadmap #

  • block GUI screen
  • Nested routing
25
likes
0
pub points
57%
popularity

Publisher

unverified uploader

Simple Flutter navigation with riverpod, freezed and Navigator 2.0.

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter, freezed_annotation, hooks_riverpod, json_annotation, meta, riverpod, riverpod_navigator_dart, tuple

More

Packages that depend on riverpod_navigator