mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Migrate NavigationBar to M3 tokens. (#98285)
This commit is contained in:
parent
ccaf51562f
commit
ca2a751661
@ -18,6 +18,7 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:gen_defaults/fab_template.dart';
|
import 'package:gen_defaults/fab_template.dart';
|
||||||
|
import 'package:gen_defaults/navigation_bar_template.dart';
|
||||||
import 'package:gen_defaults/typography_template.dart';
|
import 'package:gen_defaults/typography_template.dart';
|
||||||
|
|
||||||
Map<String, dynamic> _readTokenFile(String fileName) {
|
Map<String, dynamic> _readTokenFile(String fileName) {
|
||||||
@ -64,5 +65,6 @@ Future<void> main(List<String> args) async {
|
|||||||
tokens['colorsDark'] = _readTokenFile('color_dark.json');
|
tokens['colorsDark'] = _readTokenFile('color_dark.json');
|
||||||
|
|
||||||
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
|
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
|
||||||
|
NavigationBarTemplate('$materialLib/navigation_bar.dart', tokens).updateFile();
|
||||||
TypographyTemplate('$materialLib/typography.dart', tokens).updateFile();
|
TypographyTemplate('$materialLib/typography.dart', tokens).updateFile();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,10 @@
|
|||||||
"bottomRight": 0.0
|
"bottomRight": 0.0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"md.sys.shape.corner.full": {
|
||||||
|
"family": "SHAPE_FAMILY_CIRCULAR"
|
||||||
|
},
|
||||||
|
|
||||||
"md.sys.shape.corner.large": {
|
"md.sys.shape.corner.large": {
|
||||||
"family": "SHAPE_FAMILY_ROUNDED_CORNERS",
|
"family": "SHAPE_FAMILY_ROUNDED_CORNERS",
|
||||||
"topLeft": 16.0,
|
"topLeft": 16.0,
|
||||||
|
57
dev/tools/gen_defaults/lib/navigation_bar_template.dart
Normal file
57
dev/tools/gen_defaults/lib/navigation_bar_template.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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 'template.dart';
|
||||||
|
|
||||||
|
class NavigationBarTemplate extends TokenTemplate {
|
||||||
|
const NavigationBarTemplate(String fileName, Map<String, dynamic> tokens) : super(fileName, tokens);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String generate() => '''
|
||||||
|
// Generated version ${tokens["version"]}
|
||||||
|
class _TokenDefaultsM3 extends NavigationBarThemeData {
|
||||||
|
_TokenDefaultsM3(BuildContext context)
|
||||||
|
: _theme = Theme.of(context),
|
||||||
|
_colors = Theme.of(context).colorScheme,
|
||||||
|
super(
|
||||||
|
height: ${tokens["md.comp.navigation-bar.container.height"]},
|
||||||
|
elevation: ${elevation("md.comp.navigation-bar.container")},
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData _theme;
|
||||||
|
final ColorScheme _colors;
|
||||||
|
|
||||||
|
// With Material 3, the NavigationBar uses an overlay blend for the
|
||||||
|
// default color regardless of light/dark mode. This should be handled
|
||||||
|
// in the Material widget based off of elevation, but for now we will do
|
||||||
|
// it here in the defaults.
|
||||||
|
@override Color? get backgroundColor => ElevationOverlay.colorWithOverlay(_colors.${color("md.comp.navigation-bar.container")}, _colors.primary, ${elevation("md.comp.navigation-bar.container")});
|
||||||
|
|
||||||
|
@override MaterialStateProperty<IconThemeData?>? get iconTheme {
|
||||||
|
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
return IconThemeData(
|
||||||
|
size: ${tokens["md.comp.navigation-bar.icon.size"]},
|
||||||
|
color: states.contains(MaterialState.selected)
|
||||||
|
? _colors.${color("md.comp.navigation-bar.active.icon")}
|
||||||
|
: _colors.${color("md.comp.navigation-bar.inactive.icon")},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override Color? get indicatorColor => _colors.${color("md.comp.navigation-bar.active-indicator")};
|
||||||
|
@override ShapeBorder? get indicatorShape => ${shape("md.comp.navigation-bar.active-indicator")};
|
||||||
|
|
||||||
|
@override MaterialStateProperty<TextStyle?>? get labelTextStyle {
|
||||||
|
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
final TextStyle style = _theme.textTheme.${textStyle("md.comp.navigation-bar.label-text")}!;
|
||||||
|
return style.apply(color: states.contains(MaterialState.selected)
|
||||||
|
? _colors.${color("md.comp.navigation-bar.active.label-text")}
|
||||||
|
: _colors.${color("md.comp.navigation-bar.inactive.label-text")}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
}
|
@ -76,17 +76,24 @@ abstract class TokenTemplate {
|
|||||||
|
|
||||||
/// Generate a shape constant for the given component token.
|
/// Generate a shape constant for the given component token.
|
||||||
///
|
///
|
||||||
/// Currently only supports "SHAPE_FAMILY_ROUNDED_CORNERS" which it
|
/// Currently supports family:
|
||||||
/// maps to a [RoundedRectangleBorder] expression.
|
/// - "SHAPE_FAMILY_ROUNDED_CORNERS" which maps to [RoundedRectangleBorder].
|
||||||
|
/// - "SHAPE_FAMILY_CIRCULAR" which maps to a [StadiumBorder].
|
||||||
String shape(String componentToken) {
|
String shape(String componentToken) {
|
||||||
// TODO(darrenaustin): handle more than just rounded rectangle shapes
|
|
||||||
final Map<String, dynamic> shape = tokens[tokens['$componentToken.shape']!]! as Map<String, dynamic>;
|
final Map<String, dynamic> shape = tokens[tokens['$componentToken.shape']!]! as Map<String, dynamic>;
|
||||||
|
switch (shape['family']) {
|
||||||
|
case 'SHAPE_FAMILY_ROUNDED_CORNERS':
|
||||||
return 'const RoundedRectangleBorder(borderRadius: '
|
return 'const RoundedRectangleBorder(borderRadius: '
|
||||||
'BorderRadius.only('
|
'BorderRadius.only('
|
||||||
'topLeft: Radius.circular(${shape['topLeft']}), '
|
'topLeft: Radius.circular(${shape['topLeft']}), '
|
||||||
'topRight: Radius.circular(${shape['topRight']}), '
|
'topRight: Radius.circular(${shape['topRight']}), '
|
||||||
'bottomLeft: Radius.circular(${shape['bottomLeft']}), '
|
'bottomLeft: Radius.circular(${shape['bottomLeft']}), '
|
||||||
'bottomRight: Radius.circular(${shape['bottomRight']})))';
|
'bottomRight: Radius.circular(${shape['bottomRight']})))';
|
||||||
|
case 'SHAPE_FAMILY_CIRCULAR':
|
||||||
|
return 'const StadiumBorder()';
|
||||||
|
}
|
||||||
|
print('Unsupported shape family type: ${shape['family']} for $componentToken');
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a [TextTheme] text style name for the given component token.
|
/// Generate a [TextTheme] text style name for the given component token.
|
||||||
|
@ -101,16 +101,21 @@ static final String tokenBar = 'bar';
|
|||||||
test('Templates can get proper shapes from given data', () {
|
test('Templates can get proper shapes from given data', () {
|
||||||
const Map<String, dynamic> tokens = <String, dynamic>{
|
const Map<String, dynamic> tokens = <String, dynamic>{
|
||||||
'foo.shape': 'shape.large',
|
'foo.shape': 'shape.large',
|
||||||
|
'bar.shape': 'shape.full',
|
||||||
'shape.large': <String, dynamic>{
|
'shape.large': <String, dynamic>{
|
||||||
'family': 'SHAPE_FAMILY_ROUNDED_CORNERS',
|
'family': 'SHAPE_FAMILY_ROUNDED_CORNERS',
|
||||||
'topLeft': 1.0,
|
'topLeft': 1.0,
|
||||||
'topRight': 2.0,
|
'topRight': 2.0,
|
||||||
'bottomLeft': 3.0,
|
'bottomLeft': 3.0,
|
||||||
'bottomRight': 4.0,
|
'bottomRight': 4.0,
|
||||||
}
|
},
|
||||||
|
'shape.full': <String, dynamic>{
|
||||||
|
'family': 'SHAPE_FAMILY_CIRCULAR',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
final TestTemplate template = TestTemplate('foobar.dart', tokens);
|
final TestTemplate template = TestTemplate('foobar.dart', tokens);
|
||||||
expect(template.shape('foo'), 'const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(1.0), topRight: Radius.circular(2.0), bottomLeft: Radius.circular(3.0), bottomRight: Radius.circular(4.0)))');
|
expect(template.shape('foo'), 'const RoundedRectangleBorder(borderRadius: BorderRadius.only(topLeft: Radius.circular(1.0), topRight: Radius.circular(2.0), bottomLeft: Radius.circular(3.0), bottomRight: Radius.circular(4.0)))');
|
||||||
|
expect(template.shape('bar'), 'const StadiumBorder()');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ class NavigationBar extends StatelessWidget {
|
|||||||
required this.destinations,
|
required this.destinations,
|
||||||
this.onDestinationSelected,
|
this.onDestinationSelected,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.elevation,
|
||||||
this.height,
|
this.height,
|
||||||
this.labelBehavior,
|
this.labelBehavior,
|
||||||
}) : assert(destinations != null && destinations.length >= 2),
|
}) : assert(destinations != null && destinations.length >= 2),
|
||||||
@ -111,6 +112,13 @@ class NavigationBar extends StatelessWidget {
|
|||||||
/// [ColorScheme.onSurface] using an [ElevationOverlay].
|
/// [ColorScheme.onSurface] using an [ElevationOverlay].
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// The elevation of the [NavigationBar] itself.
|
||||||
|
///
|
||||||
|
/// If null, [NavigationBarThemeData.elevation] is used. If that
|
||||||
|
/// is also null, then if [ThemeData.useMaterial3] is true then it will
|
||||||
|
/// be 3.0 otherwise 0.0.
|
||||||
|
final double? elevation;
|
||||||
|
|
||||||
/// The height of the [NavigationBar] itself.
|
/// The height of the [NavigationBar] itself.
|
||||||
///
|
///
|
||||||
/// If this is used in [Scaffold.bottomNavigationBar] and the scaffold is
|
/// If this is used in [Scaffold.bottomNavigationBar] and the scaffold is
|
||||||
@ -145,20 +153,20 @@ class NavigationBar extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
final NavigationBarThemeData defaults = _defaultsFor(context);
|
||||||
|
|
||||||
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
|
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
|
||||||
final double effectiveHeight = height ?? navigationBarTheme.height ?? 80;
|
final double effectiveHeight = height ?? navigationBarTheme.height ?? defaults.height!;
|
||||||
final NavigationDestinationLabelBehavior effectiveLabelBehavior = labelBehavior
|
final NavigationDestinationLabelBehavior effectiveLabelBehavior = labelBehavior
|
||||||
?? navigationBarTheme.labelBehavior
|
?? navigationBarTheme.labelBehavior
|
||||||
?? NavigationDestinationLabelBehavior.alwaysShow;
|
?? defaults.labelBehavior!;
|
||||||
final double additionalBottomPadding = MediaQuery.of(context).padding.bottom;
|
final double additionalBottomPadding = MediaQuery.of(context).padding.bottom;
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
// With Material 3, the NavigationBar uses an overlay blend for the
|
|
||||||
// default color regardless of light/dark mode.
|
|
||||||
color: backgroundColor
|
color: backgroundColor
|
||||||
?? navigationBarTheme.backgroundColor
|
?? navigationBarTheme.backgroundColor
|
||||||
?? ElevationOverlay.colorWithOverlay(colorScheme.surface, colorScheme.onSurface, 3.0),
|
?? defaults.backgroundColor!,
|
||||||
|
elevation: elevation ?? navigationBarTheme.elevation ?? defaults.elevation!,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(bottom: additionalBottomPadding),
|
padding: EdgeInsets.only(bottom: additionalBottomPadding),
|
||||||
child: MediaQuery.removePadding(
|
child: MediaQuery.removePadding(
|
||||||
@ -275,25 +283,25 @@ class NavigationDestination extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
const Set<MaterialState> selectedState = <MaterialState>{MaterialState.selected};
|
||||||
final ColorScheme colorScheme = theme.colorScheme;
|
const Set<MaterialState> unselectedState = <MaterialState>{};
|
||||||
|
|
||||||
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
|
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
|
||||||
|
final NavigationBarThemeData defaults = _defaultsFor(context);
|
||||||
final Animation<double> animation = _NavigationDestinationInfo.of(context).selectedAnimation;
|
final Animation<double> animation = _NavigationDestinationInfo.of(context).selectedAnimation;
|
||||||
|
|
||||||
return _NavigationDestinationBuilder(
|
return _NavigationDestinationBuilder(
|
||||||
label: label,
|
label: label,
|
||||||
tooltip: tooltip,
|
tooltip: tooltip,
|
||||||
buildIcon: (BuildContext context) {
|
buildIcon: (BuildContext context) {
|
||||||
final IconThemeData defaultIconTheme = IconThemeData(
|
|
||||||
size: 24,
|
|
||||||
color: colorScheme.onSurface,
|
|
||||||
);
|
|
||||||
final Widget selectedIconWidget = IconTheme.merge(
|
final Widget selectedIconWidget = IconTheme.merge(
|
||||||
data: navigationBarTheme.iconTheme?.resolve(<MaterialState>{MaterialState.selected}) ?? defaultIconTheme,
|
data: navigationBarTheme.iconTheme?.resolve(selectedState)
|
||||||
|
?? defaults.iconTheme!.resolve(selectedState)!,
|
||||||
child: selectedIcon ?? icon,
|
child: selectedIcon ?? icon,
|
||||||
);
|
);
|
||||||
final Widget unselectedIconWidget = IconTheme.merge(
|
final Widget unselectedIconWidget = IconTheme.merge(
|
||||||
data: navigationBarTheme.iconTheme?.resolve(<MaterialState>{}) ?? defaultIconTheme,
|
data: navigationBarTheme.iconTheme?.resolve(unselectedState)
|
||||||
|
?? defaults.iconTheme!.resolve(unselectedState)!,
|
||||||
child: icon,
|
child: icon,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -302,7 +310,8 @@ class NavigationDestination extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
NavigationIndicator(
|
NavigationIndicator(
|
||||||
animation: animation,
|
animation: animation,
|
||||||
color: navigationBarTheme.indicatorColor,
|
color: navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
|
||||||
|
shape: navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
|
||||||
),
|
),
|
||||||
_StatusTransitionWidgetBuilder(
|
_StatusTransitionWidgetBuilder(
|
||||||
animation: animation,
|
animation: animation,
|
||||||
@ -316,11 +325,10 @@ class NavigationDestination extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
buildLabel: (BuildContext context) {
|
buildLabel: (BuildContext context) {
|
||||||
final TextStyle? defaultTextStyle = theme.textTheme.overline?.copyWith(
|
final TextStyle? effectiveSelectedLabelTextStyle = navigationBarTheme.labelTextStyle?.resolve(selectedState)
|
||||||
color: colorScheme.onSurface,
|
?? defaults.labelTextStyle!.resolve(selectedState);
|
||||||
);
|
final TextStyle? effectiveUnselectedLabelTextStyle = navigationBarTheme.labelTextStyle?.resolve(unselectedState)
|
||||||
final TextStyle? effectiveSelectedLabelTextStyle = navigationBarTheme.labelTextStyle?.resolve(<MaterialState>{MaterialState.selected}) ?? defaultTextStyle;
|
?? defaults.labelTextStyle!.resolve(unselectedState);
|
||||||
final TextStyle? effectiveUnselectedLabelTextStyle = navigationBarTheme.labelTextStyle?.resolve(<MaterialState>{}) ?? defaultTextStyle;
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(top: 4),
|
padding: const EdgeInsets.only(top: 4),
|
||||||
child: _ClampTextScaleFactor(
|
child: _ClampTextScaleFactor(
|
||||||
@ -525,6 +533,7 @@ class NavigationIndicator extends StatelessWidget {
|
|||||||
this.width = 64,
|
this.width = 64,
|
||||||
this.height = 32,
|
this.height = 32,
|
||||||
this.borderRadius = const BorderRadius.all(Radius.circular(16)),
|
this.borderRadius = const BorderRadius.all(Radius.circular(16)),
|
||||||
|
this.shape,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Determines the scale of the indicator.
|
/// Determines the scale of the indicator.
|
||||||
@ -538,25 +547,33 @@ class NavigationIndicator extends StatelessWidget {
|
|||||||
/// If null, defaults to [ColorScheme.secondary].
|
/// If null, defaults to [ColorScheme.secondary].
|
||||||
final Color? color;
|
final Color? color;
|
||||||
|
|
||||||
/// The width of the container that holds in the indicator.
|
/// The width of this indicator.
|
||||||
///
|
///
|
||||||
/// Defaults to `64`.
|
/// Defaults to `64`.
|
||||||
final double width;
|
final double width;
|
||||||
|
|
||||||
/// The height of the container that holds in the indicator.
|
/// The height of this indicator.
|
||||||
///
|
///
|
||||||
/// Defaults to `32`.
|
/// Defaults to `32`.
|
||||||
final double height;
|
final double height;
|
||||||
|
|
||||||
/// The radius of the container that holds in the indicator.
|
/// The border radius of the shape of the indicator.
|
||||||
|
///
|
||||||
|
/// This is used to create a [RoundedRectangleBorder] shape for the indicator.
|
||||||
|
/// This is ignored if [shape] is non-null.
|
||||||
///
|
///
|
||||||
/// Defaults to `BorderRadius.circular(16)`.
|
/// Defaults to `BorderRadius.circular(16)`.
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius borderRadius;
|
||||||
|
|
||||||
|
/// The shape of the indicator.
|
||||||
|
///
|
||||||
|
/// If non-null this is used as the shape used to draw the background
|
||||||
|
/// of the indicator. If null then a [RoundedRectangleBorder] with the
|
||||||
|
/// [borderRadius] is used.
|
||||||
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ColorScheme colorScheme = Theme.of(context).colorScheme;
|
|
||||||
|
|
||||||
return AnimatedBuilder(
|
return AnimatedBuilder(
|
||||||
animation: animation,
|
animation: animation,
|
||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
@ -594,9 +611,9 @@ class NavigationIndicator extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
decoration: BoxDecoration(
|
decoration: ShapeDecoration(
|
||||||
borderRadius: borderRadius,
|
shape: shape ?? RoundedRectangleBorder(borderRadius: borderRadius),
|
||||||
color: color ?? colorScheme.secondary.withOpacity(.24),
|
color: color ?? Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -1171,3 +1188,90 @@ bool _isForwardOrCompleted(Animation<double> animation) {
|
|||||||
return animation.status == AnimationStatus.forward
|
return animation.status == AnimationStatus.forward
|
||||||
|| animation.status == AnimationStatus.completed;
|
|| animation.status == AnimationStatus.completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NavigationBarThemeData _defaultsFor(BuildContext context) {
|
||||||
|
return Theme.of(context).useMaterial3 ? _TokenDefaultsM3(context) : _Defaults(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Defaults extends NavigationBarThemeData {
|
||||||
|
_Defaults(BuildContext context)
|
||||||
|
: _theme = Theme.of(context),
|
||||||
|
_colors = Theme.of(context).colorScheme,
|
||||||
|
super(
|
||||||
|
height: 80.0,
|
||||||
|
elevation: 0.0,
|
||||||
|
indicatorShape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData _theme;
|
||||||
|
final ColorScheme _colors;
|
||||||
|
|
||||||
|
// With Material 3, the NavigationBar uses an overlay blend for the
|
||||||
|
// default color regardless of light/dark mode.
|
||||||
|
@override Color? get backgroundColor => ElevationOverlay.colorWithOverlay(_colors.surface, _colors.onSurface, 3.0);
|
||||||
|
|
||||||
|
@override MaterialStateProperty<IconThemeData?>? get iconTheme {
|
||||||
|
return MaterialStateProperty.all(IconThemeData(
|
||||||
|
size: 24,
|
||||||
|
color: _colors.onSurface,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override Color? get indicatorColor => _colors.secondary.withOpacity(0.24);
|
||||||
|
|
||||||
|
@override MaterialStateProperty<TextStyle?>? get labelTextStyle => MaterialStateProperty.all(_theme.textTheme.overline!.copyWith(color: _colors.onSurface));
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN GENERATED TOKEN PROPERTIES
|
||||||
|
|
||||||
|
// Generated code to the end of this file. Do not edit by hand.
|
||||||
|
// These defaults are generated from the Material Design Token
|
||||||
|
// database by the script dev/tools/gen_defaults/bin/gen_defaults.dart.
|
||||||
|
|
||||||
|
// Generated version v0_81
|
||||||
|
class _TokenDefaultsM3 extends NavigationBarThemeData {
|
||||||
|
_TokenDefaultsM3(BuildContext context)
|
||||||
|
: _theme = Theme.of(context),
|
||||||
|
_colors = Theme.of(context).colorScheme,
|
||||||
|
super(
|
||||||
|
height: 80.0,
|
||||||
|
elevation: 3.0,
|
||||||
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData _theme;
|
||||||
|
final ColorScheme _colors;
|
||||||
|
|
||||||
|
// With Material 3, the NavigationBar uses an overlay blend for the
|
||||||
|
// default color regardless of light/dark mode. This should be handled
|
||||||
|
// in the Material widget based off of elevation, but for now we will do
|
||||||
|
// it here in the defaults.
|
||||||
|
@override Color? get backgroundColor => ElevationOverlay.colorWithOverlay(_colors.surface, _colors.primary, 3.0);
|
||||||
|
|
||||||
|
@override MaterialStateProperty<IconThemeData?>? get iconTheme {
|
||||||
|
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
return IconThemeData(
|
||||||
|
size: 24.0,
|
||||||
|
color: states.contains(MaterialState.selected)
|
||||||
|
? _colors.onSecondaryContainer
|
||||||
|
: _colors.onSurfaceVariant,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override Color? get indicatorColor => _colors.secondaryContainer;
|
||||||
|
@override ShapeBorder? get indicatorShape => const StadiumBorder();
|
||||||
|
|
||||||
|
@override MaterialStateProperty<TextStyle?>? get labelTextStyle {
|
||||||
|
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
|
final TextStyle style = _theme.textTheme.labelMedium!;
|
||||||
|
return style.apply(color: states.contains(MaterialState.selected)
|
||||||
|
? _colors.onSurface
|
||||||
|
: _colors.onSurfaceVariant
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// END GENERATED TOKEN PROPERTIES
|
||||||
|
@ -41,7 +41,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
const NavigationBarThemeData({
|
const NavigationBarThemeData({
|
||||||
this.height,
|
this.height,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.elevation,
|
||||||
this.indicatorColor,
|
this.indicatorColor,
|
||||||
|
this.indicatorShape,
|
||||||
this.labelTextStyle,
|
this.labelTextStyle,
|
||||||
this.iconTheme,
|
this.iconTheme,
|
||||||
this.labelBehavior,
|
this.labelBehavior,
|
||||||
@ -53,9 +55,15 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
/// Overrides the default value of [NavigationBar.backgroundColor].
|
/// Overrides the default value of [NavigationBar.backgroundColor].
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// Overrides the default value of [NavigationBar.elevation].
|
||||||
|
final double? elevation;
|
||||||
|
|
||||||
/// Overrides the default value of [NavigationBar]'s selection indicator.
|
/// Overrides the default value of [NavigationBar]'s selection indicator.
|
||||||
final Color? indicatorColor;
|
final Color? indicatorColor;
|
||||||
|
|
||||||
|
/// Overrides the default shape of the [NavigationBar]'s selection indicator.
|
||||||
|
final ShapeBorder? indicatorShape;
|
||||||
|
|
||||||
/// The style to merge with the default text style for
|
/// The style to merge with the default text style for
|
||||||
/// [NavigationDestination] labels.
|
/// [NavigationDestination] labels.
|
||||||
///
|
///
|
||||||
@ -77,7 +85,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
NavigationBarThemeData copyWith({
|
NavigationBarThemeData copyWith({
|
||||||
double? height,
|
double? height,
|
||||||
Color? backgroundColor,
|
Color? backgroundColor,
|
||||||
|
double? elevation,
|
||||||
Color? indicatorColor,
|
Color? indicatorColor,
|
||||||
|
ShapeBorder? indicatorShape,
|
||||||
MaterialStateProperty<TextStyle?>? labelTextStyle,
|
MaterialStateProperty<TextStyle?>? labelTextStyle,
|
||||||
MaterialStateProperty<IconThemeData?>? iconTheme,
|
MaterialStateProperty<IconThemeData?>? iconTheme,
|
||||||
NavigationDestinationLabelBehavior? labelBehavior,
|
NavigationDestinationLabelBehavior? labelBehavior,
|
||||||
@ -85,7 +95,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
return NavigationBarThemeData(
|
return NavigationBarThemeData(
|
||||||
height: height ?? this.height,
|
height: height ?? this.height,
|
||||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||||
|
elevation: elevation ?? this.elevation,
|
||||||
indicatorColor: indicatorColor ?? this.indicatorColor,
|
indicatorColor: indicatorColor ?? this.indicatorColor,
|
||||||
|
indicatorShape: indicatorShape ?? this.indicatorShape,
|
||||||
labelTextStyle: labelTextStyle ?? this.labelTextStyle,
|
labelTextStyle: labelTextStyle ?? this.labelTextStyle,
|
||||||
iconTheme: iconTheme ?? this.iconTheme,
|
iconTheme: iconTheme ?? this.iconTheme,
|
||||||
labelBehavior: labelBehavior ?? this.labelBehavior,
|
labelBehavior: labelBehavior ?? this.labelBehavior,
|
||||||
@ -104,7 +116,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
return NavigationBarThemeData(
|
return NavigationBarThemeData(
|
||||||
height: lerpDouble(a?.height, b?.height, t),
|
height: lerpDouble(a?.height, b?.height, t),
|
||||||
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
|
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
|
||||||
|
elevation: lerpDouble(a?.elevation, b?.elevation, t),
|
||||||
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
|
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
|
||||||
|
indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t),
|
||||||
labelTextStyle: _lerpProperties<TextStyle?>(a?.labelTextStyle, b?.labelTextStyle, t, TextStyle.lerp),
|
labelTextStyle: _lerpProperties<TextStyle?>(a?.labelTextStyle, b?.labelTextStyle, t, TextStyle.lerp),
|
||||||
iconTheme: _lerpProperties<IconThemeData?>(a?.iconTheme, b?.iconTheme, t, IconThemeData.lerp),
|
iconTheme: _lerpProperties<IconThemeData?>(a?.iconTheme, b?.iconTheme, t, IconThemeData.lerp),
|
||||||
labelBehavior: t < 0.5 ? a?.labelBehavior : b?.labelBehavior,
|
labelBehavior: t < 0.5 ? a?.labelBehavior : b?.labelBehavior,
|
||||||
@ -116,7 +130,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
return hashValues(
|
return hashValues(
|
||||||
height,
|
height,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
|
elevation,
|
||||||
indicatorColor,
|
indicatorColor,
|
||||||
|
indicatorShape,
|
||||||
labelTextStyle,
|
labelTextStyle,
|
||||||
iconTheme,
|
iconTheme,
|
||||||
labelBehavior,
|
labelBehavior,
|
||||||
@ -132,7 +148,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
return other is NavigationBarThemeData
|
return other is NavigationBarThemeData
|
||||||
&& other.height == height
|
&& other.height == height
|
||||||
&& other.backgroundColor == backgroundColor
|
&& other.backgroundColor == backgroundColor
|
||||||
|
&& other.elevation == elevation
|
||||||
&& other.indicatorColor == indicatorColor
|
&& other.indicatorColor == indicatorColor
|
||||||
|
&& other.indicatorShape == indicatorShape
|
||||||
&& other.labelTextStyle == labelTextStyle
|
&& other.labelTextStyle == labelTextStyle
|
||||||
&& other.iconTheme == iconTheme
|
&& other.iconTheme == iconTheme
|
||||||
&& other.labelBehavior == labelBehavior;
|
&& other.labelBehavior == labelBehavior;
|
||||||
@ -143,7 +161,9 @@ class NavigationBarThemeData with Diagnosticable {
|
|||||||
super.debugFillProperties(properties);
|
super.debugFillProperties(properties);
|
||||||
properties.add(DoubleProperty('height', height, defaultValue: null));
|
properties.add(DoubleProperty('height', height, defaultValue: null));
|
||||||
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
|
properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
|
||||||
|
properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
|
||||||
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: null));
|
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: null));
|
||||||
|
properties.add(DiagnosticsProperty<ShapeBorder>('indicatorShape', indicatorShape, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('labelTextStyle', labelTextStyle, defaultValue: null));
|
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>('labelTextStyle', labelTextStyle, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<MaterialStateProperty<IconThemeData?>>('iconTheme', iconTheme, defaultValue: null));
|
properties.add(DiagnosticsProperty<MaterialStateProperty<IconThemeData?>>('iconTheme', iconTheme, defaultValue: null));
|
||||||
properties.add(DiagnosticsProperty<NavigationDestinationLabelBehavior>('labelBehavior', labelBehavior, defaultValue: null));
|
properties.add(DiagnosticsProperty<NavigationDestinationLabelBehavior>('labelBehavior', labelBehavior, defaultValue: null));
|
||||||
|
@ -1150,6 +1150,7 @@ class ThemeData with Diagnosticable {
|
|||||||
/// Components that have been migrated to Material 3 are:
|
/// Components that have been migrated to Material 3 are:
|
||||||
///
|
///
|
||||||
/// * [FloatingActionButton]
|
/// * [FloatingActionButton]
|
||||||
|
/// * [NavigationBar]
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
|
@ -63,6 +63,31 @@ void main() {
|
|||||||
expect(_getMaterial(tester).color, equals(color));
|
expect(_getMaterial(tester).color, equals(color));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBar can update elevation', (WidgetTester tester) async {
|
||||||
|
const double elevation = 42.0;
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildWidget(
|
||||||
|
NavigationBar(
|
||||||
|
elevation: elevation,
|
||||||
|
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).elevation, equals(elevation));
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('NavigationBar adds bottom padding to height', (WidgetTester tester) async {
|
testWidgets('NavigationBar adds bottom padding to height', (WidgetTester tester) async {
|
||||||
const double bottomPadding = 40.0;
|
const double bottomPadding = 40.0;
|
||||||
|
|
||||||
@ -112,6 +137,61 @@ void main() {
|
|||||||
expect(tester.getSize(find.byType(NavigationBar)).height, expectedHeight);
|
expect(tester.getSize(find.byType(NavigationBar)).height, expectedHeight);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationBar uses proper defaults when no parameters are given', (WidgetTester tester) async {
|
||||||
|
// Pre-M3 settings that were hand coded.
|
||||||
|
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) {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(_getMaterial(tester).color, const Color(0xffeaeaea));
|
||||||
|
expect(_getMaterial(tester).elevation, 0);
|
||||||
|
expect(tester.getSize(find.byType(NavigationBar)).height, 80);
|
||||||
|
expect(_indicator(tester)?.color, const Color(0x3d2196f3));
|
||||||
|
expect(_indicator(tester)?.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)));
|
||||||
|
|
||||||
|
// M3 settings from the token database.
|
||||||
|
await tester.pumpWidget(
|
||||||
|
_buildWidget(
|
||||||
|
Theme(
|
||||||
|
data: ThemeData.light().copyWith(useMaterial3: true),
|
||||||
|
child: NavigationBar(
|
||||||
|
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, const Color(0xffecf6fe));
|
||||||
|
expect(_getMaterial(tester).elevation, 3);
|
||||||
|
expect(tester.getSize(find.byType(NavigationBar)).height, 80);
|
||||||
|
expect(_indicator(tester)?.color, const Color(0xff2196f3));
|
||||||
|
expect(_indicator(tester)?.shape, const StadiumBorder());
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('NavigationBar shows tooltips with text scaling ', (WidgetTester tester) async {
|
testWidgets('NavigationBar shows tooltips with text scaling ', (WidgetTester tester) async {
|
||||||
const String label = 'A';
|
const String label = 'A';
|
||||||
|
|
||||||
@ -390,3 +470,12 @@ Material _getMaterial(WidgetTester tester) {
|
|||||||
find.descendant(of: find.byType(NavigationBar), matching: find.byType(Material)),
|
find.descendant(of: find.byType(NavigationBar), matching: find.byType(Material)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ShapeDecoration? _indicator(WidgetTester tester) {
|
||||||
|
return tester.firstWidget<Container>(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(FadeTransition),
|
||||||
|
matching: find.byType(Container),
|
||||||
|
),
|
||||||
|
).decoration as ShapeDecoration?;
|
||||||
|
}
|
||||||
|
@ -29,7 +29,9 @@ void main() {
|
|||||||
NavigationBarThemeData(
|
NavigationBarThemeData(
|
||||||
height: 200.0,
|
height: 200.0,
|
||||||
backgroundColor: const Color(0x00000099),
|
backgroundColor: const Color(0x00000099),
|
||||||
|
elevation: 20.0,
|
||||||
indicatorColor: const Color(0x00000098),
|
indicatorColor: const Color(0x00000098),
|
||||||
|
indicatorShape: const CircleBorder(),
|
||||||
labelTextStyle: MaterialStateProperty.all(const TextStyle(fontSize: 7.0)),
|
labelTextStyle: MaterialStateProperty.all(const TextStyle(fontSize: 7.0)),
|
||||||
iconTheme: MaterialStateProperty.all(const IconThemeData(color: Color(0x00000097))),
|
iconTheme: MaterialStateProperty.all(const IconThemeData(color: Color(0x00000097))),
|
||||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||||
@ -42,20 +44,24 @@ void main() {
|
|||||||
|
|
||||||
expect(description[0], 'height: 200.0');
|
expect(description[0], 'height: 200.0');
|
||||||
expect(description[1], 'backgroundColor: Color(0x00000099)');
|
expect(description[1], 'backgroundColor: Color(0x00000099)');
|
||||||
expect(description[2], 'indicatorColor: Color(0x00000098)');
|
expect(description[2], 'elevation: 20.0');
|
||||||
expect(description[3], 'labelTextStyle: MaterialStateProperty.all(TextStyle(inherit: true, size: 7.0))');
|
expect(description[3], 'indicatorColor: Color(0x00000098)');
|
||||||
|
expect(description[4], 'indicatorShape: CircleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle.none))');
|
||||||
|
expect(description[5], 'labelTextStyle: MaterialStateProperty.all(TextStyle(inherit: true, size: 7.0))');
|
||||||
|
|
||||||
// Ignore instance address for IconThemeData.
|
// Ignore instance address for IconThemeData.
|
||||||
expect(description[4].contains('iconTheme: MaterialStateProperty.all(IconThemeData'), isTrue);
|
expect(description[6].contains('iconTheme: MaterialStateProperty.all(IconThemeData'), isTrue);
|
||||||
expect(description[4].contains('(color: Color(0x00000097))'), isTrue);
|
expect(description[6].contains('(color: Color(0x00000097))'), isTrue);
|
||||||
|
|
||||||
expect(description[5], 'labelBehavior: NavigationDestinationLabelBehavior.alwaysHide');
|
expect(description[7], 'labelBehavior: NavigationDestinationLabelBehavior.alwaysHide');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('NavigationBarThemeData values are used when no NavigationBar properties are specified', (WidgetTester tester) async {
|
testWidgets('NavigationBarThemeData values are used when no NavigationBar properties are specified', (WidgetTester tester) async {
|
||||||
const double height = 200.0;
|
const double height = 200.0;
|
||||||
const Color backgroundColor = Color(0x00000001);
|
const Color backgroundColor = Color(0x00000001);
|
||||||
|
const double elevation = 42.0;
|
||||||
const Color indicatorColor = Color(0x00000002);
|
const Color indicatorColor = Color(0x00000002);
|
||||||
|
const ShapeBorder indicatorShape = CircleBorder();
|
||||||
const double selectedIconSize = 25.0;
|
const double selectedIconSize = 25.0;
|
||||||
const double unselectedIconSize = 23.0;
|
const double unselectedIconSize = 23.0;
|
||||||
const Color selectedIconColor = Color(0x00000003);
|
const Color selectedIconColor = Color(0x00000003);
|
||||||
@ -73,7 +79,9 @@ void main() {
|
|||||||
data: NavigationBarThemeData(
|
data: NavigationBarThemeData(
|
||||||
height: height,
|
height: height,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
|
elevation: elevation,
|
||||||
indicatorColor: indicatorColor,
|
indicatorColor: indicatorColor,
|
||||||
|
indicatorShape: indicatorShape,
|
||||||
iconTheme: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
iconTheme: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
||||||
if (states.contains(MaterialState.selected)) {
|
if (states.contains(MaterialState.selected)) {
|
||||||
return const IconThemeData(
|
return const IconThemeData(
|
||||||
@ -106,7 +114,9 @@ void main() {
|
|||||||
|
|
||||||
expect(_barHeight(tester), height);
|
expect(_barHeight(tester), height);
|
||||||
expect(_barMaterial(tester).color, backgroundColor);
|
expect(_barMaterial(tester).color, backgroundColor);
|
||||||
|
expect(_barMaterial(tester).elevation, elevation);
|
||||||
expect(_indicator(tester)?.color, indicatorColor);
|
expect(_indicator(tester)?.color, indicatorColor);
|
||||||
|
expect(_indicator(tester)?.shape, indicatorShape);
|
||||||
expect(_selectedIconTheme(tester).size, selectedIconSize);
|
expect(_selectedIconTheme(tester).size, selectedIconSize);
|
||||||
expect(_selectedIconTheme(tester).color, selectedIconColor);
|
expect(_selectedIconTheme(tester).color, selectedIconColor);
|
||||||
expect(_selectedIconTheme(tester).opacity, selectedIconOpacity);
|
expect(_selectedIconTheme(tester).opacity, selectedIconOpacity);
|
||||||
@ -121,6 +131,7 @@ void main() {
|
|||||||
testWidgets('NavigationBar values take priority over NavigationBarThemeData values when both properties are specified', (WidgetTester tester) async {
|
testWidgets('NavigationBar values take priority over NavigationBarThemeData values when both properties are specified', (WidgetTester tester) async {
|
||||||
const double height = 200.0;
|
const double height = 200.0;
|
||||||
const Color backgroundColor = Color(0x00000001);
|
const Color backgroundColor = Color(0x00000001);
|
||||||
|
const double elevation = 42.0;
|
||||||
const NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
|
const NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -129,11 +140,13 @@ void main() {
|
|||||||
bottomNavigationBar: NavigationBarTheme(
|
bottomNavigationBar: NavigationBarTheme(
|
||||||
data: const NavigationBarThemeData(
|
data: const NavigationBarThemeData(
|
||||||
height: 100.0,
|
height: 100.0,
|
||||||
|
elevation: 18.0,
|
||||||
backgroundColor: Color(0x00000099),
|
backgroundColor: Color(0x00000099),
|
||||||
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
labelBehavior: NavigationDestinationLabelBehavior.alwaysHide,
|
||||||
),
|
),
|
||||||
child: NavigationBar(
|
child: NavigationBar(
|
||||||
height: height,
|
height: height,
|
||||||
|
elevation: elevation,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
labelBehavior: labelBehavior,
|
labelBehavior: labelBehavior,
|
||||||
destinations: _destinations(),
|
destinations: _destinations(),
|
||||||
@ -145,6 +158,7 @@ void main() {
|
|||||||
|
|
||||||
expect(_barHeight(tester), height);
|
expect(_barHeight(tester), height);
|
||||||
expect(_barMaterial(tester).color, backgroundColor);
|
expect(_barMaterial(tester).color, backgroundColor);
|
||||||
|
expect(_barMaterial(tester).elevation, elevation);
|
||||||
expect(_labelBehavior(tester), labelBehavior);
|
expect(_labelBehavior(tester), labelBehavior);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -179,13 +193,13 @@ Material _barMaterial(WidgetTester tester) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxDecoration? _indicator(WidgetTester tester) {
|
ShapeDecoration? _indicator(WidgetTester tester) {
|
||||||
return tester.firstWidget<Container>(
|
return tester.firstWidget<Container>(
|
||||||
find.descendant(
|
find.descendant(
|
||||||
of: find.byType(FadeTransition),
|
of: find.byType(FadeTransition),
|
||||||
matching: find.byType(Container),
|
matching: find.byType(Container),
|
||||||
),
|
),
|
||||||
).decoration as BoxDecoration?;
|
).decoration as ShapeDecoration?;
|
||||||
}
|
}
|
||||||
|
|
||||||
IconThemeData _selectedIconTheme(WidgetTester tester) {
|
IconThemeData _selectedIconTheme(WidgetTester tester) {
|
||||||
|
@ -263,13 +263,13 @@ Material _railMaterial(WidgetTester tester) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BoxDecoration? _indicatorDecoration(WidgetTester tester) {
|
ShapeDecoration? _indicatorDecoration(WidgetTester tester) {
|
||||||
return tester.firstWidget<Container>(
|
return tester.firstWidget<Container>(
|
||||||
find.descendant(
|
find.descendant(
|
||||||
of: find.byType(NavigationIndicator),
|
of: find.byType(NavigationIndicator),
|
||||||
matching: find.byType(Container),
|
matching: find.byType(Container),
|
||||||
),
|
),
|
||||||
).decoration as BoxDecoration?;
|
).decoration as ShapeDecoration?;
|
||||||
}
|
}
|
||||||
|
|
||||||
IconThemeData _selectedIconTheme(WidgetTester tester) {
|
IconThemeData _selectedIconTheme(WidgetTester tester) {
|
||||||
|
Loading…
Reference in New Issue
Block a user