diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md index 03a91573a26..2afdfb41d0f 100644 --- a/packages/go_router_builder/CHANGELOG.md +++ b/packages/go_router_builder/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.3 + +* Adds `initialLocation` for `StatefulShellBranchConfig` + ## 2.3.2 * Supports the latest `package:analyzer`. diff --git a/packages/go_router_builder/example/lib/stateful_shell_route_initial_location_example.dart b/packages/go_router_builder/example/lib/stateful_shell_route_initial_location_example.dart new file mode 100644 index 00000000000..63c16638e7b --- /dev/null +++ b/packages/go_router_builder/example/lib/stateful_shell_route_initial_location_example.dart @@ -0,0 +1,276 @@ +// 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:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +part 'stateful_shell_route_initial_location_example.g.dart'; + +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: '/home', + ); +} + +class HomeScreen extends StatelessWidget { + const HomeScreen({super.key}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: const Text('foo')), + ); +} + +@TypedStatefulShellRoute( + branches: >[ + TypedStatefulShellBranch( + routes: >[ + TypedGoRoute( + path: '/home', + ), + ], + ), + TypedStatefulShellBranch( + routes: >[ + TypedGoRoute( + path: '/notifications/:section', + ), + ], + ), + TypedStatefulShellBranch( + routes: >[ + TypedGoRoute( + path: '/orders', + ), + ], + ), + ], +) +class MainShellRouteData extends StatefulShellRouteData { + const MainShellRouteData(); + + @override + Widget builder( + BuildContext context, + GoRouterState state, + StatefulNavigationShell navigationShell, + ) { + return MainPageView( + navigationShell: navigationShell, + ); + } +} + +class HomeShellBranchData extends StatefulShellBranchData { + const HomeShellBranchData(); +} + +class NotificationsShellBranchData extends StatefulShellBranchData { + const NotificationsShellBranchData(); + + static String $initialLocation = '/notifications/old'; +} + +class OrdersShellBranchData extends StatefulShellBranchData { + const OrdersShellBranchData(); +} + +class HomeRouteData extends GoRouteData { + const HomeRouteData(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const HomePageView(label: 'Home page'); + } +} + +enum NotificationsPageSection { + latest, + old, + archive, +} + +class NotificationsRouteData extends GoRouteData { + const NotificationsRouteData({ + required this.section, + }); + + final NotificationsPageSection section; + + @override + Widget build(BuildContext context, GoRouterState state) { + return NotificationsPageView( + section: section, + ); + } +} + +class OrdersRouteData extends GoRouteData { + const OrdersRouteData(); + + @override + Widget build(BuildContext context, GoRouterState state) { + return const OrdersPageView(label: 'Orders page'); + } +} + +class MainPageView extends StatelessWidget { + const MainPageView({ + required this.navigationShell, + super.key, + }); + + final StatefulNavigationShell navigationShell; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(), + body: navigationShell, + bottomNavigationBar: BottomNavigationBar( + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.favorite), + label: 'Notifications', + ), + BottomNavigationBarItem( + icon: Icon(Icons.list), + label: 'Orders', + ), + ], + currentIndex: navigationShell.currentIndex, + onTap: (int index) => _onTap(context, index), + ), + ); + } + + void _onTap(BuildContext context, int index) { + navigationShell.goBranch( + index, + initialLocation: index == navigationShell.currentIndex, + ); + } +} + +class HomePageView extends StatelessWidget { + const HomePageView({ + required this.label, + super.key, + }); + + final String label; + + @override + Widget build(BuildContext context) { + return Center( + child: Text(label), + ); + } +} + +class NotificationsPageView extends StatelessWidget { + const NotificationsPageView({ + super.key, + required this.section, + }); + + final NotificationsPageSection section; + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 3, + initialIndex: NotificationsPageSection.values.indexOf(section), + child: const Column( + children: [ + TabBar( + tabs: [ + Tab( + child: Text( + 'Latest', + style: TextStyle(color: Colors.black87), + ), + ), + Tab( + child: Text( + 'Old', + style: TextStyle(color: Colors.black87), + ), + ), + Tab( + child: Text( + 'Archive', + style: TextStyle(color: Colors.black87), + ), + ), + ], + ), + Expanded( + child: TabBarView( + children: [ + NotificationsSubPageView( + label: 'Latest notifications', + ), + NotificationsSubPageView( + label: 'Old notifications', + ), + NotificationsSubPageView( + label: 'Archived notifications', + ), + ], + ), + ), + ], + ), + ); + } +} + +class NotificationsSubPageView extends StatelessWidget { + const NotificationsSubPageView({ + required this.label, + super.key, + }); + + final String label; + + @override + Widget build(BuildContext context) { + return Center( + child: Text(label), + ); + } +} + +class OrdersPageView extends StatelessWidget { + const OrdersPageView({ + required this.label, + super.key, + }); + + final String label; + + @override + Widget build(BuildContext context) { + return Center( + child: Text(label), + ); + } +} diff --git a/packages/go_router_builder/example/lib/stateful_shell_route_initial_location_example.g.dart b/packages/go_router_builder/example/lib/stateful_shell_route_initial_location_example.g.dart new file mode 100644 index 00000000000..5b398e07990 --- /dev/null +++ b/packages/go_router_builder/example/lib/stateful_shell_route_initial_location_example.g.dart @@ -0,0 +1,116 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: always_specify_types, public_member_api_docs + +part of 'stateful_shell_route_initial_location_example.dart'; + +// ************************************************************************** +// GoRouterGenerator +// ************************************************************************** + +List get $appRoutes => [ + $mainShellRouteData, + ]; + +RouteBase get $mainShellRouteData => StatefulShellRouteData.$route( + factory: $MainShellRouteDataExtension._fromState, + branches: [ + StatefulShellBranchData.$branch( + routes: [ + GoRouteData.$route( + path: '/home', + factory: $HomeRouteDataExtension._fromState, + ), + ], + ), + StatefulShellBranchData.$branch( + initialLocation: NotificationsShellBranchData.$initialLocation, + routes: [ + GoRouteData.$route( + path: '/notifications/:section', + factory: $NotificationsRouteDataExtension._fromState, + ), + ], + ), + StatefulShellBranchData.$branch( + routes: [ + GoRouteData.$route( + path: '/orders', + factory: $OrdersRouteDataExtension._fromState, + ), + ], + ), + ], + ); + +extension $MainShellRouteDataExtension on MainShellRouteData { + static MainShellRouteData _fromState(GoRouterState state) => + const MainShellRouteData(); +} + +extension $HomeRouteDataExtension on HomeRouteData { + static HomeRouteData _fromState(GoRouterState state) => const HomeRouteData(); + + String get location => GoRouteData.$location( + '/home', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension $NotificationsRouteDataExtension on NotificationsRouteData { + static NotificationsRouteData _fromState(GoRouterState state) => + NotificationsRouteData( + section: _$NotificationsPageSectionEnumMap + ._$fromName(state.pathParameters['section']!), + ); + + String get location => GoRouteData.$location( + '/notifications/${Uri.encodeComponent(_$NotificationsPageSectionEnumMap[section]!)}', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +const _$NotificationsPageSectionEnumMap = { + NotificationsPageSection.latest: 'latest', + NotificationsPageSection.old: 'old', + NotificationsPageSection.archive: 'archive', +}; + +extension $OrdersRouteDataExtension on OrdersRouteData { + static OrdersRouteData _fromState(GoRouterState state) => + const OrdersRouteData(); + + String get location => GoRouteData.$location( + '/orders', + ); + + void go(BuildContext context) => context.go(location); + + Future push(BuildContext context) => context.push(location); + + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + void replace(BuildContext context) => context.replace(location); +} + +extension on Map { + T _$fromName(String value) => + entries.singleWhere((element) => element.value == value).key; +} diff --git a/packages/go_router_builder/example/test/stateful_shell_route_initial_location_test.dart b/packages/go_router_builder/example/test/stateful_shell_route_initial_location_test.dart new file mode 100644 index 00000000000..6e819bf7303 --- /dev/null +++ b/packages/go_router_builder/example/test/stateful_shell_route_initial_location_test.dart @@ -0,0 +1,20 @@ +// 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. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router_builder_example/stateful_shell_route_initial_location_example.dart'; + +void main() { + testWidgets( + 'Navigate to Notifications section with old tab selected by default', + (WidgetTester tester) async { + await tester.pumpWidget(App()); + expect(find.text('Home'), findsOneWidget); + + await tester.tap(find.text('Notifications')); + await tester.pumpAndSettle(); + expect(find.text('Old notifications'), findsOneWidget); + }, + ); +} diff --git a/packages/go_router_builder/lib/src/route_config.dart b/packages/go_router_builder/lib/src/route_config.dart index 5925c11541f..d2ff611961e 100644 --- a/packages/go_router_builder/lib/src/route_config.dart +++ b/packages/go_router_builder/lib/src/route_config.dart @@ -137,6 +137,7 @@ class StatefulShellBranchConfig extends RouteBaseConfig { required super.routeDataClass, required super.parent, this.restorationScopeId, + this.initialLocation, }) : super._(); /// The command for calling the navigator key getter from the ShellRouteData. @@ -145,6 +146,9 @@ class StatefulShellBranchConfig extends RouteBaseConfig { /// The restoration scope id. final String? restorationScopeId; + /// The initial route. + final String? initialLocation; + @override Iterable classDeclarations() => []; @@ -153,7 +157,8 @@ class StatefulShellBranchConfig extends RouteBaseConfig { @override String get routeConstructorParameters => '${navigatorKey == null ? '' : 'navigatorKey: $navigatorKey,'}' - '${restorationScopeId == null ? '' : 'restorationScopeId: $restorationScopeId,'}'; + '${restorationScopeId == null ? '' : 'restorationScopeId: $restorationScopeId,'}' + '${initialLocation == null ? '' : 'initialLocation: $initialLocation,'}'; @override String get routeDataClassName => 'StatefulShellBranchData'; @@ -502,6 +507,10 @@ abstract class RouteBaseConfig { classElement, parameterName: r'$restorationScopeId', ), + initialLocation: _generateParameterGetterCode( + classElement, + parameterName: r'$initialLocation', + ), ); break; case 'TypedGoRoute': diff --git a/packages/go_router_builder/pubspec.yaml b/packages/go_router_builder/pubspec.yaml index da788c9d187..cf48de720f0 100644 --- a/packages/go_router_builder/pubspec.yaml +++ b/packages/go_router_builder/pubspec.yaml @@ -2,7 +2,7 @@ name: go_router_builder description: >- A builder that supports generated strongly-typed route helpers for package:go_router -version: 2.3.2 +version: 2.3.3 repository: https://github.com/flutter/packages/tree/main/packages/go_router_builder issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router_builder%22 diff --git a/packages/go_router_builder/test_inputs/statefull_shell_branch_data.dart b/packages/go_router_builder/test_inputs/statefull_shell_branch_data.dart new file mode 100644 index 00000000000..ec9b7bbc824 --- /dev/null +++ b/packages/go_router_builder/test_inputs/statefull_shell_branch_data.dart @@ -0,0 +1,12 @@ +// 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. + +import 'package:go_router/go_router.dart'; + +@TypedStatefulShellBranch() +class ShellRouteBranchData extends StatefulShellBranchData { + const ShellRouteBranchData(); + + static const String $initialLocation = '/main/second-tab'; +} diff --git a/packages/go_router_builder/test_inputs/statefull_shell_branch_data.dart.expect b/packages/go_router_builder/test_inputs/statefull_shell_branch_data.dart.expect new file mode 100644 index 00000000000..07b95b1deeb --- /dev/null +++ b/packages/go_router_builder/test_inputs/statefull_shell_branch_data.dart.expect @@ -0,0 +1,3 @@ +RouteBase get $shellRouteBranchData => StatefulShellBranchData.$branch( + initialLocation: ShellRouteBranchData.$initialLocation, + );