Skip to content

Commit

Permalink
Merge pull request #5 from nubank/poc/nuvigator-navigator
Browse files Browse the repository at this point in the history
[V2] Nuvigator
  • Loading branch information
leoiacovini authored Oct 28, 2019
2 parents 19777ef + c4c68fb commit dd92e79
Show file tree
Hide file tree
Showing 49 changed files with 2,147 additions and 971 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pubspec.lock
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
example/ios/Flutter/flutter_export_environment.sh

# Exceptions to above rules.
!**/ios/**/default.mode1v3
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# CHANGELOG

## 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
Expand Down
168 changes: 41 additions & 127 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Routing and Navigation package.

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
complex aspects and tricky behaviours that the framework will handle for you.
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.
Expand All @@ -19,67 +19,65 @@ and how you can use they in your project.
Basic usage

```dart
class MyRouter extends SimpleRouter {
class MyRouter extends BaseRouter {
@override
Map<String, Screen> get screensMap => {
'MyScreenRouteName': Screen.card((screenContext) => MyScreen(screenContext))
'MyScreenRouteName': Screen(
builder: (screenContext) => MyScreen(screenContext),
),
};
}
```

```dart
class MyGlobalRouter extends GlobalRouter {
@override
List<Router> get routers => [
MyRouter(),
];
}
```
```dart
final router = MyGlobalRouter();
final router = GlobalRouter(baseRouter: MyRouter());
Widget build(BuildContext context) {
return MaterialApp(onChangeRoute: router.getRoute);
return MaterialApp(
builder: Nuvigator(
router: router,
screenType: materialScreenType,
initialRoute: 'MyScreenRouteName',
),
);
}
```

## Screens

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

