add navigation bar component (#83047)
This commit is contained in:
parent
61bce1d83b
commit
f3049c7762
@ -99,6 +99,8 @@ export 'src/material/material_localizations.dart';
|
|||||||
export 'src/material/material_state.dart';
|
export 'src/material/material_state.dart';
|
||||||
export 'src/material/material_state_mixin.dart';
|
export 'src/material/material_state_mixin.dart';
|
||||||
export 'src/material/mergeable_material.dart';
|
export 'src/material/mergeable_material.dart';
|
||||||
|
export 'src/material/navigation_bar.dart';
|
||||||
|
export 'src/material/navigation_bar_theme.dart';
|
||||||
export 'src/material/navigation_rail.dart';
|
export 'src/material/navigation_rail.dart';
|
||||||
export 'src/material/navigation_rail_theme.dart';
|
export 'src/material/navigation_rail_theme.dart';
|
||||||
export 'src/material/no_splash.dart';
|
export 'src/material/no_splash.dart';
|
||||||
|
1164
packages/flutter/lib/src/material/navigation_bar.dart
Normal file
1164
packages/flutter/lib/src/material/navigation_bar.dart
Normal file
File diff suppressed because it is too large
Load Diff
228
packages/flutter/lib/src/material/navigation_bar_theme.dart
Normal file
228
packages/flutter/lib/src/material/navigation_bar_theme.dart
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
// 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 'dart:ui' show lerpDouble;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'material_state.dart';
|
||||||
|
import 'navigation_bar.dart';
|
||||||
|
import 'theme.dart';
|
||||||
|
|
||||||
|
/// Defines default property values for descendant [NavigationBar]
|
||||||
|
/// widgets.
|
||||||
|
///
|
||||||
|
/// Descendant widgets obtain the current [NavigationBarThemeData] object
|
||||||
|
/// using `NavigationBarTheme.of(context)`. Instances of
|
||||||
|
/// [NavigationBarThemeData] can be customized with
|
||||||
|
/// [NavigationBarThemeData.copyWith].
|
||||||
|
///
|
||||||
|
/// Typically a [NavigationBarThemeData] is specified as part of the
|
||||||
|
/// overall [Theme] with [ThemeData.navigationBarTheme]. Alternatively, a
|
||||||
|
/// [NavigationBarTheme] inherited widget can be used to theme [NavigationBar]s
|
||||||
|
/// in a subtree of widgets.
|
||||||
|
///
|
||||||
|
/// All [NavigationBarThemeData] properties are `null` by default.
|
||||||
|
/// When null, the [NavigationBar] will provide its own defaults based on the
|
||||||
|
/// overall [Theme]'s textTheme and colorScheme. See the individual
|
||||||
|
/// [NavigationBar] properties for details.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData], which describes the overall theme information for the
|
||||||
|
/// application.
|
||||||
|
@immutable
|
||||||
|
class NavigationBarThemeData with Diagnosticable {
|
||||||
|
/// Creates a theme that can be used for [ThemeData.navigationBarTheme] and
|
||||||
|
/// [NavigationBarTheme].
|
||||||
|
const NavigationBarThemeData({
|
||||||
|
this.height,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.indicatorColor,
|
||||||
|
this.labelTextStyle,
|
||||||
|
this.iconTheme,
|
||||||
|
this.labelBehavior,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Overrides the default value of [NavigationBar.height].
|
||||||
|
final double? height;
|
||||||
|
|
||||||
|
/// Overrides the default value of [NavigationBar.backgroundColor].
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// Overrides the default value of [NavigationBar]'s selection indicator.
|
||||||
|
final Color? indicatorColor;
|
||||||
|
|
||||||
|
/// The style to merge with the default text style for
|
||||||
|
/// [NavigationDestination] labels.
|
||||||
|
///
|
||||||
|
/// You can use this to specify a different style when the label is selected.
|
||||||
|
final MaterialStateProperty<TextStyle?>? labelTextStyle;
|
||||||
|
|
||||||
|
/// The theme to merge with the default icon theme for
|
||||||
|
/// [NavigationDestination] icons.
|
||||||
|
///
|
||||||
|
/// You can use this to specify a different icon theme when the icon is
|
||||||
|
/// selected.
|
||||||
|
final MaterialStateProperty<IconThemeData?>? iconTheme;
|
||||||
|
|
||||||
|
/// Overrides the default value of [NavigationBar.labelBehavior].
|
||||||
|
final NavigationDestinationLabelBehavior? labelBehavior;
|
||||||
|
|
||||||
|
/// Creates a copy of this object with the given fields replaced with the
|
||||||
|
/// new values.
|
||||||
|
NavigationBarThemeData copyWith({
|
||||||
|
double? height,
|
||||||
|
Color? backgroundColor,
|
||||||
|
Color? indicatorColor,
|
||||||
|
MaterialStateProperty<TextStyle?>? labelTextStyle,
|
||||||
|
MaterialStateProperty<IconThemeData?>? iconTheme,
|
||||||
|
NavigationDestinationLabelBehavior? labelBehavior,
|
||||||
|
}) {
|
||||||
|
return NavigationBarThemeData(
|
||||||
|
height: height ?? this.height,
|
||||||
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||||
|
indicatorColor: indicatorColor ?? this.indicatorColor,
|
||||||
|
labelTextStyle: labelTextStyle ?? this.labelTextStyle,
|
||||||
|
iconTheme: iconTheme ?? this.iconTheme,
|
||||||
|
labelBehavior: labelBehavior ?? this.labelBehavior,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Linearly interpolate between two navigation rail themes.
|
||||||
|
///
|
||||||
|
/// If both arguments are null then null is returned.
|
||||||
|
///
|
||||||
|
/// {@macro dart.ui.shadow.lerp}
|
||||||
|
static NavigationBarThemeData? lerp(NavigationBarThemeData? a, NavigationBarThemeData? b, double t) {
|
||||||
|
assert(t != null);
|
||||||
|
if (a == null && b == null)
|
||||||
|
return null;
|
||||||
|
return NavigationBarThemeData(
|
||||||
|
height: lerpDouble(a?.height, b?.height, t),
|
||||||
|
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
|
||||||
|
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
|
||||||
|
labelTextStyle: _lerpProperties<TextStyle?>(a?.labelTextStyle, b?.labelTextStyle, t, TextStyle.lerp),
|
||||||
|
iconTheme: _lerpProperties<IconThemeData?>(a?.iconTheme, b?.iconTheme, t, IconThemeData.lerp),
|
||||||
|
labelBehavior: t < 0.5 ? a?.labelBehavior : b?.labelBehavior,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return hashValues(
|
||||||
|
height,
|
||||||
|
backgroundColor,
|
||||||
|
indicatorColor,
|
||||||
|
labelTextStyle,
|
||||||
|
iconTheme,
|
||||||
|
labelBehavior,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other))
|
||||||
|
return true;
|
||||||
|
if (other.runtimeType != runtimeType)
|
||||||
|
return false;
|
||||||
|
return other is NavigationBarThemeData
|
||||||
|
&& other.height == height
|
||||||
|
&& other.backgroundColor == backgroundColor
|
||||||
|
&& other.indicatorColor == indicatorColor
|
||||||
|
&& other.labelTextStyle == labelTextStyle
|
||||||
|
&& other.iconTheme == iconTheme
|
||||||
|
&& other.labelBehavior == labelBehavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(DoubleProperty('height', height, defaultValue: null));
|
||||||
|
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
|
||||||
|
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('labelTextStyle', labelTextStyle, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<MaterialStateProperty<IconThemeData?>>('iconTheme', iconTheme, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<NavigationDestinationLabelBehavior>('labelBehavior', labelBehavior, defaultValue: null));
|
||||||
|
}
|
||||||
|
|
||||||
|
static MaterialStateProperty<T>? _lerpProperties<T>(
|
||||||
|
MaterialStateProperty<T>? a,
|
||||||
|
MaterialStateProperty<T>? b,
|
||||||
|
double t,
|
||||||
|
T Function(T?, T?, double) lerpFunction,
|
||||||
|
) {
|
||||||
|
// Avoid creating a _LerpProperties object for a common case.
|
||||||
|
if (a == null && b == null)
|
||||||
|
return null;
|
||||||
|
return _LerpProperties<T>(a, b, t, lerpFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LerpProperties<T> implements MaterialStateProperty<T> {
|
||||||
|
const _LerpProperties(this.a, this.b, this.t, this.lerpFunction);
|
||||||
|
|
||||||
|
final MaterialStateProperty<T>? a;
|
||||||
|
final MaterialStateProperty<T>? b;
|
||||||
|
final double t;
|
||||||
|
final T Function(T?, T?, double) lerpFunction;
|
||||||
|
|
||||||
|
@override
|
||||||
|
T resolve(Set<MaterialState> states) {
|
||||||
|
final T? resolvedA = a?.resolve(states);
|
||||||
|
final T? resolvedB = b?.resolve(states);
|
||||||
|
return lerpFunction(resolvedA, resolvedB, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An inherited widget that defines visual properties for [NavigationBar]s and
|
||||||
|
/// [NavigationDestination]s in this widget's subtree.
|
||||||
|
///
|
||||||
|
/// Values specified here are used for [NavigationBar] properties that are not
|
||||||
|
/// given an explicit non-null value.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [ThemeData.navigationBarTheme], which describes the
|
||||||
|
/// [NavigationBarThemeData] in the overall theme for the application.
|
||||||
|
class NavigationBarTheme extends InheritedTheme {
|
||||||
|
/// Creates a navigation rail theme that controls the
|
||||||
|
/// [NavigationBarThemeData] properties for a [NavigationBar].
|
||||||
|
///
|
||||||
|
/// The data argument must not be null.
|
||||||
|
const NavigationBarTheme({
|
||||||
|
Key? key,
|
||||||
|
required this.data,
|
||||||
|
required Widget child,
|
||||||
|
}) : assert(data != null), super(key: key, child: child);
|
||||||
|
|
||||||
|
/// Specifies the background color, label text style, icon theme, and label
|
||||||
|
/// type values for descendant [NavigationBar] widgets.
|
||||||
|
final NavigationBarThemeData data;
|
||||||
|
|
||||||
|
/// The closest instance of this class that encloses the given context.
|
||||||
|
///
|
||||||
|
/// If there is no enclosing [NavigationBarTheme] widget, then
|
||||||
|
/// [ThemeData.navigationBarTheme] is used.
|
||||||
|
///
|
||||||
|
/// Typical usage is as follows:
|
||||||
|
///
|
||||||
|
/// ```dart
|
||||||
|
/// NavigationBarTheme theme = NavigationBarTheme.of(context);
|
||||||
|
/// ```
|
||||||
|
static NavigationBarThemeData of(BuildContext context) {
|
||||||
|
final NavigationBarTheme? navigationBarTheme = context.dependOnInheritedWidgetOfExactType<NavigationBarTheme>();
|
||||||
|
return navigationBarTheme?.data ?? Theme.of(context).navigationBarTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget wrap(BuildContext context, Widget child) {
|
||||||
|
return NavigationBarTheme(data: data, child: child);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateShouldNotify(NavigationBarTheme oldWidget) => data != oldWidget.data;
|
||||||
|
}
|
@ -28,6 +28,7 @@ import 'floating_action_button_theme.dart';
|
|||||||
import 'ink_splash.dart';
|
import 'ink_splash.dart';
|
||||||
import 'ink_well.dart' show InteractiveInkFeatureFactory;
|
import 'ink_well.dart' show InteractiveInkFeatureFactory;
|
||||||
import 'input_decorator.dart';
|
import 'input_decorator.dart';
|
||||||
|
import 'navigation_bar_theme.dart';
|
||||||
import 'navigation_rail_theme.dart';
|
import 'navigation_rail_theme.dart';
|
||||||
import 'outlined_button_theme.dart';
|
import 'outlined_button_theme.dart';
|
||||||
import 'page_transitions_theme.dart';
|
import 'page_transitions_theme.dart';
|
||||||
@ -317,6 +318,7 @@ class ThemeData with Diagnosticable {
|
|||||||
ColorScheme? colorScheme,
|
ColorScheme? colorScheme,
|
||||||
DialogTheme? dialogTheme,
|
DialogTheme? dialogTheme,
|
||||||
FloatingActionButtonThemeData? floatingActionButtonTheme,
|
FloatingActionButtonThemeData? floatingActionButtonTheme,
|
||||||
|
NavigationBarThemeData? navigationBarTheme,
|
||||||
NavigationRailThemeData? navigationRailTheme,
|
NavigationRailThemeData? navigationRailTheme,
|
||||||
Typography? typography,
|
Typography? typography,
|
||||||
NoDefaultCupertinoThemeData? cupertinoOverrideTheme,
|
NoDefaultCupertinoThemeData? cupertinoOverrideTheme,
|
||||||
@ -462,6 +464,7 @@ class ThemeData with Diagnosticable {
|
|||||||
);
|
);
|
||||||
dialogTheme ??= const DialogTheme();
|
dialogTheme ??= const DialogTheme();
|
||||||
floatingActionButtonTheme ??= const FloatingActionButtonThemeData();
|
floatingActionButtonTheme ??= const FloatingActionButtonThemeData();
|
||||||
|
navigationBarTheme ??= const NavigationBarThemeData();
|
||||||
navigationRailTheme ??= const NavigationRailThemeData();
|
navigationRailTheme ??= const NavigationRailThemeData();
|
||||||
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault();
|
||||||
snackBarTheme ??= const SnackBarThemeData();
|
snackBarTheme ??= const SnackBarThemeData();
|
||||||
@ -543,6 +546,7 @@ class ThemeData with Diagnosticable {
|
|||||||
colorScheme: colorScheme,
|
colorScheme: colorScheme,
|
||||||
dialogTheme: dialogTheme,
|
dialogTheme: dialogTheme,
|
||||||
floatingActionButtonTheme: floatingActionButtonTheme,
|
floatingActionButtonTheme: floatingActionButtonTheme,
|
||||||
|
navigationBarTheme: navigationBarTheme,
|
||||||
navigationRailTheme: navigationRailTheme,
|
navigationRailTheme: navigationRailTheme,
|
||||||
typography: typography,
|
typography: typography,
|
||||||
cupertinoOverrideTheme: cupertinoOverrideTheme,
|
cupertinoOverrideTheme: cupertinoOverrideTheme,
|
||||||
@ -677,6 +681,7 @@ class ThemeData with Diagnosticable {
|
|||||||
required this.colorScheme,
|
required this.colorScheme,
|
||||||
required this.dialogTheme,
|
required this.dialogTheme,
|
||||||
required this.floatingActionButtonTheme,
|
required this.floatingActionButtonTheme,
|
||||||
|
required this.navigationBarTheme,
|
||||||
required this.navigationRailTheme,
|
required this.navigationRailTheme,
|
||||||
required this.typography,
|
required this.typography,
|
||||||
required this.cupertinoOverrideTheme,
|
required this.cupertinoOverrideTheme,
|
||||||
@ -763,6 +768,7 @@ class ThemeData with Diagnosticable {
|
|||||||
assert(colorScheme != null),
|
assert(colorScheme != null),
|
||||||
assert(dialogTheme != null),
|
assert(dialogTheme != null),
|
||||||
assert(floatingActionButtonTheme != null),
|
assert(floatingActionButtonTheme != null),
|
||||||
|
assert(navigationBarTheme != null),
|
||||||
assert(navigationRailTheme != null),
|
assert(navigationRailTheme != null),
|
||||||
assert(typography != null),
|
assert(typography != null),
|
||||||
assert(snackBarTheme != null),
|
assert(snackBarTheme != null),
|
||||||
@ -1269,6 +1275,10 @@ class ThemeData with Diagnosticable {
|
|||||||
/// [FloatingActionButton].
|
/// [FloatingActionButton].
|
||||||
final FloatingActionButtonThemeData floatingActionButtonTheme;
|
final FloatingActionButtonThemeData floatingActionButtonTheme;
|
||||||
|
|
||||||
|
/// A theme for customizing the background color, text style, and icon themes
|
||||||
|
/// of a [NavigationBar].
|
||||||
|
final NavigationBarThemeData navigationBarTheme;
|
||||||
|
|
||||||
/// A theme for customizing the background color, elevation, text style, and
|
/// A theme for customizing the background color, elevation, text style, and
|
||||||
/// icon themes of a [NavigationRail].
|
/// icon themes of a [NavigationRail].
|
||||||
final NavigationRailThemeData navigationRailTheme;
|
final NavigationRailThemeData navigationRailTheme;
|
||||||
@ -1485,6 +1495,7 @@ class ThemeData with Diagnosticable {
|
|||||||
ColorScheme? colorScheme,
|
ColorScheme? colorScheme,
|
||||||
DialogTheme? dialogTheme,
|
DialogTheme? dialogTheme,
|
||||||
FloatingActionButtonThemeData? floatingActionButtonTheme,
|
FloatingActionButtonThemeData? floatingActionButtonTheme,
|
||||||
|
NavigationBarThemeData? navigationBarTheme,
|
||||||
NavigationRailThemeData? navigationRailTheme,
|
NavigationRailThemeData? navigationRailTheme,
|
||||||
Typography? typography,
|
Typography? typography,
|
||||||
NoDefaultCupertinoThemeData? cupertinoOverrideTheme,
|
NoDefaultCupertinoThemeData? cupertinoOverrideTheme,
|
||||||
@ -1576,6 +1587,7 @@ class ThemeData with Diagnosticable {
|
|||||||
colorScheme: (colorScheme ?? this.colorScheme).copyWith(brightness: brightness),
|
colorScheme: (colorScheme ?? this.colorScheme).copyWith(brightness: brightness),
|
||||||
dialogTheme: dialogTheme ?? this.dialogTheme,
|
dialogTheme: dialogTheme ?? this.dialogTheme,
|
||||||
floatingActionButtonTheme: floatingActionButtonTheme ?? this.floatingActionButtonTheme,
|
floatingActionButtonTheme: floatingActionButtonTheme ?? this.floatingActionButtonTheme,
|
||||||
|
navigationBarTheme: navigationBarTheme ?? this.navigationBarTheme,
|
||||||
navigationRailTheme: navigationRailTheme ?? this.navigationRailTheme,
|
navigationRailTheme: navigationRailTheme ?? this.navigationRailTheme,
|
||||||
typography: typography ?? this.typography,
|
typography: typography ?? this.typography,
|
||||||
cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme,
|
cupertinoOverrideTheme: cupertinoOverrideTheme ?? this.cupertinoOverrideTheme,
|
||||||
@ -1737,6 +1749,7 @@ class ThemeData with Diagnosticable {
|
|||||||
colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t),
|
colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t),
|
||||||
dialogTheme: DialogTheme.lerp(a.dialogTheme, b.dialogTheme, t),
|
dialogTheme: DialogTheme.lerp(a.dialogTheme, b.dialogTheme, t),
|
||||||
floatingActionButtonTheme: FloatingActionButtonThemeData.lerp(a.floatingActionButtonTheme, b.floatingActionButtonTheme, t)!,
|
floatingActionButtonTheme: FloatingActionButtonThemeData.lerp(a.floatingActionButtonTheme, b.floatingActionButtonTheme, t)!,
|
||||||
|
navigationBarTheme: NavigationBarThemeData.lerp(a.navigationBarTheme, b.navigationBarTheme, t)!,
|
||||||
navigationRailTheme: NavigationRailThemeData.lerp(a.navigationRailTheme, b.navigationRailTheme, t)!,
|
navigationRailTheme: NavigationRailThemeData.lerp(a.navigationRailTheme, b.navigationRailTheme, t)!,
|
||||||
typography: Typography.lerp(a.typography, b.typography, t),
|
typography: Typography.lerp(a.typography, b.typography, t),
|
||||||
cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme,
|
cupertinoOverrideTheme: t < 0.5 ? a.cupertinoOverrideTheme : b.cupertinoOverrideTheme,
|
||||||
@ -1826,6 +1839,7 @@ class ThemeData with Diagnosticable {
|
|||||||
&& other.colorScheme == colorScheme
|
&& other.colorScheme == colorScheme
|
||||||
&& other.dialogTheme == dialogTheme
|
&& other.dialogTheme == dialogTheme
|
||||||
&& other.floatingActionButtonTheme == floatingActionButtonTheme
|
&& other.floatingActionButtonTheme == floatingActionButtonTheme
|
||||||
|
&& other.navigationBarTheme == navigationBarTheme
|
||||||
&& other.navigationRailTheme == navigationRailTheme
|
&& other.navigationRailTheme == navigationRailTheme
|
||||||
&& other.typography == typography
|
&& other.typography == typography
|
||||||
&& other.cupertinoOverrideTheme == cupertinoOverrideTheme
|
&& other.cupertinoOverrideTheme == cupertinoOverrideTheme
|
||||||
@ -1914,6 +1928,7 @@ class ThemeData with Diagnosticable {
|
|||||||
colorScheme,
|
colorScheme,
|
||||||
dialogTheme,
|
dialogTheme,
|
||||||
floatingActionButtonTheme,
|
floatingActionButtonTheme,
|
||||||
|
navigationBarTheme,
|
||||||
navigationRailTheme,
|
navigationRailTheme,
|
||||||
typography,
|
typography,
|
||||||
cupertinoOverrideTheme,
|
cupertinoOverrideTheme,
|
||||||
@ -1999,6 +2014,7 @@ class ThemeData with Diagnosticable {
|
|||||||
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultData.colorScheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<ColorScheme>('colorScheme', colorScheme, defaultValue: defaultData.colorScheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<DialogTheme>('dialogTheme', dialogTheme, defaultValue: defaultData.dialogTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<DialogTheme>('dialogTheme', dialogTheme, defaultValue: defaultData.dialogTheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<FloatingActionButtonThemeData>('floatingActionButtonThemeData', floatingActionButtonTheme, defaultValue: defaultData.floatingActionButtonTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<FloatingActionButtonThemeData>('floatingActionButtonThemeData', floatingActionButtonTheme, defaultValue: defaultData.floatingActionButtonTheme, level: DiagnosticLevel.debug));
|
||||||
|
properties.add(DiagnosticsProperty<NavigationBarThemeData>('navigationBarTheme', navigationBarTheme, defaultValue: defaultData.navigationBarTheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<NavigationRailThemeData>('navigationRailThemeData', navigationRailTheme, defaultValue: defaultData.navigationRailTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<NavigationRailThemeData>('navigationRailThemeData', navigationRailTheme, defaultValue: defaultData.navigationRailTheme, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<Typography>('typography', typography, defaultValue: defaultData.typography, level: DiagnosticLevel.debug));
|
||||||
properties.add(DiagnosticsProperty<NoDefaultCupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme, level: DiagnosticLevel.debug));
|
properties.add(DiagnosticsProperty<NoDefaultCupertinoThemeData>('cupertinoOverrideTheme', cupertinoOverrideTheme, defaultValue: defaultData.cupertinoOverrideTheme, level: DiagnosticLevel.debug));
|
||||||
|
396
packages/flutter/test/material/navigation_bar_test.dart
Normal file
396
packages/flutter/test/material/navigation_bar_test.dart
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
// 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_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Navigation bar updates destinations when tapped', (WidgetTester tester) async {
|
||||||
|
int mutatedIndex = -1;
|
||||||
|
final Widget widget = _buildWidget(
|
||||||
|
NavigationBar(
|
||||||
|
destinations: const <Widget>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onDestinationSelected: (int i) {
|
||||||
|
mutatedIndex = i;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(widget);
|
||||||
|
|
||||||
|
expect(find.text('AC'), findsOneWidget);
|
||||||
|
expect(find.text('Alarm'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.text('Alarm'));
|
||||||
|
expect(mutatedIndex, 1);
|
||||||
|
|
||||||
|
await tester.tap(find.text('AC'));
|
||||||
|
expect(mutatedIndex, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBar can update background color', (WidgetTester tester) async {
|
||||||
|
const Color color = Colors.yellow;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildWidget(
|
||||||
|
NavigationBar(
|
||||||
|
backgroundColor: color,
|
||||||
|
destinations: const <Widget>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onDestinationSelected: (int i) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(_getMaterial(tester).color, equals(color));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBar adds bottom padding to height', (WidgetTester tester) async {
|
||||||
|
const double bottomPadding = 40.0;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildWidget(
|
||||||
|
NavigationBar(
|
||||||
|
destinations: const <Widget>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onDestinationSelected: (int i) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final double defaultSize = tester.getSize(find.byType(NavigationBar)).height;
|
||||||
|
expect(defaultSize, 80);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildWidget(
|
||||||
|
MediaQuery(
|
||||||
|
data: const MediaQueryData(padding: EdgeInsets.only(bottom: bottomPadding)),
|
||||||
|
child: NavigationBar(
|
||||||
|
destinations: const <Widget>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onDestinationSelected: (int i) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final double expectedHeight = defaultSize + bottomPadding;
|
||||||
|
expect(tester.getSize(find.byType(NavigationBar)).height, expectedHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBar shows tooltips with text scaling ', (WidgetTester tester) async {
|
||||||
|
const String label = 'A';
|
||||||
|
|
||||||
|
Widget buildApp({ required double textScaleFactor }) {
|
||||||
|
return MediaQuery(
|
||||||
|
data: MediaQueryData(textScaleFactor: textScaleFactor),
|
||||||
|
child: Localizations(
|
||||||
|
locale: const Locale('en', 'US'),
|
||||||
|
delegates: const <LocalizationsDelegate<dynamic>>[
|
||||||
|
DefaultMaterialLocalizations.delegate,
|
||||||
|
DefaultWidgetsLocalizations.delegate,
|
||||||
|
],
|
||||||
|
child: Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Navigator(
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
bottomNavigationBar: NavigationBar(
|
||||||
|
destinations: const <NavigationDestination>[
|
||||||
|
NavigationDestination(
|
||||||
|
label: label,
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
tooltip: label,
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'B',
|
||||||
|
icon: Icon(Icons.battery_alert),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildApp(textScaleFactor: 1.0));
|
||||||
|
expect(find.text(label), findsOneWidget);
|
||||||
|
await tester.longPress(find.text(label));
|
||||||
|
expect(find.text(label), findsNWidgets(2));
|
||||||
|
|
||||||
|
// The default size of a tooltip with the text A.
|
||||||
|
const Size defaultTooltipSize = Size(14.0, 14.0);
|
||||||
|
expect(tester.getSize(find.text(label).last), defaultTooltipSize);
|
||||||
|
// The duration is needed to ensure the tooltip disappears.
|
||||||
|
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildApp(textScaleFactor: 4.0));
|
||||||
|
expect(find.text(label), findsOneWidget);
|
||||||
|
await tester.longPress(find.text(label));
|
||||||
|
expect(tester.getSize(find.text(label).last), Size(defaultTooltipSize.width * 4, defaultTooltipSize.height * 4));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Custom tooltips in NavigationBarDestination', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
bottomNavigationBar: NavigationBar(
|
||||||
|
destinations: const <NavigationDestination>[
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'A',
|
||||||
|
tooltip: 'A tooltip',
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'B',
|
||||||
|
icon: Icon(Icons.battery_alert),
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
label: 'C',
|
||||||
|
icon: Icon(Icons.cake),
|
||||||
|
tooltip: '',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(find.text('A'), findsOneWidget);
|
||||||
|
await tester.longPress(find.text('A'));
|
||||||
|
expect(find.byTooltip('A tooltip'), findsOneWidget);
|
||||||
|
|
||||||
|
expect(find.text('B'), findsOneWidget);
|
||||||
|
await tester.longPress(find.text('B'));
|
||||||
|
expect(find.byTooltip('B'), findsOneWidget);
|
||||||
|
|
||||||
|
expect(find.text('C'), findsOneWidget);
|
||||||
|
await tester.longPress(find.text('C'));
|
||||||
|
expect(find.byTooltip('C'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
testWidgets('Navigation bar semantics', (WidgetTester tester) async {
|
||||||
|
Widget _widget({int selectedIndex = 0}) {
|
||||||
|
return _buildWidget(
|
||||||
|
NavigationBar(
|
||||||
|
selectedIndex: selectedIndex,
|
||||||
|
destinations: const <Widget>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(_widget(selectedIndex: 0));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('AC')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'AC\nTab 1 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: true,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('Alarm')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'Alarm\nTab 2 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: false,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_widget(selectedIndex: 1));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('AC')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'AC\nTab 1 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: false,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('Alarm')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'Alarm\nTab 2 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: true,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Navigation bar semantics with some labels hidden', (WidgetTester tester) async {
|
||||||
|
Widget _widget({int selectedIndex = 0}) {
|
||||||
|
return _buildWidget(
|
||||||
|
NavigationBar(
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
|
||||||
|
selectedIndex: selectedIndex,
|
||||||
|
destinations: const <Widget>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(_widget(selectedIndex: 0));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('AC')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'AC\nTab 1 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: true,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('Alarm')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'Alarm\nTab 2 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: false,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(_widget(selectedIndex: 1));
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('AC')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'AC\nTab 1 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: false,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
tester.getSemantics(find.text('Alarm')),
|
||||||
|
matchesSemantics(
|
||||||
|
label: 'Alarm\nTab 2 of 2',
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
isFocusable: true,
|
||||||
|
isSelected: true,
|
||||||
|
hasTapAction: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Navigation bar does not grow with text scale factor', (WidgetTester tester) async {
|
||||||
|
const int _animationMilliseconds = 800;
|
||||||
|
|
||||||
|
Widget _widget({double textScaleFactor = 1}) {
|
||||||
|
return _buildWidget(
|
||||||
|
MediaQuery(
|
||||||
|
data: MediaQueryData(textScaleFactor: textScaleFactor),
|
||||||
|
child: NavigationBar(
|
||||||
|
animationDuration: const Duration(milliseconds: _animationMilliseconds),
|
||||||
|
destinations: const <NavigationDestination>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.ac_unit),
|
||||||
|
label: 'AC',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.access_alarm),
|
||||||
|
label: 'Alarm',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(_widget());
|
||||||
|
final double initialHeight = tester.getSize(find.byType(NavigationBar)).height;
|
||||||
|
|
||||||
|
await tester.pumpWidget(_widget(textScaleFactor: 2));
|
||||||
|
final double newHeight = tester.getSize(find.byType(NavigationBar)).height;
|
||||||
|
|
||||||
|
expect(newHeight, equals(initialHeight));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildWidget(Widget child) {
|
||||||
|
return MaterialApp(
|
||||||
|
theme: ThemeData.light(),
|
||||||
|
home: Scaffold(
|
||||||
|
bottomNavigationBar: Center(
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Material _getMaterial(WidgetTester tester) {
|
||||||
|
return tester.firstWidget<Material>(
|
||||||
|
find.descendant(of: find.byType(NavigationBar), matching: find.byType(Material)),
|
||||||
|
);
|
||||||
|
}
|
254
packages/flutter/test/material/navigation_bar_theme_test.dart
Normal file
254
packages/flutter/test/material/navigation_bar_theme_test.dart
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
// 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/rendering.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('copyWith, ==, hashCode basics', () {
|
||||||
|
expect(const NavigationBarThemeData(), const NavigationBarThemeData().copyWith());
|
||||||
|
expect(const NavigationBarThemeData().hashCode, const NavigationBarThemeData().copyWith().hashCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Default debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
const NavigationBarThemeData().debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
expect(description, <String>[]);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Custom debugFillProperties', (WidgetTester tester) async {
|
||||||
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
||||||
|
NavigationBarThemeData(
|
||||||
|
height: 200.0,
|
||||||
|
backgroundColor: const Color(0x00000099),
|
||||||
|
indicatorColor: const Color(0x00000098),
|
||||||
|
labelTextStyle: MaterialStateProperty.all(const TextStyle(fontSize: 7.0)),
|
||||||
|
iconTheme: MaterialStateProperty.all(const IconThemeData(color: Color(0x00000097))),
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||||
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
|
final List<String> description = builder.properties
|
||||||
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
||||||
|
.map((DiagnosticsNode node) => node.toString())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
expect(description[0], 'height: 200.0');
|
||||||
|
expect(description[1], 'backgroundColor: Color(0x00000099)');
|
||||||
|
expect(description[2], 'indicatorColor: Color(0x00000098)');
|
||||||
|
expect(description[3], 'labelTextStyle: MaterialStateProperty.all(TextStyle(inherit: true, size: 7.0))');
|
||||||
|
|
||||||
|
// Ignore instance address for IconThemeData.
|
||||||
|
expect(description[4].contains('iconTheme: MaterialStateProperty.all(IconThemeData'), isTrue);
|
||||||
|
expect(description[4].contains('(color: Color(0x00000097))'), isTrue);
|
||||||
|
|
||||||
|
expect(description[5], 'labelBehavior: NavigationDestinationLabelBehavior.alwaysHide');
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBarThemeData values are used when no NavigationBar properties are specified', (WidgetTester tester) async {
|
||||||
|
const double height = 200.0;
|
||||||
|
const Color backgroundColor = Color(0x00000001);
|
||||||
|
const Color indicatorColor = Color(0x00000002);
|
||||||
|
const double selectedIconSize = 25.0;
|
||||||
|
const double unselectedIconSize = 23.0;
|
||||||
|
const Color selectedIconColor = Color(0x00000003);
|
||||||
|
const Color unselectedIconColor = Color(0x00000004);
|
||||||
|
const double selectedIconOpacity = 0.99;
|
||||||
|
const double unselectedIconOpacity = 0.98;
|
||||||
|
const double selectedLabelFontSize = 13.0;
|
||||||
|
const double unselectedLabelFontSize = 11.0;
|
||||||
|
const NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
bottomNavigationBar: NavigationBarTheme(
|
||||||
|
data: NavigationBarThemeData(
|
||||||
|
height: height,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
indicatorColor: indicatorColor,
|
||||||
|
iconTheme: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.selected)) {
|
||||||
|
return const IconThemeData(
|
||||||
|
size: selectedIconSize,
|
||||||
|
color: selectedIconColor,
|
||||||
|
opacity: selectedIconOpacity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const IconThemeData(
|
||||||
|
size: unselectedIconSize,
|
||||||
|
color: unselectedIconColor,
|
||||||
|
opacity: unselectedIconOpacity,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
labelTextStyle: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.selected)) {
|
||||||
|
return const TextStyle(fontSize: selectedLabelFontSize);
|
||||||
|
}
|
||||||
|
return const TextStyle(fontSize: unselectedLabelFontSize);
|
||||||
|
}),
|
||||||
|
labelBehavior: labelBehavior,
|
||||||
|
),
|
||||||
|
child: NavigationBar(
|
||||||
|
selectedIndex: 0,
|
||||||
|
destinations: _destinations(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(_barHeight(tester), height);
|
||||||
|
expect(_barMaterial(tester).color, backgroundColor);
|
||||||
|
expect(_indicator(tester)?.color, indicatorColor);
|
||||||
|
expect(_selectedIconTheme(tester).size, selectedIconSize);
|
||||||
|
expect(_selectedIconTheme(tester).color, selectedIconColor);
|
||||||
|
expect(_selectedIconTheme(tester).opacity, selectedIconOpacity);
|
||||||
|
expect(_unselectedIconTheme(tester).size, unselectedIconSize);
|
||||||
|
expect(_unselectedIconTheme(tester).color, unselectedIconColor);
|
||||||
|
expect(_unselectedIconTheme(tester).opacity, unselectedIconOpacity);
|
||||||
|
expect(_selectedLabelStyle(tester).fontSize, selectedLabelFontSize);
|
||||||
|
expect(_unselectedLabelStyle(tester).fontSize, unselectedLabelFontSize);
|
||||||
|
expect(_labelBehavior(tester), labelBehavior);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBar values take priority over NavigationBarThemeData values when both properties are specified', (WidgetTester tester) async {
|
||||||
|
const double height = 200.0;
|
||||||
|
const Color backgroundColor = Color(0x00000001);
|
||||||
|
const NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
bottomNavigationBar: NavigationBarTheme(
|
||||||
|
data: const NavigationBarThemeData(
|
||||||
|
height: 100.0,
|
||||||
|
backgroundColor: Color(0x00000099),
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||||
|
),
|
||||||
|
child: NavigationBar(
|
||||||
|
height: height,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
labelBehavior: labelBehavior,
|
||||||
|
selectedIndex: 0,
|
||||||
|
destinations: _destinations(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(_barHeight(tester), height);
|
||||||
|
expect(_barMaterial(tester).color, backgroundColor);
|
||||||
|
expect(_labelBehavior(tester), labelBehavior);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<NavigationDestination> _destinations() {
|
||||||
|
return const <NavigationDestination>[
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.favorite_border),
|
||||||
|
selectedIcon: Icon(Icons.favorite),
|
||||||
|
label: 'Abc',
|
||||||
|
),
|
||||||
|
NavigationDestination(
|
||||||
|
icon: Icon(Icons.star_border),
|
||||||
|
selectedIcon: Icon(Icons.star),
|
||||||
|
label: 'Def',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
double _barHeight(WidgetTester tester) {
|
||||||
|
return tester.getRect(
|
||||||
|
find.byType(NavigationBar),
|
||||||
|
).height;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material _barMaterial(WidgetTester tester) {
|
||||||
|
return tester.firstWidget<Material>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(NavigationBar),
|
||||||
|
matching: find.byType(Material),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoxDecoration? _indicator(WidgetTester tester) {
|
||||||
|
return tester.firstWidget<Container>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(FadeTransition),
|
||||||
|
matching: find.byType(Container),
|
||||||
|
),
|
||||||
|
).decoration as BoxDecoration?;
|
||||||
|
}
|
||||||
|
|
||||||
|
IconThemeData _selectedIconTheme(WidgetTester tester) {
|
||||||
|
return _iconTheme(tester, Icons.favorite);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconThemeData _unselectedIconTheme(WidgetTester tester) {
|
||||||
|
return _iconTheme(tester, Icons.star_border);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconThemeData _iconTheme(WidgetTester tester, IconData icon) {
|
||||||
|
return tester.firstWidget<IconTheme>(
|
||||||
|
find.ancestor(
|
||||||
|
of: find.byIcon(icon),
|
||||||
|
matching: find.byType(IconTheme),
|
||||||
|
),
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle _selectedLabelStyle(WidgetTester tester) {
|
||||||
|
return tester.widget<RichText>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.text('Abc'),
|
||||||
|
matching: find.byType(RichText),
|
||||||
|
),
|
||||||
|
).text.style!;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle _unselectedLabelStyle(WidgetTester tester) {
|
||||||
|
return tester.widget<RichText>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.text('Def'),
|
||||||
|
matching: find.byType(RichText),
|
||||||
|
),
|
||||||
|
).text.style!;
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationDestinationLabelBehavior _labelBehavior(WidgetTester tester) {
|
||||||
|
if (_opacityAboveLabel('Abc').evaluate().isNotEmpty && _opacityAboveLabel('Def').evaluate().isNotEmpty) {
|
||||||
|
return _labelOpacity(tester, 'Abc') == 1
|
||||||
|
? NavigationDestinationLabelBehavior.onlyShowSelected
|
||||||
|
: NavigationDestinationLabelBehavior.alwaysHide;
|
||||||
|
} else {
|
||||||
|
return NavigationDestinationLabelBehavior.alwaysShow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Finder _opacityAboveLabel(String text) {
|
||||||
|
return find.ancestor(
|
||||||
|
of: find.text(text),
|
||||||
|
matching: find.byType(Opacity),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only valid when labelBehavior != alwaysShow.
|
||||||
|
double _labelOpacity(WidgetTester tester, String text) {
|
||||||
|
final Opacity opacityWidget = tester.widget<Opacity>(
|
||||||
|
find.ancestor(
|
||||||
|
of: find.text(text),
|
||||||
|
matching: find.byType(Opacity),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return opacityWidget.opacity;
|
||||||
|
}
|
@ -324,6 +324,7 @@ void main() {
|
|||||||
colorScheme: const ColorScheme.light(),
|
colorScheme: const ColorScheme.light(),
|
||||||
dialogTheme: const DialogTheme(backgroundColor: Colors.black),
|
dialogTheme: const DialogTheme(backgroundColor: Colors.black),
|
||||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(backgroundColor: Colors.black),
|
floatingActionButtonTheme: const FloatingActionButtonThemeData(backgroundColor: Colors.black),
|
||||||
|
navigationBarTheme: const NavigationBarThemeData(backgroundColor: Colors.black),
|
||||||
navigationRailTheme: const NavigationRailThemeData(backgroundColor: Colors.black),
|
navigationRailTheme: const NavigationRailThemeData(backgroundColor: Colors.black),
|
||||||
typography: Typography.material2018(platform: TargetPlatform.android),
|
typography: Typography.material2018(platform: TargetPlatform.android),
|
||||||
cupertinoOverrideTheme: null,
|
cupertinoOverrideTheme: null,
|
||||||
@ -420,6 +421,7 @@ void main() {
|
|||||||
colorScheme: const ColorScheme.light(),
|
colorScheme: const ColorScheme.light(),
|
||||||
dialogTheme: const DialogTheme(backgroundColor: Colors.white),
|
dialogTheme: const DialogTheme(backgroundColor: Colors.white),
|
||||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(backgroundColor: Colors.white),
|
floatingActionButtonTheme: const FloatingActionButtonThemeData(backgroundColor: Colors.white),
|
||||||
|
navigationBarTheme: const NavigationBarThemeData(backgroundColor: Colors.white),
|
||||||
navigationRailTheme: const NavigationRailThemeData(backgroundColor: Colors.white),
|
navigationRailTheme: const NavigationRailThemeData(backgroundColor: Colors.white),
|
||||||
typography: Typography.material2018(platform: TargetPlatform.iOS),
|
typography: Typography.material2018(platform: TargetPlatform.iOS),
|
||||||
cupertinoOverrideTheme: ThemeData.light().cupertinoOverrideTheme,
|
cupertinoOverrideTheme: ThemeData.light().cupertinoOverrideTheme,
|
||||||
@ -497,6 +499,7 @@ void main() {
|
|||||||
colorScheme: otherTheme.colorScheme,
|
colorScheme: otherTheme.colorScheme,
|
||||||
dialogTheme: otherTheme.dialogTheme,
|
dialogTheme: otherTheme.dialogTheme,
|
||||||
floatingActionButtonTheme: otherTheme.floatingActionButtonTheme,
|
floatingActionButtonTheme: otherTheme.floatingActionButtonTheme,
|
||||||
|
navigationBarTheme: otherTheme.navigationBarTheme,
|
||||||
navigationRailTheme: otherTheme.navigationRailTheme,
|
navigationRailTheme: otherTheme.navigationRailTheme,
|
||||||
typography: otherTheme.typography,
|
typography: otherTheme.typography,
|
||||||
cupertinoOverrideTheme: otherTheme.cupertinoOverrideTheme,
|
cupertinoOverrideTheme: otherTheme.cupertinoOverrideTheme,
|
||||||
@ -571,6 +574,7 @@ void main() {
|
|||||||
expect(themeDataCopy.colorScheme, equals(otherTheme.colorScheme));
|
expect(themeDataCopy.colorScheme, equals(otherTheme.colorScheme));
|
||||||
expect(themeDataCopy.dialogTheme, equals(otherTheme.dialogTheme));
|
expect(themeDataCopy.dialogTheme, equals(otherTheme.dialogTheme));
|
||||||
expect(themeDataCopy.floatingActionButtonTheme, equals(otherTheme.floatingActionButtonTheme));
|
expect(themeDataCopy.floatingActionButtonTheme, equals(otherTheme.floatingActionButtonTheme));
|
||||||
|
expect(themeDataCopy.navigationBarTheme, equals(otherTheme.navigationBarTheme));
|
||||||
expect(themeDataCopy.navigationRailTheme, equals(otherTheme.navigationRailTheme));
|
expect(themeDataCopy.navigationRailTheme, equals(otherTheme.navigationRailTheme));
|
||||||
expect(themeDataCopy.typography, equals(otherTheme.typography));
|
expect(themeDataCopy.typography, equals(otherTheme.typography));
|
||||||
expect(themeDataCopy.cupertinoOverrideTheme, equals(otherTheme.cupertinoOverrideTheme));
|
expect(themeDataCopy.cupertinoOverrideTheme, equals(otherTheme.cupertinoOverrideTheme));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user