nuvigator 0.2.2

  • Readme
  • Changelog
  • Example
  • Installing
  • 89

Nuvigator #

CircleCI Pub

Routing and Navigation package.

What #

This package aims to provide a powerful routing abstraction over Flutter Navigators. Using a most declarative and concise approach you will be able to model complex navigation flows without needing to worry about several tricky behaviours that the framework will handle for you.

Below you will find a description of the main components of this frameworks, and how you can use they in your project.

Main Concepts #

Basic usage

@NuRouter()
class MyRouter extends BaseRouter {

  @NuRoute()
  ScreenRoute myRoute() => ScreenRoute(
    builder: (context) => MyScreen(screenContext),
  );

  @override
  Map<RouteDef, ScreenRouteBuilder> get screensMap  => _$myScreensMap(this);
}

// Wraps the top most Router into a special Router to be able to handle with DeepLinks and system behaviors. This is a
// must. Be sure to wrap your top most router into a GlobalRouter.
final router = GlobalRouter(baseRouter: MyRouter());

Widget build(BuildContext context) {
  return MaterialApp(
    builder: Nuvigator(
      router: router,
      screenType: materialScreenType,
      initialRoute: MyRoutes.myRoute,
    ), 
  );
}

Nuvigator #

Nuvigator is a custom Navigator, it behaves just like a normal Navigator, but with several custom improvements and features. It is engineered specially to work under nested scenarios, where you can have several Flows one inside another, it will event ensure that your Hero transitions work well under those scenarios. Nuvigator will be your main interface to interact with navigation, and it can be easily fetched from the context, just like a normal Navigator, ie: Nuvigator.of(context).

Nuvigator includes several extra methods that can be used, for example the Nuvigator.of(context).openDeepLink() methods that will try to open the desired deepLink. Or event the Nuvigator.of(context).closeFlow() that will try to close a nested Nuvigator.

Each Nuvigator should have a Router. The Router acts just like the routing controller. While the Nuvigator will be responsible for visualization, widget render and state keeping. The Router will be Pure class that will be responsible to provide elements to be presented and managed by the Nuvigator.

ScreenRoute and FlowRoute #

Envelopes a widget that is going to be presented as a Screen by a Nuvigator. The Screen contains a ScreenBuilder function, and some attributes, like screenType, that are going to be used to generate a route properly.

When creating a ScreenRoute it can receive a optional generic type that will be considered as the type of the value to be returned by this Route when popped.

Screen Options:

  • builder
  • screenType
  • wrapper

FlowRoute is just a subclass of the ScreenRoute that may receive a Nuvigator instead of a builder function. Using the FlowRoute will allow Nuvigator to properly create all nested Nuvigator routing methods correctly.

When using FlowRoute you SHOULD declare the generic type of the Router that is being used by it's Nuvigator. This is required so the static analysis is able to properly generate the code when running the builder.

Creating Routers #

Defining a new Router will probably be the what you will do the most when working with Nuvigator, so understanding how to do it properly is important. A Router class is a class that should extend a BaseRouter and annotate the class with the @NuRouter annotation.

Defining Routes #

Inside your MyCustomRouter you can define your routes, each route will be represented by a method annotated with the @NuRoute annotation. Those methods should return a ScreenRoute or a FlowRoute, and can receive any number of named arguments, those will be used as the Arguments that should/can be passed to your route when navigating to it.

Example:

@NuRouter()
class MyCustomRouter extends BaseRouter {
  
  @NuRoute()
  ScreenRoute firstScreen({String argumentHere}) => ScreenRoute(
    builder: (context) => MyFirstScreen(context),
  ); 
  
  @override
  Map<RouteDef, ScreenRouteBuilder> get screensMap  => _$myCustomScreensMap(this); // Will be created by generated code

}

The NuRoute annotation may receive some options, in special, the deepLink option is used to declare a deepLink that will be used to access this route. The usage is the same when using a FlowRoute, the only changes will be in the generated code, that will take in account the nested nuvigator.

DeepLinks support both pathParameters (using URL templates) and queryParameters. Those parameters will be passed to your Route as arguments as Strings, so keep that in mind when declaring the Route arguments.

Example:

@NuRoute(deepLink: 'myRoute/:argumentName')

Obs: DeepLinks contained in routers that are used in nested Nuvigators (inside a FlowRoute) will NOT be considered and are not reachable. If you want to create a deepLink to a flow, be sure to add it to the @NuRoute annotation in the Router that declares the FlowRoute.

Grouped Routers #

In addition to declaring Routes in your Router, you can also declare child Routers. Those routers will have it's Routes presented as being part of the father Router. We may also refer this pattern as Router Grouping or Grouped Routers. This allows for splitting a huge router that potentially could have many Routes, into smaller autonomous Routers that can be imported and grouped together in the main Router.

