Skip to content

[go_router_builder] Add go_router StatefulShellRoute support to go_router_builder #4238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Aug 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions packages/go_router_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.3.0

* Adds Support for StatefulShellRoute

## 2.2.5

* Fixes a bug where shell routes without const constructor were not generated correctly.
Expand Down
4 changes: 2 additions & 2 deletions packages/go_router_builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ To use `go_router_builder`, you need to have the following dependencies in
```yaml
dependencies:
# ...along with your other dependencies
go_router: ^7.0.0
go_router: ^9.0.3

dev_dependencies:
# ...along with your other dev-dependencies
build_runner: ^2.0.0
go_router_builder: ^2.0.0
go_router_builder: ^2.3.0
```

### Source code
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

part 'stateful_shell_route_example.g.dart';

final GlobalKey<NavigatorState> _sectionANavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'sectionANav');
void main() => runApp(App());

class App extends StatelessWidget {
App({super.key});

@override
Widget build(BuildContext context) => MaterialApp.router(
routerConfig: _router,
);

final GoRouter _router = GoRouter(
routes: $appRoutes,
initialLocation: '/detailsA',
);
}

class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('foo')),
);
}

@TypedStatefulShellRoute<MyShellRouteData>(
branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
TypedStatefulShellBranch<BranchAData>(
routes: <TypedRoute<RouteData>>[
TypedGoRoute<DetailsARouteData>(path: '/detailsA'),
],
),
TypedStatefulShellBranch<BranchBData>(
routes: <TypedRoute<RouteData>>[
TypedGoRoute<DetailsBRouteData>(path: '/detailsB'),
],
),
],
)
class MyShellRouteData extends StatefulShellRouteData {
const MyShellRouteData();

@override
Widget builder(
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) {
return navigationShell;
}

static const String $restorationScopeId = 'restorationScopeId';

static Widget $navigatorContainerBuilder(BuildContext context,
StatefulNavigationShell navigationShell, List<Widget> children) {
return ScaffoldWithNavBar(
navigationShell: navigationShell,
children: children,
);
}
}

class BranchAData extends StatefulShellBranchData {
const BranchAData();
}

class BranchBData extends StatefulShellBranchData {
const BranchBData();

static final GlobalKey<NavigatorState> $navigatorKey = _sectionANavigatorKey;
static const String $restorationScopeId = 'restorationScopeId';
}

class DetailsARouteData extends GoRouteData {
const DetailsARouteData();

@override
Widget build(BuildContext context, GoRouterState state) {
return const DetailsScreen(label: 'A');
}
}

class DetailsBRouteData extends GoRouteData {
const DetailsBRouteData();

@override
Widget build(BuildContext context, GoRouterState state) {
return const DetailsScreen(label: 'B');
}
}

/// Builds the "shell" for the app by building a Scaffold with a
/// BottomNavigationBar, where [child] is placed in the body of the Scaffold.
class ScaffoldWithNavBar extends StatelessWidget {
/// Constructs an [ScaffoldWithNavBar].
const ScaffoldWithNavBar({
required this.navigationShell,
required this.children,
Key? key,
}) : super(key: key ?? const ValueKey<String>('ScaffoldWithNavBar'));

/// The navigation shell and container for the branch Navigators.
final StatefulNavigationShell navigationShell;

/// The children (branch Navigators) to display in a custom container
/// ([AnimatedBranchContainer]).
final List<Widget> children;

@override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBranchContainer(
currentIndex: navigationShell.currentIndex,
children: children,
),
bottomNavigationBar: BottomNavigationBar(
// Here, the items of BottomNavigationBar are hard coded. In a real
// world scenario, the items would most likely be generated from the
// branches of the shell route, which can be fetched using
// `navigationShell.route.branches`.
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'),
BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'),
],
currentIndex: navigationShell.currentIndex,
onTap: (int index) => _onTap(context, index),
),
);
}

/// Navigate to the current location of the branch at the provided index when
/// tapping an item in the BottomNavigationBar.
void _onTap(BuildContext context, int index) {
// When navigating to a new branch, it's recommended to use the goBranch
// method, as doing so makes sure the last navigation state of the
// Navigator for the branch is restored.
navigationShell.goBranch(
index,
// A common pattern when using bottom navigation bars is to support
// navigating to the initial location when tapping the item that is
// already active. This example demonstrates how to support this behavior,
// using the initialLocation parameter of goBranch.
initialLocation: index == navigationShell.currentIndex,
);
}
}

/// Custom branch Navigator container that provides animated transitions
/// when switching branches.
class AnimatedBranchContainer extends StatelessWidget {
/// Creates a AnimatedBranchContainer
const AnimatedBranchContainer(
{super.key, required this.currentIndex, required this.children});

/// The index (in [children]) of the branch Navigator to display.
final int currentIndex;

/// The children (branch Navigators) to display in this container.
final List<Widget> children;

@override
Widget build(BuildContext context) {
return Stack(
children: children.mapIndexed(
(int index, Widget navigator) {
return AnimatedScale(
scale: index == currentIndex ? 1 : 1.5,
duration: const Duration(milliseconds: 400),
child: AnimatedOpacity(
opacity: index == currentIndex ? 1 : 0,
duration: const Duration(milliseconds: 400),
child: _branchNavigatorWrapper(index, navigator),
),
);
},
).toList());
}

Widget _branchNavigatorWrapper(int index, Widget navigator) => IgnorePointer(
ignoring: index != currentIndex,
child: TickerMode(
enabled: index == currentIndex,
child: navigator,
),
);
}

/// The details screen for either the A or B screen.
class DetailsScreen extends StatefulWidget {
/// Constructs a [DetailsScreen].
const DetailsScreen({
required this.label,
this.param,
this.extra,
super.key,
});

/// The label to display in the center of the screen.
final String label;

/// Optional param
final String? param;

/// Optional extra object
final Object? extra;
@override
State<StatefulWidget> createState() => DetailsScreenState();
}

/// The state for DetailsScreen
class DetailsScreenState extends State<DetailsScreen> {
int _counter = 0;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Details Screen - ${widget.label}'),
),
body: _build(context),
);
}

Widget _build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Details for ${widget.label} - Counter: $_counter',
style: Theme.of(context).textTheme.titleLarge),
const Padding(padding: EdgeInsets.all(4)),
TextButton(
onPressed: () {
setState(() {
_counter++;
});
},
child: const Text('Increment counter'),
),
const Padding(padding: EdgeInsets.all(8)),
if (widget.param != null)
Text('Parameter: ${widget.param!}',
style: Theme.of(context).textTheme.titleMedium),
const Padding(padding: EdgeInsets.all(8)),
if (widget.extra != null)
Text('Extra: ${widget.extra!}',
style: Theme.of(context).textTheme.titleMedium),
],
),
);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/go_router_builder/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ environment:
sdk: ">=2.18.0 <4.0.0"

dependencies:
collection: ^1.15.0
flutter:
sdk: flutter
go_router: ^10.0.0
Expand Down
Loading