## Routers
`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. `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)`.

### Interface
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.

A Router is just a interface that aims to provide a screen for a given route name.
## ScreenRoute and FlowRoute

```dart
abstract class Router {
Screen getScreen({String routeName});
Envelopes a widget that is going to be presented as a Screen by a Navigator.
The Screen contains a ScreenBuilder function, and some attributes, like transition type,
that are going to be used to generate a route properly.

Future<DeepLinkFlow> getDeepLinkFlowForUrl(String url) => null;
}
```
Screen Options:

Below you will find some implementations of this Interface that can be used to define the Routing in you application.
- builder
- screenType
- deepLink
- wrapperFn

### SimpleRouter
### BaseRouter

Basic Usage:

```dart
class ChatRouter extends SimpleRouter {
@override
Map<String, Screen> get screensMap => {
'test': Screen<void>.card((screenContext) => ChatHomeScreen(screenContext)),
};
class ChatRouter extends BaseRouter {
@override
Map<String, String> get deepLinksMap => {
'/myGroup/test/': 'test',
Map<String, ScreenRoute> get screensMap => {
'home': ScreenRoute(
builder: (context) => ChatHomeScreen(context),
screenType: materialScreenType,
deepLink: '/myGroup/test/',
),
};
}
```
Expand All @@ -90,11 +88,6 @@ Options:

A Map<String, Screen> that contains the mapping between RouteNames and Screens.

- `deepLinksMap`

A Map<String, String> that contains the mapping between deeplink path and RouteNames. The keys
accept path params template notation, eg: `/myFeature/:id/details`

- `deepLinkPrefix`

A String, that will be used as prefix for every entry key at the `deepLinksMaps`.
Expand All @@ -105,88 +98,9 @@ A function that will receives a ScreenContext and a child Widget. Should return
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).


### GroupRouter

`GroupRouter` extends the `SimpleRouter` so, every option of the SimpleRouter can be used here. In addition to that
the `GroupRouter` supports a list if child `Routers` to be iterated in the case of itself not being able to resolve
the requested RouteName.

```dart
class LendingRouter extends GroupRouter {
@override
List<Router> get routers => [
ChatRouter(),
];
}
```

Options:

All from `SimpleRouter` plus:

- `routers`

A `List<Router>` that will be called in the case of the GroupRouter itself not being able to resolve the RouteName.

- `deepLinkPrefix`

Behaves like the one in the `SimpleRouter`, but in the case of the DeepLink being dispatched to the child `routers`, it
will have this prefix trimmed/removed before sending down it.

### Writing Custom Routers

## Flows

You will find the `FlowRouter` that can be used as a Mixin for any SimpleRouter subclass
this Mixin enables the Router to present it's screens in a nested Navigator
as a new Flow in the App. Flow Routers are a very powerful abstraction
that enables any Router to easily become a Flow coordinator.

Example:

```dart
class MyRouter extends SimpleRouter with FlowRouter<void> {
@override
Map<String, Screen> get screensMap => {
'MyScreenRouteName': Screen.card<void>((screenContext) => MyScreen(screenContext))
};
}
```

Options:

(All from the extendend Router plus)

- `transitionType`

The Transition type that will be used to present the flow with the first screen already mounted.

- `flowWrapper`

Overridable function that will provide a Wrapper for the entire Flow.
Useful for providing information to the context that will be shared by all
flow screens.

- `initialRouteName`

RouteName of the first Screen of this Flow, will permit the usage of the
method: `flowRouter.initialScreen` to get the initial screen of this Flow.


## NavigationService

Is an abstraction created to help you navigate through Screens. Every ScreenWidget will receive a ScreenContext, that
contains an instance of a `NavigationService`. NavigationService contains custom `pushNamed`, `pop` and others methods
for navigating inside the Navigators. NavigationService will handle properly Nested Navigators, Flows and other tricky
behaviours originated from complex Navigation structures.

Always use the `NavigationService` when wanting to navigate!
Routers to be grouped and checked if this router does not match any screen.

`NavigationService` can be extended (and usually is encouraged) to include custom navigation methods with already typed
params and responses.
## Code Generators

8 changes: 8 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
builders:
nuvigator:
import: "package:nuvigator/builder.dart"
builder_factories: ["nuvigatorBuilder"]
build_extensions: {".dart": [".nuvigator.g.part"]}
auto_apply: dependents
build_to: cache
applies_builders: ["source_gen|combining_builder"]
43 changes: 23 additions & 20 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import 'package:example/samples/navigation/samples_router.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:nuvigator/nuvigator.dart';

import 'samples/modules/sample_one/navigation/sample_one_router.dart';
import 'src/example_app_router.dart';

void main() => runApp(MyApp());

final rootNavigatorKey = GlobalKey<NavigatorState>();

class MyApp extends StatelessWidget {
static final ExampleAppRouter router = ExampleAppRouter(rootNavigatorKey);
static final router = GlobalRouter(baseRouter: samplesRouter);

@override
Widget build(BuildContext context) {
return Provider.value(
value: router,
child: MaterialApp(
title: 'Nubank',
navigatorKey: rootNavigatorKey,
initialRoute: 'home',
onGenerateRoute: router.getRoute,
return MaterialApp(
title: 'Nubank',
builder: Nuvigator(
screenType: cupertinoDialogScreenType,
router: router,
initialRoute: SamplesRoutes.home,
),
);
}
}

class HomeScreen extends ExampleScreenWidget {
HomeScreen(ScreenContext screenContext) : super(screenContext);
class HomeScreen extends ScreenWidget {
HomeScreen(BuildContext context) : super(context);

@override
Widget build(BuildContext context) {
Expand All @@ -39,20 +35,27 @@ class HomeScreen extends ExampleScreenWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FlatButton(
child: const Text('Go to sample one with flutter navigation'),
onPressed: () => navigation.samples.sampleOne.start('id_1234'),
Hero(
child: const FlutterLogo(),
tag: 'HERO',
),
FlatButton(
child: const Text('Go to sample one with flutter navigation'),
onPressed: () async {
final result = await SamplesNavigation.of(context)
.sampleOneNavigation
.toScreenOne(testId: 'From Home');
print('RESULT $result');
}),
FlatButton(
child: const Text('Go to sample one with deepLink'),
onPressed: () => ExampleAppRouter.of(context)
onPressed: () => GlobalRouter.of(context)
.openDeepLink<void>(Uri.parse(screenOneDeepLink)),
),
FlatButton(
child: const Text('Go to sample two with flow'),
onPressed: () async {
final value = await navigation.samples.sampleTwo.start('id_1234');
print('Return from sample two with value: $value');
SamplesNavigation.of(context).toSecond(testId: 'From Home');
},
),
],
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import 'package:flutter/material.dart';
import 'package:nuvigator/nuvigator.dart';

import '../screen/screen_one.dart';
import '../screen/screen_two.dart';
import 'sample_one_routes.dart';

part 'sample_one_router.g.dart';

const screenOneDeepLink =
'exapp://deepPrefix/sampleOne/screenOne/id_1234_deepLink';

class _SampleOneRouter extends SimpleRouter {
@NuRouter()
class SampleOneRouter extends BaseRouter {
@override
String get deepLinkPrefix => '/sampleOne/';

@override
Map<String, Screen> get screensMap => {
SampleOneRoutes.screen_one: s1ScreenOnePage,
SampleOneRoutes.screen_two: s1ScreenTwoPage,
};
@NuRoute(deepLink: 'screenOne/:testId')
ScreenRoute screenOne({@required String testId}) => const ScreenRoute(
builder: ScreenOne.builder,
);

@NuRoute()
ScreenRoute<int> screenTwo() => const ScreenRoute<int>(
builder: ScreenTwo.builder,
);

@override
Map<String, String> get deepLinksMap => {
'screenOne/:testId': SampleOneRoutes.screen_one,
};
Map<RouteDef, ScreenRouteBuilder> get screensMap =>
_$sampleOneScreensMap(this);
}

final sampleOneRouter = _SampleOneRouter();
final sampleOneRouter = SampleOneRouter();
Loading

0 comments on commit dd92e79

Please sign in to comment.