Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
990c293
implemented helpers for StatefulShellRoute
hannah-hyj Jun 16, 2023
4918014
Merge branch 'main' into statefulshellroute
hannah-hyj Jun 16, 2023
c6bde6d
lint
hannah-hyj Jun 16, 2023
52ce037
Update route_data.dart
hannah-hyj Jun 16, 2023
b9c8080
lint
hannah-hyj Jun 16, 2023
9005a8c
Update route_data.dart
hannah-hyj Jun 16, 2023
8cf69b6
update
hannah-hyj Jun 26, 2023
5963020
resolve comments
hannah-hyj Jun 28, 2023
4f60cd6
1
hannah-hyj Jun 16, 2023
e4fc526
Revert "1"
hannah-hyj Jun 16, 2023
d328021
update
hannah-hyj Jun 26, 2023
bc19f53
update tests
hannah-hyj Jun 28, 2023
f827680
Merge branch 'main' of https://github.com/flutter/packages into build…
hannah-hyj Jul 9, 2023
6a09020
update
hannah-hyj Jul 10, 2023
bb6064b
bump version
hannah-hyj Jul 10, 2023
441eb78
Update route_config.dart
hannah-hyj Jul 10, 2023
8b4e2de
Update stateful_shell_route_test.dart
hannah-hyj Jul 10, 2023
a2ef5d2
Merge branch 'main' of https://github.com/flutter/packages into build…
hannah-hyj Jul 12, 2023
13865ac
update route_config
hannah-hyj Jul 12, 2023
e3d109d
Update route_config.dart
hannah-hyj Jul 19, 2023
51ae0a0
Merge branch 'main' of https://github.com/flutter/packages into build…
hannah-hyj Jul 19, 2023
bfd36ef
merge
hannah-hyj Jul 20, 2023
03c069b
resolve comments
hannah-hyj Jul 25, 2023
0da2339
update
hannah-hyj Jul 25, 2023
06c7e67
Update route_config.dart
hannah-hyj Jul 25, 2023
ecbc5f5
update
hannah-hyj Jul 31, 2023
9808f62
Merge branch 'main' of https://github.com/flutter/packages into build…
hannah-hyj Jul 31, 2023
6044e32
Update pubspec.yaml
hannah-hyj Jul 31, 2023
3c2ed3b
lint
hannah-hyj Jul 31, 2023
1e78def
Update stateful_shell_route_example.dart
hannah-hyj Jul 31, 2023
eecb67e
Update pubspec.yaml
hannah-hyj Jul 31, 2023
2e63519
Update pubspec.yaml
hannah-hyj Jul 31, 2023
cfb1de8
Update stateful_shell_route_example.dart
hannah-hyj Jul 31, 2023
7fa8523
Merge branch 'main' into builder-stateful
hannah-hyj Aug 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
implemented helpers for StatefulShellRoute
  • Loading branch information
hannah-hyj committed Jun 16, 2023
commit 990c2934525a158bc65e311f4c5cba1ad5279c9c
4 changes: 4 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 8.0.3

- Adds helpers for go_router_builder for StatefulShellRoute support

## 8.0.2

- Fixes a bug in `debugLogDiagnostics` to support StatefulShellRoute.
Expand Down
6 changes: 5 additions & 1 deletion packages/go_router/lib/go_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ export 'src/route_data.dart'
RouteData,
GoRouteData,
ShellRouteData,
StatefulShellBranchData,
StatefulShellRouteData,
TypedRoute,
TypedGoRoute,
TypedShellRoute;
TypedShellRoute,
TypedStatefulShellBranch,
TypedStatefulShellRoute;
export 'src/router.dart';
export 'src/typedefs.dart'
show
Expand Down
4 changes: 2 additions & 2 deletions packages/go_router/lib/src/route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -655,8 +655,8 @@ class StatefulShellRoute extends ShellRouteBase {
required this.navigatorContainerBuilder,
this.restorationScopeId,
}) : assert(branches.isNotEmpty),
assert((pageBuilder != null) ^ (builder != null),
'One of builder or pageBuilder must be provided, but not both'),
assert((pageBuilder != null) || (builder != null),
'One of builder or pageBuilder must be provided'),
assert(_debugUniqueNavigatorKeys(branches).length == branches.length,
'Navigator keys must be unique'),
assert(_debugValidateParentNavigatorKeys(branches)),
Expand Down
159 changes: 157 additions & 2 deletions packages/go_router/lib/src/route_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ abstract class ShellRouteData extends RouteData {
) =>
const NoOpPage();

/// [pageBuilder] is used to build the page
/// [builder] is used to build the widget
Widget builder(
BuildContext context,
GoRouterState state,
Expand All @@ -159,7 +159,7 @@ abstract class ShellRouteData extends RouteData {
final Object? extra = state.extra;

// If the "extra" value is of type `T` then we know it's the source
// instance of `GoRouteData`, so it doesn't need to be recreated.
// instance of `ShellRouteData`, so it doesn't need to be recreated.
if (extra is T) {
return extra;
}
Expand Down Expand Up @@ -205,6 +205,131 @@ abstract class ShellRouteData extends RouteData {
);
}



/// Base class for supporting
/// [StatefulShellRoute](https://pub.dev/documentation/go_router/latest/go_router/StatefulShellRoute-class.html)
abstract class StatefulShellRouteData extends RouteData {
/// Default const constructor
const StatefulShellRouteData();


/// [pageBuilder] is used to build the page
Page<void> pageBuilder(
BuildContext context,
GoRouterState state,
Widget navigator,
) =>
const NoOpPage();

/// [builder] is used to build the widget
Widget builder(
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) =>
throw UnimplementedError(
'One of `builder` or `pageBuilder` must be implemented.',
);

/// A helper function used by generated code.
///
/// Should not be used directly.
static StatefulShellRoute $route<T extends StatefulShellRouteData>({
required T Function(GoRouterState) factory,
required List<StatefulShellBranch> branches,
String? restorationScopeId,
}) {
T factoryImpl(GoRouterState state) {
final Object? extra = state.extra;

// If the "extra" value is of type `T` then we know it's the source
// instance of `StatefulShellRouteData`, so it doesn't need to be recreated.
if (extra is T) {
return extra;
}

return (_stateObjectExpando[state] ??= factory(state)) as T;
}

Widget builder(
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) =>
factoryImpl(state).builder(
context,
state,
navigationShell,
);

Page<void> pageBuilder(
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) =>
factoryImpl(state).pageBuilder(
context,
state,
navigationShell,
);

return StatefulShellRoute.indexedStack(
branches: branches,
builder: builder,
pageBuilder: pageBuilder,
restorationScopeId: restorationScopeId,
);
}

/// Used to cache [StatefulShellRouteData] that corresponds to a given [GoRouterState]
/// to minimize the number of times it has to be deserialized.
static final Expando<StatefulShellRouteData> _stateObjectExpando =
Expando<StatefulShellRouteData>(
'GoRouteState to StatefulShellRouteData expando',
);
}

/// Base class for supporting
/// [StatefulShellRoute](https://pub.dev/documentation/go_router/latest/go_router/StatefulShellRoute-class.html)
abstract class StatefulShellBranchData extends RouteData {
/// Default const constructor
const StatefulShellBranchData();

/// A helper function used by generated code.
///
/// Should not be used directly.
static StatefulShellBranch $route<T extends StatefulShellBranchData>({
required T Function(GoRouterState) factory,
GlobalKey<NavigatorState>? navigatorKey,
List<RouteBase> routes = const <RouteBase>[],
}) {
T factoryImpl(GoRouterState state) {
final Object? extra = state.extra;

// If the "extra" value is of type `T` then we know it's the source
// instance of `StatefulShellBranchData`, so it doesn't need to be recreated.
if (extra is T) {
return extra;
}

return (_stateObjectExpando[state] ??= factory(state)) as T;
}

return StatefulShellBranch(
routes: routes,
navigatorKey: navigatorKey,
);
}

/// Used to cache [StatefulShellBranchData] that corresponds to a given [GoRouterState]
/// to minimize the number of times it has to be deserialized.
static final Expando<StatefulShellBranchData> _stateObjectExpando =
Expando<StatefulShellBranchData>(
'GoRouteState to StatefulShellBranchData expando',
);
}

/// A superclass for each typed route descendant
class TypedRoute<T extends RouteData> {
/// Default const constructor
Expand Down Expand Up @@ -256,6 +381,36 @@ class TypedShellRoute<T extends ShellRouteData> extends TypedRoute<T> {
final List<TypedRoute<RouteData>> routes;
}

/// A superclass for each typed shell route descendant
@Target(<TargetKind>{TargetKind.library, TargetKind.classType})
class TypedStatefulShellRoute<T extends StatefulShellRouteData>
extends TypedRoute<T> {
/// Default const constructor
const TypedStatefulShellRoute({
this.routes = const <TypedRoute<RouteData>>[],
});

/// Child route definitions.
///
/// See [RouteBase.routes].
final List<TypedRoute<RouteData>> routes;
}

/// A superclass for each typed shell route descendant
@Target(<TargetKind>{TargetKind.library, TargetKind.classType})
class TypedStatefulShellBranch<T extends StatefulShellBranchData>
extends TypedRoute<T> {
/// Default const constructor
const TypedStatefulShellBranch({
this.routes = const <TypedRoute<RouteData>>[],
});

/// Child route definitions.
///
/// See [RouteBase.routes].
final List<TypedRoute<RouteData>> routes;
}

/// Internal class used to signal that the default page behavior should be used.
@internal
class NoOpPage extends Page<void> {
Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: go_router
description: A declarative router for Flutter based on Navigation 2 supporting
deep linking, data-driven routes and more
version: 8.0.2
version: 8.0.3
repository: https://github.com/flutter/packages/tree/main/packages/go_router
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22

Expand Down
106 changes: 105 additions & 1 deletion packages/go_router/test/route_data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ final ShellRoute _shellRouteDataBuilder = ShellRouteData.$route(
),
],
);

class _GoRouteDataBuildPage extends GoRouteData {
const _GoRouteDataBuildPage();
@override
Expand Down Expand Up @@ -86,6 +85,71 @@ final ShellRoute _shellRouteDataPageBuilder = ShellRouteData.$route(
],
);

class _StatefulShellBranchDataBuilder extends StatefulShellBranchData {
const _StatefulShellBranchDataBuilder();
}
class _StatefulShellRouteDataBuilder extends StatefulShellRouteData {
const _StatefulShellRouteDataBuilder();

@override
Widget builder(
BuildContext context,
GoRouterState state,
Widget navigator,
) =>
SizedBox(
key: const Key('builder'),
child: navigator,
);
}
final StatefulShellRoute _statefulShellRouteDataBuilder =
StatefulShellRouteData.$route(
factory: (GoRouterState state) => const _StatefulShellRouteDataBuilder(),
branches: <StatefulShellBranch>[
StatefulShellBranchData.$route(
factory: (GoRouterState state) => const _StatefulShellBranchDataBuilder(),
routes: <RouteBase>[
GoRouteData.$route(
path: '/child',
factory: (GoRouterState state) => const _GoRouteDataBuild(),
),
],
),
],
);

class _StatefulShellRouteDataPageBuilder extends StatefulShellRouteData {
const _StatefulShellRouteDataPageBuilder();

@override
Page<void> pageBuilder(
BuildContext context,
GoRouterState state,
Widget navigator,
) =>
MaterialPage<void>(
child: SizedBox(
key: const Key('page-builder'),
child: navigator,
),
);
}

final StatefulShellRoute _statefulShellRouteDataPageBuilder = StatefulShellRouteData.$route(
factory: (GoRouterState state) => const _StatefulShellRouteDataPageBuilder(),
branches: <StatefulShellBranch>[
StatefulShellBranchData.$route(
factory: (GoRouterState state) => const _StatefulShellBranchDataBuilder(),
routes: <RouteBase>[
GoRouteData.$route(
path: '/child',
factory: (GoRouterState state) => const _GoRouteDataBuild(),
),
],
),
],
);

class _GoRouteDataRedirectPage extends GoRouteData {
const _GoRouteDataRedirectPage();
@override
Expand Down Expand Up @@ -181,6 +245,46 @@ void main() {
);
});

group('StatefulShellRouteData', () {
testWidgets(
'It should build the page from the overridden build method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/child',
routes: <RouteBase>[
_statefulShellRouteDataBuilder,
],
);
await tester.pumpWidget(MaterialApp.router(
routeInformationProvider: goRouter.routeInformationProvider,
routeInformationParser: goRouter.routeInformationParser,
routerDelegate: goRouter.routerDelegate,
));
expect(find.byKey(const Key('builder')), findsOneWidget);
expect(find.byKey(const Key('page-builder')), findsNothing);
},
);

testWidgets(
'It should build the page from the overridden buildPage method',
(WidgetTester tester) async {
final GoRouter goRouter = GoRouter(
initialLocation: '/child',
routes: <RouteBase>[
_statefulShellRouteDataPageBuilder,
],
);
await tester.pumpWidget(MaterialApp.router(
routeInformationProvider: goRouter.routeInformationProvider,
routeInformationParser: goRouter.routeInformationParser,
routerDelegate: goRouter.routerDelegate,
));
expect(find.byKey(const Key('builder')), findsNothing);
expect(find.byKey(const Key('page-builder')), findsOneWidget);
},
);
});

testWidgets(
'It should redirect using the overridden redirect method',
(WidgetTester tester) async {
Expand Down