Unverified Commit f07db401 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

`NavigationBar` improvements (#116992)

parent f0ea3764
......@@ -6,10 +6,10 @@
import 'package:flutter/material.dart';
void main() => runApp(const ExampleApp());
void main() => runApp(const NavigationBarApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
class NavigationBarApp extends StatelessWidget {
const NavigationBarApp({super.key});
@override
Widget build(BuildContext context) {
......
This diff is collapsed.
......@@ -11,10 +11,9 @@ void main() {
testWidgets('Navigation bar updates destination on tap',
(WidgetTester tester) async {
await tester.pumpWidget(
const example.ExampleApp(),
const example.NavigationBarApp(),
);
final NavigationBar navigationBarWidget =
tester.firstWidget(find.byType(NavigationBar));
final NavigationBar navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
/// NavigationDestinations must be rendered
expect(find.text('Explore'), findsOneWidget);
......
......@@ -3,106 +3,41 @@
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter_api_samples/material/navigation_bar/navigation_bar.1.dart' as example;
import 'package:flutter_api_samples/material/navigation_bar/navigation_bar.1.dart'
as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('RootPage: only selected destination is on stage', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: example.Home()));
testWidgets('Navigation bar updates label behavior when tapping buttons',
(WidgetTester tester) async {
await tester.pumpWidget(
const example.NavigationBarApp(),
);
NavigationBar navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
const String tealTitle = 'Teal RootPage - /';
const String cyanTitle = 'Cyan RootPage - /';
const String orangeTitle = 'Orange RootPage - /';
const String blueTitle = 'Blue RootPage - /';
expect(find.text('Label behavior: alwaysShow'), findsOneWidget);
await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
/// Test alwaysShow label behavior button.
await tester.tap(find.widgetWithText(ElevatedButton, 'alwaysShow'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsOneWidget);
expect(find.text(cyanTitle), findsNothing);
expect(find.text(orangeTitle), findsNothing);
expect(find.text(blueTitle), findsNothing);
await tester.tap(find.widgetWithText(NavigationDestination, 'Cyan'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsNothing);
expect(find.text(cyanTitle), findsOneWidget);
expect(find.text(orangeTitle), findsNothing);
expect(find.text(blueTitle), findsNothing);
await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsNothing);
expect(find.text(cyanTitle), findsNothing);
expect(find.text(orangeTitle), findsOneWidget);
expect(find.text(blueTitle), findsNothing);
await tester.tap(find.widgetWithText(NavigationDestination, 'Blue'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsNothing);
expect(find.text(cyanTitle), findsNothing);
expect(find.text(orangeTitle), findsNothing);
expect(find.text(blueTitle), findsOneWidget);
});
testWidgets('RootPage', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: example.Home()));
await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
await tester.pumpAndSettle();
await tester.tap(find.text('Local Dialog'));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsNothing);
await tester.pumpAndSettle();
await tester.tap(find.text('Root Dialog'));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsOneWidget);
await tester.tapAt(const Offset(5, 5));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsNothing);
await tester.tap(find.text('Local BottomSheet'));
await tester.pumpAndSettle();
expect(find.byType(BottomSheet), findsOneWidget);
await tester.tap(find.byType(BackButton));
await tester.pumpAndSettle();
expect(find.byType(BottomSheet), findsNothing);
await tester.tap(find.text('Push /list'));
await tester.pumpAndSettle();
expect(find.text('Teal ListPage - /list'), findsOneWidget);
});
expect(find.text('Label behavior: alwaysShow'), findsOneWidget);
expect(navigationBarWidget.labelBehavior, NavigationDestinationLabelBehavior.alwaysShow);
testWidgets('ListPage', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: example.Home()));
expect(find.text('Teal RootPage - /'), findsOneWidget);
await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
/// Test onlyShowSelected label behavior button.
await tester.tap(find.widgetWithText(ElevatedButton, 'onlyShowSelected'));
await tester.pumpAndSettle();
expect(find.text('Teal ListPage - /list'), findsOneWidget);
expect(find.text('Push /text [0]'), findsOneWidget);
await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
await tester.pumpAndSettle();
expect(find.text('Orange ListPage - /list'), findsOneWidget);
expect(find.text('Push /text [0]'), findsOneWidget);
expect(find.text('Label behavior: onlyShowSelected'), findsOneWidget);
navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
expect(navigationBarWidget.labelBehavior, NavigationDestinationLabelBehavior.onlyShowSelected);
await tester.tap(find.byType(BackButton));
/// Test alwaysHide label behavior button.
await tester.tap(find.widgetWithText(ElevatedButton, 'alwaysHide'));
await tester.pumpAndSettle();
expect(find.text('Orange RootPage - /'), findsOneWidget);
await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
await tester.pumpAndSettle();
expect(find.text('Teal ListPage - /list'), findsOneWidget);
await tester.tap(find.byType(BackButton));
await tester.pumpAndSettle();
expect(find.text('Teal RootPage - /'), findsOneWidget);
expect(find.text('Label behavior: alwaysHide'), findsOneWidget);
navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
expect(navigationBarWidget.labelBehavior, NavigationDestinationLabelBehavior.alwaysHide);
});
}
// 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/navigation_bar/navigation_bar.2.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('RootPage: only selected destination is on stage', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: example.Home()));
const String tealTitle = 'Teal RootPage - /';
const String cyanTitle = 'Cyan RootPage - /';
const String orangeTitle = 'Orange RootPage - /';
const String blueTitle = 'Blue RootPage - /';
await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsOneWidget);
expect(find.text(cyanTitle), findsNothing);
expect(find.text(orangeTitle), findsNothing);
expect(find.text(blueTitle), findsNothing);
await tester.tap(find.widgetWithText(NavigationDestination, 'Cyan'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsNothing);
expect(find.text(cyanTitle), findsOneWidget);
expect(find.text(orangeTitle), findsNothing);
expect(find.text(blueTitle), findsNothing);
await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsNothing);
expect(find.text(cyanTitle), findsNothing);
expect(find.text(orangeTitle), findsOneWidget);
expect(find.text(blueTitle), findsNothing);
await tester.tap(find.widgetWithText(NavigationDestination, 'Blue'));
await tester.pumpAndSettle();
expect(find.text(tealTitle), findsNothing);
expect(find.text(cyanTitle), findsNothing);
expect(find.text(orangeTitle), findsNothing);
expect(find.text(blueTitle), findsOneWidget);
});
testWidgets('RootPage', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: example.Home()));
await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
await tester.pumpAndSettle();
await tester.tap(find.text('Local Dialog'));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsOneWidget);
await tester.tap(find.text('OK'));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsNothing);
await tester.pumpAndSettle();
await tester.tap(find.text('Root Dialog'));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsOneWidget);
await tester.tapAt(const Offset(5, 5));
await tester.pumpAndSettle();
expect(find.text('Teal AlertDialog'), findsNothing);
await tester.tap(find.text('Local BottomSheet'));
await tester.pumpAndSettle();
expect(find.byType(BottomSheet), findsOneWidget);
await tester.tap(find.byType(BackButton));
await tester.pumpAndSettle();
expect(find.byType(BottomSheet), findsNothing);
await tester.tap(find.text('Push /list'));
await tester.pumpAndSettle();
expect(find.text('Teal ListPage - /list'), findsOneWidget);
});
testWidgets('ListPage', (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: example.Home()));
expect(find.text('Teal RootPage - /'), findsOneWidget);
await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
await tester.pumpAndSettle();
expect(find.text('Teal ListPage - /list'), findsOneWidget);
expect(find.text('Push /text [0]'), findsOneWidget);
await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
await tester.pumpAndSettle();
expect(find.text('Orange ListPage - /list'), findsOneWidget);
expect(find.text('Push /text [0]'), findsOneWidget);
await tester.tap(find.byType(BackButton));
await tester.pumpAndSettle();
expect(find.text('Orange RootPage - /'), findsOneWidget);
await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
await tester.pumpAndSettle();
expect(find.text('Teal ListPage - /list'), findsOneWidget);
await tester.tap(find.byType(BackButton));
await tester.pumpAndSettle();
expect(find.text('Teal RootPage - /'), findsOneWidget);
});
}
......@@ -53,6 +53,14 @@ const double _kIndicatorWidth = 64;
/// {@end-tool}
///
/// {@tool dartpad}
/// This example showcases [NavigationBar] label behaviors. When tapping on one
/// of the label behavior options, the [labelBehavior] of the [NavigationBar]
/// will be updated.
///
/// ** See code in examples/api/lib/material/navigation_bar/navigation_bar.1.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This example shows a [NavigationBar] as it is used within a [Scaffold]
/// widget when there are nested navigators that provide local navigation. The
/// [NavigationBar] has four [NavigationDestination] widgets with different
......@@ -60,7 +68,7 @@ const double _kIndicatorWidth = 64;
/// item's index and displays a corresponding page with its own local navigator
/// in the body of a [Scaffold].
///
/// ** See code in examples/api/lib/material/navigation_bar/navigation_bar.1.dart **
/// ** See code in examples/api/lib/material/navigation_bar/navigation_bar.2.dart **
/// {@end-tool}
/// See also:
///
......@@ -266,6 +274,8 @@ class NavigationDestination extends StatelessWidget {
super.key,
required this.icon,
this.selectedIcon,
this.indicatorColor,
this.indicatorShape,
required this.label,
this.tooltip,
});
......@@ -290,6 +300,12 @@ class NavigationDestination extends StatelessWidget {
/// would use a size of 24.0 and [ColorScheme.onSurface].
final Widget? selectedIcon;
/// The color of the [indicatorShape] when this destination is selected.
final Color? indicatorColor;
/// The shape of the selected inidicator.
final ShapeBorder? indicatorShape;
/// The text label that appears below the icon of this
/// [NavigationDestination].
///
......@@ -335,8 +351,8 @@ class NavigationDestination extends StatelessWidget {
children: <Widget>[
NavigationIndicator(
animation: animation,
color: navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
shape: navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
color: indicatorColor ?? navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
shape: indicatorShape ?? navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
),
_StatusTransitionWidgetBuilder(
animation: animation,
......@@ -440,10 +456,10 @@ class _NavigationDestinationBuilder extends StatelessWidget {
final double labelPadding;
switch (info.labelBehavior) {
case NavigationDestinationLabelBehavior.alwaysShow:
labelPadding = 10;
labelPadding = 8;
break;
case NavigationDestinationLabelBehavior.onlyShowSelected:
labelPadding = selected ? 10 : 0;
labelPadding = selected ? 8 : 0;
break;
case NavigationDestinationLabelBehavior.alwaysHide:
labelPadding = 0;
......
......@@ -589,7 +589,7 @@ void main() {
await tester.pumpAndSettle();
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
Offset indicatorCenter = const Offset(600, 30);
Offset indicatorCenter = const Offset(600, 32);
const Size includedIndicatorSize = Size(64, 32);
const Size excludedIndicatorSize = Size(74, 40);
......@@ -715,7 +715,7 @@ void main() {
selectedIndex = 1;
await tester.pumpWidget(buildWidget(labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected));
await tester.pumpAndSettle();
indicatorCenter = const Offset(600, 30);
indicatorCenter = const Offset(600, 32);
expect(
inkFeatures,
......@@ -803,6 +803,96 @@ void main() {
transform = tester.widget<Transform>(transformFinder).transform;
expect(transform.getColumn(0)[0], 1.0);
});
testWidgets('Navigation destination updates indicator color and shape', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true);
const Color color = Color(0xff0000ff);
const ShapeBorder shape = CircleBorder();
Widget buildNaviagationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
return MaterialApp(
theme: theme,
home: Scaffold(
bottomNavigationBar: NavigationBar(
destinations: <Widget>[
NavigationDestination(
icon: const Icon(Icons.ac_unit),
label: 'AC',
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
),
const NavigationDestination(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
onDestinationSelected: (int i) { },
),
),
);
}
await tester.pumpWidget(buildNaviagationBar());
// Test default indicator color and shape.
expect(_indicator(tester)?.color, theme.colorScheme.secondaryContainer);
expect(_indicator(tester)?.shape, const StadiumBorder());
await tester.pumpWidget(buildNaviagationBar(indicatorColor: color, indicatorShape: shape));
// Test custom indicator color and shape.
expect(_indicator(tester)?.color, color);
expect(_indicator(tester)?.shape, shape);
});
group('Material 2', () {
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
// is turned on by default, these tests can be removed.
testWidgets('Navigation destination updates indicator color and shape', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
const Color color = Color(0xff0000ff);
const ShapeBorder shape = CircleBorder();
Widget buildNaviagationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
return MaterialApp(
theme: theme,
home: Scaffold(
bottomNavigationBar: NavigationBar(
destinations: <Widget>[
NavigationDestination(
icon: const Icon(Icons.ac_unit),
label: 'AC',
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
),
const NavigationDestination(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
onDestinationSelected: (int i) { },
),
),
);
}
await tester.pumpWidget(buildNaviagationBar());
// Test default indicator color and shape.
expect(_indicator(tester)?.color, theme.colorScheme.secondary.withOpacity(0.24));
expect(
_indicator(tester)?.shape,
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
);
await tester.pumpWidget(buildNaviagationBar(indicatorColor: color, indicatorShape: shape));
// Test custom indicator color and shape.
expect(_indicator(tester)?.color, color);
expect(_indicator(tester)?.shape, shape);
});
});
}
Widget _buildWidget(Widget child) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment