felangel / felangel/flow_builder

[Proposal] Add Routing Support

Abierto
#41 18 comentarios 69 reacciones 1 asignado Asignado a @felangel Ver en GitHub
enhancement feedback wanted
Lenguaje dominante
Dart
Estrellas
416
Forks
68
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

# [Proposal] Add Routing Support

Currently `FlowBuilder` does not have custom routing support for deep linking, dynamic linking, custom paths, query parameters, browser url synchronization, etc (#20).

This proposal outlines a potential FlowBuilder API which can accomodate for the above use-cases by leveraging the Navigator 2.0 router APIs. Ideally, the proposed enhancements should be backward compatible with the existing API.

## Routing API

```dart
FlowBuilder(
// The state of the flow.
state: FlowState(),
// `VoidCallback` invoked whenever the route location changes.
// Responsible for reacting to changes in the location.
//
// * Will be invoked immediately when the app is launched
// to determine what the initial state of the flow should be.
// * Will be invoked when the current route changes via `pushNamed`, `pushReplacementNamed`, etc.
onLocationChanged: (BuildContext context, FlowLocation location) {
// Equivalent to `window.location.pathname`.
final String path = location.path;

// Map of query parameters which are analagous to `window.location.search`.
final Map params = location.params;

// Determine the flow state based on the current location.
final FlowState state = _determineFlowState(path, params);

/// Update the flow state.
context.flow().update((_) => state);
},
// Called whenever the flow state changes. Will remaing unchanged.
// Responsible for determining the correct navigation stack
// based on the current flow state.
onGeneratePages: (FlowState state, List currentPages) {
final List pages = _determinePages(state, currentPages);
return pages;
}
)
```

### Execution Flow

1. `FlowBuilder` is initialized with a `state`
2. `onLocationChanged` is invoked when a location change occurs.
3. Flow state can be updated based on the location change.
4. `onGeneratePages` is triggered when the flow state changes & updates the nav stack.

The developer can optionally react to changes in `FlowLocation` (abstraction on top of `RouteInformation`) and trigger updates in flow state.

### Named Routes

Named routes can be achieved by defining a `FlowPage` which extends `Page`.

```dart
const profilePath = 'profile';

...

FlowBuilder(
state: Profile(),
onLocationChanged: (BuildContext context, FlowLocation location) {
if (location.path != profilePath) return;
// Alternatively we can potentially use `fromJson` with `package:json_serializable`.
// `final profile = Profile.fromJson(location.params);`
final profile = Profile(
name: location.params['name'] as String?,
age: int.tryParse(location.params['age'] as String?)
);
context.flow().update((_) => profile);
},
onGeneratePages: (Profile profile, List pages) {
return [
FlowPage(
child: ProfileNameForm(),
location: FlowLocation(path: profilePath),
),
if (profile.name != null)
FlowPage(
child: ProfileAgeForm(),
location: FlowLocation(path: profilePath, params: {'name': profile.name})
),
]
}
)
```

The above code will result in the following state to route mapping:

- `Profile()` (default): `/profile`
- `Profile(name: 'Felix')`: `/profile?name=Felix`
- `Profile(name: 'Felix', age: 26)`: `/profile?name=Felix&age=26`

### Navigation

Navigation will largely remain unchanged. Using `context.flow` or a `FlowController`, developers can `update` the flow state or `complete` the flow. The main difference would be updates to the flow state can potentially be accompanied by location changes if the associated pages are of type `FlowPage` with a custom `location`. When a named route is pushed via `Navigator.of(context).pushNamed`, all available `FlowBuilder` instances will be notified via `onLocationChanged`.

### Nested Routes

`FlowBuilders` can be nested to support nested routing. For example:

```dart
enum AuthState { uninitialized, unauthenticated, authenticated }

class AuthBloc extends Bloc {...}

FlowBuilder(
state: context.watch().state,
onGeneratePages: (AuthState state, List pages) {
switch (state) {
case AuthState.uninitialized:
return [Splash.page()];
case AuthState.unauthenticated:
return [Login.page()];
case AuthState.authenticated:
return [Home.page()];
}
}
)
```

We can push a nested flow from within `Login` to initiate a Sign Up flow.

```dart
class Login extends StatelessWidget {

...

@override
Widget build(BuildContext context) {
...
ElevatedButton(
onPressed: () => Navigator.of(context).push(SignUp.page());
)
}
}
```

```dart
class SignUp extends StatelessWidget {

...

@override
Widget build(BuildContext context) {
return FlowBuilder(
...
)
}
}
```

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.