diff --git a/examples/api/lib/material/bottom_navigation_bar/bottom_navigation_bar.2.dart b/examples/api/lib/material/bottom_navigation_bar/bottom_navigation_bar.2.dart new file mode 100644 index 00000000000..ee5ebed3e5b --- /dev/null +++ b/examples/api/lib/material/bottom_navigation_bar/bottom_navigation_bar.2.dart @@ -0,0 +1,114 @@ +// Copyright 2014 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. + +// Flutter code sample for BottomNavigationBar + +import 'package:flutter/material.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + static const String _title = 'Flutter Code Sample'; + + @override + Widget build(BuildContext context) { + return const MaterialApp( + title: _title, + home: MyStatefulWidget(), + ); + } +} + +class MyStatefulWidget extends StatefulWidget { + const MyStatefulWidget({super.key}); + + @override + State createState() => _MyStatefulWidgetState(); +} + +class _MyStatefulWidgetState extends State { + int _selectedIndex = 0; + final ScrollController _homeController = ScrollController(); + + Widget _listViewBody() { + return ListView.separated( + controller: _homeController, + itemBuilder: (BuildContext context, int index) { + return Center( + child: Text( + 'Item $index', + ), + ); + }, + separatorBuilder: (BuildContext context, int index) => const Divider( + thickness: 1, + ), + itemCount: 50); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('BottomNavigationBar Sample'), + ), + body: _listViewBody(), + bottomNavigationBar: BottomNavigationBar( + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.open_in_new_rounded), + label: 'Open Dialog', + ), + ], + currentIndex: _selectedIndex, + selectedItemColor: Colors.amber[800], + onTap: (int index) { + switch (index) { + case 0: + // only scroll to top when current index is selected. + if (_selectedIndex == index) { + _homeController.animateTo( + 0.0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeOut, + ); + } + break; + case 1: + showModal(context); + break; + } + setState( + () { + _selectedIndex = index; + }, + ); + }, + ), + ); + } + + void showModal(BuildContext context) { + showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + content: const Text('Example Dialog'), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('Close'), + ) + ], + ), + ); + } +} diff --git a/examples/api/test/material/bottom_navigation_bar/bottom_navigation_bar.2.test.dart b/examples/api/test/material/bottom_navigation_bar/bottom_navigation_bar.2.test.dart new file mode 100644 index 00000000000..9d7788a91c6 --- /dev/null +++ b/examples/api/test/material/bottom_navigation_bar/bottom_navigation_bar.2.test.dart @@ -0,0 +1,38 @@ +// Copyright 2014 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/material.dart'; +import 'package:flutter_api_samples/material/bottom_navigation_bar/bottom_navigation_bar.2.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('BottomNavigationBar Updates Screen Content', + (WidgetTester tester) async { + await tester.pumpWidget( + const example.MyApp(), + ); + + expect(find.widgetWithText(AppBar, 'BottomNavigationBar Sample'), + findsOneWidget); + expect(find.byType(BottomNavigationBar), findsOneWidget); + expect(find.widgetWithText(Center, 'Item 0'), findsOneWidget); + + await tester.scrollUntilVisible( + find.widgetWithText(Center, 'Item 49'), 100); + await tester.pumpAndSettle(); + expect(find.widgetWithText(Center, 'Item 49'), findsOneWidget); + + await tester.tap(find.byIcon(Icons.home)); + await tester.tap(find.byIcon(Icons.home)); + await tester.pumpAndSettle(); + + final Scrollable bodyScrollView = tester.widget(find.byType(Scrollable)); + expect(bodyScrollView.controller?.offset, 0.0); + + await tester.tap(find.byIcon(Icons.open_in_new_rounded)); + await tester.pumpAndSettle(); + expect(find.byType(AlertDialog), findsOneWidget); + }); +} diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 4880f8237cc..82e05b756bf 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -118,6 +118,15 @@ enum BottomNavigationBarLandscapeLayout { /// /// ** See code in examples/api/lib/material/bottom_navigation_bar/bottom_navigation_bar.1.dart ** /// {@end-tool} +/// +/// {@tool dartpad} +/// This example shows [BottomNavigationBar] used in a [Scaffold] Widget with +/// different interaction patterns. Tapping twice on the first [BottomNavigationBarItem] +/// uses the [ScrollController] to animate the [ListView] to the top. The second +/// [BottomNavigationBarItem] shows a Modal Dialog. +/// +/// ** See code in examples/api/lib/material/bottom_navigation_bar/bottom_navigation_bar.2.dart ** +/// {@end-tool} /// See also: /// /// * [BottomNavigationBarItem]