[go_router_builder] Generate initialLocation with StatefulShellBranchConfig (#4880)

Add the possibility to generate `initialLocation` with `StatefulShellBranchConfig`

before:
```dart
class MyRoute extends StatefulShellBranchData {
  const MyRoute();
}
```

after:
```dart
class MyRoute extends StatefulShellBranchData {
  const MyRoute();

  static const String $initialLocation = '/my-route';
}
```

*List which issues are fixed by this PR. You must list at least one issue.*
[issues/130786](https://github.com/flutter/flutter/issues/130786#issuecomment-1712671560)
diff --git a/packages/go_router_builder/CHANGELOG.md b/packages/go_router_builder/CHANGELOG.md
index 03a9157..2afdfb4 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 0000000..63c1663
--- /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<MainShellRouteData>(
+  branches: <TypedStatefulShellBranch<StatefulShellBranchData>>[
+    TypedStatefulShellBranch<HomeShellBranchData>(
+      routes: <TypedRoute<RouteData>>[
+        TypedGoRoute<HomeRouteData>(
+          path: '/home',
+        ),
+      ],
+    ),
+    TypedStatefulShellBranch<NotificationsShellBranchData>(
+      routes: <TypedRoute<RouteData>>[
+        TypedGoRoute<NotificationsRouteData>(
+          path: '/notifications/:section',
+        ),
+      ],
+    ),
+    TypedStatefulShellBranch<OrdersShellBranchData>(
+      routes: <TypedRoute<RouteData>>[
+        TypedGoRoute<OrdersRouteData>(
+          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>[
+          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: <Widget>[
+          TabBar(
+            tabs: <Tab>[
+              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: <Widget>[
+                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 0000000..5b398e0
--- /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<RouteBase> 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<T?> push<T>(BuildContext context) => context.push<T>(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<T?> push<T>(BuildContext context) => context.push<T>(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<T?> push<T>(BuildContext context) => context.push<T>(location);
+
+  void pushReplacement(BuildContext context) =>
+      context.pushReplacement(location);
+
+  void replace(BuildContext context) => context.replace(location);
+}
+
+extension<T extends Enum> on Map<T, String> {
+  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 0000000..6e819bf
--- /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 5925c11..d2ff611 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 @@
     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 @@
   /// The restoration scope id.
   final String? restorationScopeId;
 
+  /// The initial route.
+  final String? initialLocation;
+
   @override
   Iterable<String> classDeclarations() => <String>[];
 
@@ -153,7 +157,8 @@
   @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 @@
             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 da788c9..cf48de7 100644
--- a/packages/go_router_builder/pubspec.yaml
+++ b/packages/go_router_builder/pubspec.yaml
@@ -2,7 +2,7 @@
 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 0000000..ec9b7bb
--- /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<ShellRouteBranchData>()
+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 0000000..07b95b1
--- /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,
+    );