[flutter_adaptive_scaffold] Call onDestinationSelected in bottom nav. (#2668)

diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md
index 734b393..70e4f65 100644
--- a/packages/flutter_adaptive_scaffold/CHANGELOG.md
+++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.5
+
+* Calls onDestinationChanged callback in bottom nav bar.
+
 ## 0.0.4
 
 * Fix static analyzer warnings using `core` lint.
diff --git a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart
index 13ca200..0521634 100644
--- a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart
+++ b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart
@@ -212,7 +212,7 @@
   /// Option to override the drawerBreakpoint for the usage of [Drawer] over the
   /// usual [BottomNavigationBar].
   ///
-  /// Defaults to [Breakpoints.onlySmallDesktop].
+  /// Defaults to [Breakpoints.smallDesktop].
   final Breakpoint drawerBreakpoint;
 
   /// Option to override the default [AppBar] when using drawer in desktop
@@ -303,10 +303,12 @@
 
   /// Public helper method to be used for creating a [BottomNavigationBar] from
   /// a list of [NavigationDestination]s.
-  static Builder standardBottomNavigationBar(
-      {required List<NavigationDestination> destinations,
-      int currentIndex = 0,
-      double iconSize = 24}) {
+  static Builder standardBottomNavigationBar({
+    required List<NavigationDestination> destinations,
+    int currentIndex = 0,
+    double iconSize = 24,
+    ValueChanged<int>? onDestinationSelected,
+  }) {
     return Builder(
       builder: (_) {
         return BottomNavigationBar(
@@ -315,6 +317,7 @@
           items: destinations
               .map((NavigationDestination e) => _toBottomNavItem(e))
               .toList(),
+          onTap: onDestinationSelected,
         );
       },
     );
@@ -524,7 +527,10 @@
                           key: const Key('bottomNavigation'),
                           builder: (_) =>
                               AdaptiveScaffold.standardBottomNavigationBar(
-                                  destinations: widget.destinations),
+                            currentIndex: widget.selectedIndex,
+                            destinations: widget.destinations,
+                            onDestinationSelected: widget.onSelectedIndexChange,
+                          ),
                         ),
                       },
                     )
diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml
index 9de8b2c..46ab151 100644
--- a/packages/flutter_adaptive_scaffold/pubspec.yaml
+++ b/packages/flutter_adaptive_scaffold/pubspec.yaml
@@ -1,6 +1,6 @@
 name: flutter_adaptive_scaffold
 description: Widgets to easily build adaptive layouts, including navigation elements.
-version: 0.0.4
+version: 0.0.5
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22
 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold
 
diff --git a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart
index 71b081c..1b24b6b 100644
--- a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart
+++ b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart
@@ -20,7 +20,8 @@
     final Finder primaryNav = find.byKey(const Key('primaryNavigation'));
     final Finder primaryNav1 = find.byKey(const Key('primaryNavigation1'));
 
-    await tester.pumpWidget(await scaffold(width: 300, tester: tester));
+    await tester.binding.setSurfaceSize(SimulatedLayout.mobile.size);
+    await tester.pumpWidget(SimulatedLayout.mobile.app());
     await tester.pumpAndSettle();
 
     expect(smallBody, findsOneWidget);
@@ -29,10 +30,11 @@
     expect(primaryNav, findsNothing);
 
     expect(tester.getTopLeft(smallBody), Offset.zero);
-    expect(tester.getTopLeft(smallSBody), const Offset(150, 0));
+    expect(tester.getTopLeft(smallSBody), const Offset(200, 0));
     expect(tester.getTopLeft(bottomNav), const Offset(0, 744));
 
-    await tester.pumpWidget(await scaffold(width: 900, tester: tester));
+    await tester.binding.setSurfaceSize(SimulatedLayout.tablet.size);
+    await tester.pumpWidget(SimulatedLayout.tablet.app());
     await tester.pumpAndSettle();
 
     expect(smallBody, findsNothing);
@@ -43,11 +45,12 @@
     expect(primaryNav, findsOneWidget);
 
     expect(tester.getTopLeft(body), const Offset(88, 0));
