felangel / felangel/flow_builder

[Proposal] Add Routing Support

Đang mở
#41 18 bình luận 69 reaction 1 người được giao Được giao cho @felangel Xem trên GitHub
enhancement feedback wanted
Ngôn ngữ chính
Dart
Star
416
Fork
68
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

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

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.