Skip to content

Commit c9eeda9

Browse files
committed
Navigation type reset: clear the history and open a screen above an optional base
NavigationType.reset with baseDestinationId and baseParams, like pushNamedAndRemoveUntil with a base route left beneath the destination. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UErtKMSFkZWHUh8DrQMcZW
1 parent f590013 commit c9eeda9

3 files changed

Lines changed: 76 additions & 2 deletions

File tree

‎lib/src/api/models/action/navigation_action.dart‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ enum NavigationType {
1717
pop,
1818

1919
/// Replace the current page with the specified page in the navigation stack.
20-
replace;
20+
replace,
21+
22+
/// Clear the navigation history and open the specified page, with
23+
/// [NavigationAction.baseDestinationId] (if set) beneath it, so going back
24+
/// leads there and no further: Flutter's `pushNamedAndRemoveUntil`. For
25+
/// example, after creating a trip, open the trip above the trips list
26+
/// instead of above the creation form.
27+
reset;
2128

2229
/// Displayable string representation of [NavigationType].
2330
String get prettify {
@@ -28,6 +35,8 @@ enum NavigationType {
2835
return 'Navigate back';
2936
case NavigationType.replace:
3037
return 'Replace page';
38+
case NavigationType.reset:
39+
return 'Clear history and navigate';
3140
}
3241
}
3342
}
@@ -44,28 +53,50 @@ class NavigationAction extends ActionModel with Equatable, SerializableMixin {
4453
/// Parameters to pass to the destination page.
4554
final Map<String, dynamic> params;
4655

56+
/// For [NavigationType.reset]: the canvas left beneath the destination
57+
/// once the history is cleared, which going back from the destination
58+
/// opens. Null leaves the destination alone in the history.
59+
final String? baseDestinationId;
60+
61+
/// Parameters to pass to the [baseDestinationId] page.
62+
final Map<String, dynamic>? baseParams;
63+
4764
/// Creates a new [NavigationAction].
4865
NavigationAction({
4966
this.navigationType = NavigationType.push,
5067
required this.destinationId,
5168
Map<String, dynamic>? params,
69+
this.baseDestinationId,
70+
this.baseParams,
5271
super.nonBlocking,
5372
super.enabled,
5473
}) : params = {...params ?? {}},
5574
super(type: ActionType.navigation);
5675

5776
@override
58-
List<Object?> get props => [destinationId, navigationType, params];
77+
List<Object?> get props => [
78+
destinationId,
79+
navigationType,
80+
params,
81+
baseDestinationId,
82+
baseParams,
83+
];
5984

6085
/// Duplicates this [NavigationAction] with given data overrides.
6186
NavigationAction copyWith({
6287
NavigationType? navigationType,
6388
String? destinationId,
6489
Map<String, String>? params,
90+
String? baseDestinationId,
91+
Map<String, dynamic>? baseParams,
6592
}) => NavigationAction(
6693
navigationType: navigationType ?? this.navigationType,
6794
destinationId: destinationId ?? this.destinationId,
6895
params: {...this.params, ...params ?? {}},
96+
baseDestinationId: baseDestinationId ?? this.baseDestinationId,
97+
baseParams: baseParams ?? this.baseParams,
98+
nonBlocking: nonBlocking,
99+
enabled: enabled,
69100
);
70101

71102
/// Creates a new [NavigationAction] instance from a JSON data.

‎lib/src/api/models/action/navigation_action.g.dart‎

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/navigation_reset_test.dart‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:codelessly_api/codelessly_api.dart';
2+
import 'package:test/test.dart';
3+
4+
void main() {
5+
test('a reset navigation round-trips with its base screen', () {
6+
final NavigationAction action = NavigationAction(
7+
navigationType: NavigationType.reset,
8+
destinationId: 'trip',
9+
params: {'tripId': r'${data.id}'},
10+
baseDestinationId: 'trips',
11+
baseParams: {'tab': 'upcoming'},
12+
);
13+
final Map json = action.toJson();
14+
expect(json['navigationType'], 'reset');
15+
expect(json['baseDestinationId'], 'trips');
16+
final ActionModel back = ActionModel.fromJson(json);
17+
expect(back, isA<NavigationAction>());
18+
expect(back, action);
19+
expect((back as NavigationAction).baseParams, {'tab': 'upcoming'});
20+
expect(NavigationType.reset.prettify, 'Clear history and navigate');
21+
});
22+
23+
test('a navigation without a base screen writes no base keys', () {
24+
final Map json = NavigationAction(destinationId: 'home').toJson();
25+
expect(json.containsKey('baseDestinationId'), isFalse);
26+
expect(json.containsKey('baseParams'), isFalse);
27+
expect(json.containsKey('navigationType'), isFalse);
28+
final NavigationAction reset = NavigationAction.fromJson({
29+
'type': 'navigation',
30+
'navigationType': 'reset',
31+
'destinationId': 'home',
32+
});
33+
expect(reset.navigationType, NavigationType.reset);
34+
expect(reset.baseDestinationId, isNull);
35+
});
36+
}

0 commit comments

Comments
 (0)