-    expect(tester.getTopLeft(sBody), const Offset(450, 0));
+    expect(tester.getTopLeft(sBody), const Offset(400, 0));
     expect(tester.getTopLeft(primaryNav), Offset.zero);
     expect(tester.getBottomRight(primaryNav), const Offset(88, 800));
 
-    await tester.pumpWidget(await scaffold(width: 1100, tester: tester));
+    await tester.binding.setSurfaceSize(SimulatedLayout.desktop.size);
+    await tester.pumpWidget(SimulatedLayout.desktop.app());
     await tester.pumpAndSettle();
 
     expect(body, findsNothing);
@@ -68,8 +71,10 @@
     final Finder b = find.byKey(const Key('body'));
     final Finder sBody = find.byKey(const Key('sBody'));
 
-    await tester.pumpWidget(await scaffold(width: 400, tester: tester));
-    await tester.pumpWidget(await scaffold(width: 800, tester: tester));
+    await tester.binding.setSurfaceSize(SimulatedLayout.mobile.size);
+    await tester.pumpWidget(SimulatedLayout.mobile.app());
+    await tester.binding.setSurfaceSize(SimulatedLayout.tablet.size);
+    await tester.pumpWidget(SimulatedLayout.tablet.app());
 
     await tester.pump();
     await tester.pump(const Duration(milliseconds: 200));
@@ -107,10 +112,11 @@
     final Finder b = find.byKey(const Key('body'));
     final Finder sBody = find.byKey(const Key('sBody'));
 
-    await tester.pumpWidget(
-        await scaffold(width: 400, tester: tester, animations: false));
-    await tester.pumpWidget(
-        await scaffold(width: 800, tester: tester, animations: false));
+    await tester.binding.setSurfaceSize(SimulatedLayout.mobile.size);
+    await tester.pumpWidget(SimulatedLayout.mobile.app(animations: false));
+
+    await tester.binding.setSurfaceSize(SimulatedLayout.tablet.size);
+    await tester.pumpWidget(SimulatedLayout.tablet.app(animations: false));
 
     await tester.pump();
     await tester.pump(const Duration(milliseconds: 200));
@@ -120,6 +126,48 @@
     expect(tester.getTopLeft(sBody), const Offset(400, 0));
     expect(tester.getBottomRight(sBody), const Offset(800, 800));
   });
+
+  // The goal of this test is to run through each of the navigation elements
+  // and test whether tapping on that element will update the selected index
+  // globally
+  testWidgets('tapping navigation elements calls onSelectedIndexChange',
+      (WidgetTester tester) async {
+    // for each screen size there is a different navigational element that
+    // we want to test tapping to set the selected index
+    await Future.forEach(SimulatedLayout.values,
+        (SimulatedLayout region) async {
+      int selectedIndex = 0;
+      final MaterialApp app = region.app(initialIndex: selectedIndex);
+      await tester.binding.setSurfaceSize(region.size);
+      await tester.pumpWidget(app);
+      await tester.pumpAndSettle();
+
+      // tap on the next icon
+      selectedIndex = (selectedIndex + 1) % TestScaffold.destinations.length;
+
+      // Resolve the icon that should be found
+      final NavigationDestination destination =
+          TestScaffold.destinations[selectedIndex];
+      expect(destination.icon, isA<Icon>());
+      final Icon icon = destination.icon as Icon;
+      expect(icon.icon, isNotNull);
+
+      // Find the icon in the application to tap
+      final Widget navigationSlot =
+          tester.widget(find.byKey(Key(region.navSlotKey)));
+      final Finder target =
+          find.widgetWithIcon(navigationSlot.runtimeType, icon.icon!);
+      expect(target, findsOneWidget);
+
+      await tester.tap(target);
+      await tester.pumpAndSettle();
+
+      // Check that the state was set appropriately
+      final Finder scaffold = find.byType(TestScaffold);
+      final TestScaffoldState state = tester.state<TestScaffoldState>(scaffold);
+      expect(selectedIndex, state.index);
+    });
+  });
 }
 
 class TestBreakpoint0 extends Breakpoint {
@@ -152,34 +200,95 @@
   }
 }
 