To declare child routers you just need to add a new field with desired router type, and annotate it with the @NuRouter annotation.

Example:

@NuRouter()
class MyCustomRouter extends BaseRouter {
  
  @NuRouter()
  MyOtherRouter myOtherRouter = MyOtherRouter();

  @override
  List<Router> get routers => _$myCustomRoutersList(this); // Will be created by the generated code
  @override
  Map<RouteDef, ScreenRouteBuilder> get screensMap  => _$myCustomScreensMap(this); // Will be created by generated code
}

BaseRouter Options #

It is a Router interface implementation with sensible defaults that should be used most of the times. BaseRoute includes some options that can be used to enhance the navigation experience.

When extending from the BaseRouter you can override the following properties to add custom behaviors to your routes:

  • deepLinkPrefix: A String, that will be used as prefix for the deepLinks declared on each route, and also on the grouped routers.

  • screensWrapper: A function to wrap each route presented by this router. Should return a new Widget that wraps this child Widget. The Wrapper will be applied to all Screens in this Router. (this function runs one time for each screen, and not one time for the entire Router).

Code Generators #

You probably noted in the examples above that we have methods that will be created by the Nuvigator generator. So while they don't exists you can just make they return null or leave un-implemented.

Before running the generator we recommend being sure that each Router is in a separated file, and also make sure that you have added the part 'my_custom_router.g.dart'; directive in your router file.

After running the generator (flutter pub run build_runner build --delete-conflicting-outputs), you will notice that each router file will have it's part of file created. Now you can complete the screensMap and routersList functions with the generated: _$myScreensMap(this); and _$samplesRoutersList(this);. Generated code will usually follow this pattern of stripping out the Router part of your Router class and using the rest of the name for generated code.

Generated code includes the following features:

  • Routes "enum" class
  • Typed Arguments classes
  • Typed ScreenInterface classes
  • Navigation class
  • Implementation Methods

Routes Class #

The Routes Class is a "enum" like class, that contains mapping to the generated route names for your router, eg:

class SamplesRoutes {
  static const home = 'samples/home';

  static const second = 'samples/second';
}

Typed Argument Classes #

Those are classes representing the arguments each route can receive. They include parse methods to extract itself from the BuildContext. eg:

class SecondArgs {
  SecondArgs({@required this.testId});

  final String testId;

  static SecondArgs parse(Map<String, Object> args) {
    ...
  }

  static SecondArgs of(BuildContext context) {
    ...
  }
}

Typed ScreenInterfaces Classes #

It's a extension of StatelessWidget that can be extended by your Screen Widget. This class is make to provide easy access to the current Nuvigator instance, and also to the Route arguments. eg:

abstract class SecondScreen extends ScreenWidget {
  SecondScreen(BuildContext context) : super(context);

  SecondArgs get args => SecondArgs.of(context);
  SamplesNavigation get samplesNavigation => SamplesNavigation.of(context);
}

class MySecondScreenWidget extends SecondScreen {
  MySecondScreenWidget(BuildContext context): super(context);

  Widget build(BuildContext context) {
    print(args.testId);
    // nuvigator.pop();
    ...
  }
}

It's a helper class that contains a bunch of typed navigation methods to navigate through your App. Each declared @NuRoute will have several methods created, each for one of the existing push methods. You can also configure which methods you want to generate using the @NuRoute.pushMethods parameter.

Nested flows (declared with the FlowRoute) will also have it's generated Navigation Class instance provided as a field in the parent Navigation. The same applies for Grouped Routers. Usage eg:

final result = await SamplesNavigation.of(context).sampleOneNavigation.toScreenOne(testId: 'From Home');
print('RESULT $result');


SamplesNavigation.of(context).toSecond(testId: 'From Home');

CHANGELOG #

0.2.2 #

  • Increase plugins version range

0.2.1 #

  • Fix the bug when Android back button is pressed closing the app. Now, when the back button is pressed, the nuvigator will try to close the current page and will just close the app when doesn't have any pages to pop.

0.2.0 #

  • Add FlowRouter back, compatible with the new API of nested Nuvigators
  • Increase version constraint of analyzer

0.1.1 #

  • Fix a bug when trying to call onDeepLinkNotfound

0.1.0+2 #

  • Update pubspec.yaml dependencies versions

0.1.0+1 #

  • Improve pub description and update packages

