felangel / felangel/flow_builder

[Proposal] Add Routing Support

Offen
#41 18 Kommentare 69 Reaktionen 1 zugewiesene Person Zugewiesen an @felangel Auf GitHub ansehen
enhancement feedback wanted
Vorherrschende Sprache
Dart
Sterne
416
Forks
68
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

# [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(
...
)
}
}
```

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.