-Future<MaterialApp> scaffold({
-  required double width,
-  required WidgetTester tester,
-  bool animations = true,
-}) async {
-  await tester.binding.setSurfaceSize(Size(width, 800));
-  return MaterialApp(
-    home: MediaQuery(
-      data: MediaQueryData(size: Size(width, 800)),
-      child: AdaptiveScaffold(
-        drawerBreakpoint: NeverOnBreakpoint(),
-        internalAnimations: animations,
-        smallBreakpoint: TestBreakpoint0(),
-        mediumBreakpoint: TestBreakpoint800(),
-        largeBreakpoint: TestBreakpoint1000(),
-        destinations: const <NavigationDestination>[
-          NavigationDestination(icon: Icon(Icons.inbox), label: 'Inbox'),
-          NavigationDestination(icon: Icon(Icons.article), label: 'Articles'),
-          NavigationDestination(icon: Icon(Icons.chat), label: 'Chat'),
-          NavigationDestination(icon: Icon(Icons.video_call), label: 'Video'),
-        ],
-        smallBody: (_) => Container(color: Colors.red),
-        body: (_) => Container(color: Colors.green),
-        largeBody: (_) => Container(color: Colors.blue),
-        smallSecondaryBody: (_) => Container(color: Colors.red),
-        secondaryBody: (_) => Container(color: Colors.green),
-        largeSecondaryBody: (_) => Container(color: Colors.blue),
-      ),
+class TestScaffold extends StatefulWidget {
+  const TestScaffold({
+    super.key,
+    this.initialIndex = 0,
+    this.isAnimated = true,
+  });
+
+  final int initialIndex;
+  final bool isAnimated;
+
+  static const List<NavigationDestination> destinations =
+      <NavigationDestination>[
+    NavigationDestination(
+      key: Key('Inbox'),
+      icon: Icon(Icons.inbox),
+      label: 'Inbox',
     ),
-  );
+    NavigationDestination(
+      key: Key('Articles'),
+      icon: Icon(Icons.article),
+      label: 'Articles',
+    ),
+    NavigationDestination(
+      key: Key('Chat'),
+      icon: Icon(Icons.chat),
+      label: 'Chat',
+    ),
+  ];
+
+  @override
+  State<TestScaffold> createState() => TestScaffoldState();
+}
+
+class TestScaffoldState extends State<TestScaffold> {
+  late int index = widget.initialIndex;
+
+  @override
+  Widget build(BuildContext context) {
+    return AdaptiveScaffold(
+      selectedIndex: index,
+      onSelectedIndexChange: (int index) {
+        setState(() {
+          this.index = index;
+        });
+      },
+      drawerBreakpoint: NeverOnBreakpoint(),
+      internalAnimations: widget.isAnimated,
+      smallBreakpoint: TestBreakpoint0(),
+      mediumBreakpoint: TestBreakpoint800(),
+      largeBreakpoint: TestBreakpoint1000(),
+      destinations: TestScaffold.destinations,
+      smallBody: (_) => Container(color: Colors.red),
+      body: (_) => Container(color: Colors.green),
+      largeBody: (_) => Container(color: Colors.blue),
+      smallSecondaryBody: (_) => Container(color: Colors.red),
+      secondaryBody: (_) => Container(color: Colors.green),
+      largeSecondaryBody: (_) => Container(color: Colors.blue),
+    );
+  }
+}
+
+enum SimulatedLayout {
+  mobile(width: 400, navSlotKey: 'bottomNavigation'),
+  tablet(width: 800, navSlotKey: 'primaryNavigation'),
+  desktop(width: 1100, navSlotKey: 'primaryNavigation1');
+
+  const SimulatedLayout({
+    required double width,
+    required this.navSlotKey,
+  }) : _width = width;
+
+  final double _width;
+  final double _height = 800;
+  final String navSlotKey;
+
+  Size get size => Size(_width, _height);
+
+  MaterialApp app({
+    int initialIndex = 0,
+    bool animations = true,
+  }) {
+    return MaterialApp(
+      home: MediaQuery(
+        data: MediaQueryData(size: size),
+        child: TestScaffold(
+          initialIndex: initialIndex,
+          isAnimated: animations,
+        ),
+      ),
+    );
+  }
 }