0.1.0 #

  • [BREAKING] Major refactor and API revamp
  • Create Nuvigator navigator widget
  • Make Hero animations work
  • Screen was renamed to ScreenRoute
  • FlowRoute is a ScreenRoute for nested Nuvigators.
  • Make ScreenRoute cary information about DeepLinks
  • Make GlobalRouter able to be created with callbacks and a baseRouter
  • Add cupertinoDialogScreenType ScreenType
  • Provide access to the GlobalRouter through InheritedWidgets
  • Remove the need to extend the GlobalRouter
  • Removal of NavigationService in favor of ScreenRoute
  • Removal of FlowRouter in favor of nested Nuvigators
  • Removal of ScreenContext in favor of BuildContext
  • Added code generation for creating code from a base Router defined

0.0.4 #

  • Fix transition animation when coming from native
  • Fix transition animation when popping from flow

0.0.3 #

  • FlowRouter type now extends Object
  • The arguments of push methods was changed from Map to Object
  • ScreenWidget now is generic to set args type (default is Object)

0.0.2 #

  • Add popUntil to NavigationService

0.0.1 #

  • Initial version

example/README.md

showcase #

A new Flutter project.

Getting Started #

This project is a starting point for a Flutter application.

A few resources to get you started if this is your first Flutter project:

For help getting started with Flutter, view our online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  nuvigator: ^0.2.2

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:nuvigator/nuvigator.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
82
Health:
Code health derived from static analysis. [more]
95
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
89
Learn more about scoring.

We analyzed this package on Jan 16, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.0
  • pana: 0.13.4
  • Flutter: 1.12.13+hotfix.5

Health suggestions

Fix lib/builder/navigation_class.dart. (-1.99 points)

Analysis of lib/builder/navigation_class.dart reported 4 hints:

line 254 col 45: 'name' is deprecated and shouldn't be used. Check element, or use getDisplayString().

line 254 col 67: 'name' is deprecated and shouldn't be used. Check element, or use getDisplayString().

line 291 col 40: 'name' is deprecated and shouldn't be used. Check element, or use getDisplayString().

line 295 col 38: 'name' is deprecated and shouldn't be used. Check element, or use getDisplayString().

Fix lib/src/nuvigator.dart. (-1.99 points)

Analysis of lib/src/nuvigator.dart reported 4 hints:

line 88 col 19: 'rootAncestorStateOfType' is deprecated and shouldn't be used. Use findRootAncestorStateOfType instead. This feature was deprecated after v1.12.1..

line 88 col 43: 'TypeMatcher' is deprecated and shouldn't be used. TypeMatcher has been deprecated because it is no longer used in framework(only in deprecated methods). This feature was deprecated after v1.12.1..

line 89 col 19: 'ancestorStateOfType' is deprecated and shouldn't be used. Use findAncestorStateOfType instead. This feature was deprecated after v1.12.1..

line 89 col 39: 'TypeMatcher' is deprecated and shouldn't be used. TypeMatcher has been deprecated because it is no longer used in framework(only in deprecated methods). This feature was deprecated after v1.12.1..

Fix lib/builder/args_class.dart. (-0.50 points)

Analysis of lib/builder/args_class.dart reported 1 hint:

line 145 col 34: 'name' is deprecated and shouldn't be used. Check element, or use getDisplayString().

Fix lib/src/global_router.dart. (-0.50 points)

Analysis of lib/src/global_router.dart reported 1 hint:

line 43 col 17: 'inheritFromWidgetOfExactType' is deprecated and shouldn't be used. Use dependOnInheritedWidgetOfExactType instead. This feature was deprecated after v1.12.1..

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.2.2 <3.0.0
analyzer >=0.36.4 <0.40.0 0.39.4
build >=0.12.6 <2.0.0 1.2.2
build_config >=0.2.6 <0.5.0 0.4.1+1
code_builder ^3.2.0 3.2.1
dart_style >=1.2.9 <1.3.4 1.3.3
flutter 0.0.0
path_to_regexp ^0.3.0 0.3.0
source_gen >=0.9.4+4 <0.9.5 0.9.4+7
Transitive dependencies
_fe_analyzer_shared 1.0.3
args 1.5.2
async 2.4.0
built_collection 4.3.2
built_value 7.0.8
charcode 1.1.2
checked_yaml 1.0.2
collection 1.14.11 1.14.12
convert 2.1.1
crypto 2.1.4
csslib 0.16.1
fixnum 0.10.11
glob 1.2.0
html 0.14.0+3
js 0.6.1+1
json_annotation 3.0.1
logging 0.11.4
matcher 0.12.6
meta 1.1.8
node_interop 1.0.3
node_io 1.0.1+2
package_config 1.1.0
path 1.6.4
pedantic 1.9.0
pub_semver 1.4.2
pubspec_parse 0.1.5
quiver 2.1.2+1
sky_engine 0.0.99
source_span 1.6.0
stack_trace 1.9.3
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.1.6
vector_math 2.0.8
watcher 0.9.7+13
yaml 2.2.0
Dev dependencies
build_runner ^1.6.5
flutter_test
mockito ^4.1.1