Migrate NavigationRail to Material 3. (#99171)
This commit is contained in:
parent
9291c8ba31
commit
57aa49028d
@ -20,6 +20,7 @@ import 'dart:io';
|
||||
import 'package:gen_defaults/dialog_template.dart';
|
||||
import 'package:gen_defaults/fab_template.dart';
|
||||
import 'package:gen_defaults/navigation_bar_template.dart';
|
||||
import 'package:gen_defaults/navigation_rail_template.dart';
|
||||
import 'package:gen_defaults/typography_template.dart';
|
||||
|
||||
Map<String, dynamic> _readTokenFile(String fileName) {
|
||||
@ -45,6 +46,7 @@ Future<void> main(List<String> args) async {
|
||||
'input_chip.json',
|
||||
'motion.json',
|
||||
'navigation_bar.json',
|
||||
'navigation_rail.json',
|
||||
'outlined_card.json',
|
||||
'palette.json',
|
||||
'shape.json',
|
||||
@ -70,6 +72,7 @@ Future<void> main(List<String> args) async {
|
||||
|
||||
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
|
||||
NavigationBarTemplate('$materialLib/navigation_bar.dart', tokens).updateFile();
|
||||
NavigationRailTemplate('$materialLib/navigation_rail.dart', tokens).updateFile();
|
||||
TypographyTemplate('$materialLib/typography.dart', tokens).updateFile();
|
||||
DialogTemplate('$materialLib/dialog.dart', tokens).updateFile();
|
||||
}
|
||||
|
57
dev/tools/gen_defaults/lib/navigation_rail_template.dart
Normal file
57
dev/tools/gen_defaults/lib/navigation_rail_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 NavigationRailTemplate extends TokenTemplate {
|
||||
const NavigationRailTemplate(String fileName, Map<String, dynamic> tokens) : super(fileName, tokens);
|
||||
|
||||
@override
|
||||
String generate() => '''
|
||||
// Generated version ${tokens["version"]}
|
||||
class _TokenDefaultsM3 extends NavigationRailThemeData {
|
||||
_TokenDefaultsM3(BuildContext context)
|
||||
: _theme = Theme.of(context),
|
||||
_colors = Theme.of(context).colorScheme,
|
||||
super(
|
||||
elevation: ${elevation("md.comp.navigation-rail.container")},
|
||||
groupAlignment: -1,
|
||||
labelType: NavigationRailLabelType.none,
|
||||
useIndicator: true,
|
||||
minWidth: ${tokens["md.comp.navigation-rail.container.width"]},
|
||||
minExtendedWidth: 256,
|
||||
);
|
||||
|
||||
final ThemeData _theme;
|
||||
final ColorScheme _colors;
|
||||
|
||||
@override Color? get backgroundColor => _colors.${color("md.comp.navigation-rail.container")};
|
||||
|
||||
@override TextStyle? get unselectedLabelTextStyle {
|
||||
return _theme.textTheme.${textStyle("md.comp.navigation-rail.label-text")}!.copyWith(color: _colors.${color("md.comp.navigation-rail.inactive.focus.label-text")});
|
||||
}
|
||||
|
||||
@override TextStyle? get selectedLabelTextStyle {
|
||||
return _theme.textTheme.${textStyle("md.comp.navigation-rail.label-text")}!.copyWith(color: _colors.${color("md.comp.navigation-rail.active.focus.label-text")});
|
||||
}
|
||||
|
||||
@override IconThemeData? get unselectedIconTheme {
|
||||
return IconThemeData(
|
||||
size: ${tokens["md.comp.navigation-rail.icon.size"]},
|
||||
color: _colors.${color("md.comp.navigation-rail.inactive.icon")},
|
||||
);
|
||||
}
|
||||
|
||||
@override IconThemeData? get selectedIconTheme {
|
||||
return IconThemeData(
|
||||
size: ${tokens["md.comp.navigation-rail.icon.size"]},
|
||||
color: _colors.${color("md.comp.navigation-rail.active.icon")},
|
||||
);
|
||||
}
|
||||
|
||||
@override Color? get indicatorColor => _colors.${color("md.comp.navigation-rail.active-indicator")};
|
||||
|
||||
}
|
||||
''';
|
||||
}
|
@ -365,32 +365,29 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData theme = Theme.of(context);
|
||||
final NavigationRailThemeData navigationRailTheme = NavigationRailTheme.of(context);
|
||||
final NavigationRailThemeData defaults = Theme.of(context).useMaterial3 ? _TokenDefaultsM3(context) : _DefaultsM2(context);
|
||||
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
|
||||
|
||||
final Color backgroundColor = widget.backgroundColor ?? navigationRailTheme.backgroundColor ?? theme.colorScheme.surface;
|
||||
final double elevation = widget.elevation ?? navigationRailTheme.elevation ?? 0;
|
||||
final double minWidth = widget.minWidth ?? _minRailWidth;
|
||||
final double minExtendedWidth = widget.minExtendedWidth ?? _minExtendedRailWidth;
|
||||
final Color baseSelectedColor = theme.colorScheme.primary;
|
||||
final Color baseUnselectedColor = theme.colorScheme.onSurface.withOpacity(0.64);
|
||||
final IconThemeData? defaultUnselectedIconTheme = widget.unselectedIconTheme ?? navigationRailTheme.unselectedIconTheme;
|
||||
final IconThemeData unselectedIconTheme = IconThemeData(
|
||||
size: defaultUnselectedIconTheme?.size ?? 24.0,
|
||||
color: defaultUnselectedIconTheme?.color ?? theme.colorScheme.onSurface,
|
||||
opacity: defaultUnselectedIconTheme?.opacity ?? 0.64,
|
||||
);
|
||||
final IconThemeData? defaultSelectedIconTheme = widget.selectedIconTheme ?? navigationRailTheme.selectedIconTheme;
|
||||
final IconThemeData selectedIconTheme = IconThemeData(
|
||||
size: defaultSelectedIconTheme?.size ?? 24.0,
|
||||
color: defaultSelectedIconTheme?.color ?? theme.colorScheme.primary,
|
||||
opacity: defaultSelectedIconTheme?.opacity ?? 1.0,
|
||||
);
|
||||
final TextStyle unselectedLabelTextStyle = theme.textTheme.bodyText1!.copyWith(color: baseUnselectedColor).merge(widget.unselectedLabelTextStyle ?? navigationRailTheme.unselectedLabelTextStyle);
|
||||
final TextStyle selectedLabelTextStyle = theme.textTheme.bodyText1!.copyWith(color: baseSelectedColor).merge(widget.selectedLabelTextStyle ?? navigationRailTheme.selectedLabelTextStyle);
|
||||
final double groupAlignment = widget.groupAlignment ?? navigationRailTheme.groupAlignment ?? -1.0;
|
||||
final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? NavigationRailLabelType.none;
|
||||
final Color backgroundColor = widget.backgroundColor ?? navigationRailTheme.backgroundColor ?? defaults.backgroundColor!;
|
||||
final double elevation = widget.elevation ?? navigationRailTheme.elevation ?? defaults.elevation!;
|
||||
final double minWidth = widget.minWidth ?? navigationRailTheme.minWidth ?? defaults.minWidth!;
|
||||
final double minExtendedWidth = widget.minExtendedWidth ?? navigationRailTheme.minExtendedWidth ?? defaults.minExtendedWidth!;
|
||||
final TextStyle unselectedLabelTextStyle = widget.unselectedLabelTextStyle ?? navigationRailTheme.unselectedLabelTextStyle ?? defaults.unselectedLabelTextStyle!;
|
||||
final TextStyle selectedLabelTextStyle = widget.selectedLabelTextStyle ?? navigationRailTheme.selectedLabelTextStyle ?? defaults.selectedLabelTextStyle!;
|
||||
final IconThemeData unselectedIconTheme = widget.unselectedIconTheme ?? navigationRailTheme.unselectedIconTheme ?? defaults.unselectedIconTheme!;
|
||||
final IconThemeData selectedIconTheme = widget.selectedIconTheme ?? navigationRailTheme.selectedIconTheme ?? defaults.selectedIconTheme!;
|
||||
final double groupAlignment = widget.groupAlignment ?? navigationRailTheme.groupAlignment ?? defaults.groupAlignment!;
|
||||
final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType!;
|
||||
final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator!;
|
||||
final Color? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
|
||||
|
||||
// For backwards compatibility, in M2 the opacity of the unselected icons needs
|
||||
// to be set to the default if it isn't in the given theme. This can be removed
|
||||
// when Material 3 is the default.
|
||||
final IconThemeData effectiveUnselectedIconTheme = Theme.of(context).useMaterial3
|
||||
? unselectedIconTheme
|
||||
: unselectedIconTheme.copyWith(opacity: unselectedIconTheme.opacity ?? defaults.unselectedIconTheme!.opacity);
|
||||
|
||||
return _ExtendedNavigationRailAnimation(
|
||||
animation: _extendedAnimation,
|
||||
@ -404,12 +401,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
|
||||
_verticalSpacer,
|
||||
if (widget.leading != null)
|
||||
...<Widget>[
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: lerpDouble(minWidth, minExtendedWidth, _extendedAnimation.value)!,
|
||||
),
|
||||
child: widget.leading,
|
||||
),
|
||||
widget.leading!,
|
||||
_verticalSpacer,
|
||||
],
|
||||
Expanded(
|
||||
@ -428,11 +420,11 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
|
||||
label: widget.destinations[i].label,
|
||||
destinationAnimation: _destinationAnimations[i],
|
||||
labelType: labelType,
|
||||
iconTheme: widget.selectedIndex == i ? selectedIconTheme : unselectedIconTheme,
|
||||
iconTheme: widget.selectedIndex == i ? selectedIconTheme : effectiveUnselectedIconTheme,
|
||||
labelTextStyle: widget.selectedIndex == i ? selectedLabelTextStyle : unselectedLabelTextStyle,
|
||||
padding: widget.destinations[i].padding,
|
||||
useIndicator: widget.useIndicator ?? navigationRailTheme.useIndicator ?? theme.useMaterial3,
|
||||
indicatorColor: widget.indicatorColor ?? navigationRailTheme.indicatorColor,
|
||||
useIndicator: useIndicator,
|
||||
indicatorColor: useIndicator ? indicatorColor : null,
|
||||
onTap: () {
|
||||
if (widget.onDestinationSelected != null)
|
||||
widget.onDestinationSelected!(i);
|
||||
@ -443,12 +435,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
|
||||
),
|
||||
),
|
||||
if (widget.trailing != null)
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: lerpDouble(minWidth, minExtendedWidth, _extendedAnimation.value)!,
|
||||
),
|
||||
child: widget.trailing,
|
||||
),
|
||||
widget.trailing!,
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -563,6 +550,8 @@ class _RailDestination extends StatelessWidget {
|
||||
'[NavigationRail.indicatorColor] does not have an effect when [NavigationRail.useIndicator] is false',
|
||||
);
|
||||
|
||||
final bool material3 = Theme.of(context).useMaterial3;
|
||||
|
||||
final Widget themedIcon = IconTheme(
|
||||
data: iconTheme,
|
||||
child: icon,
|
||||
@ -576,18 +565,27 @@ class _RailDestination extends StatelessWidget {
|
||||
|
||||
switch (labelType) {
|
||||
case NavigationRailLabelType.none:
|
||||
final Widget iconPart = SizedBox(
|
||||
width: minWidth,
|
||||
height: minWidth,
|
||||
child: Center(
|
||||
child: _AddIndicator(
|
||||
addIndicator: useIndicator,
|
||||
indicatorColor: indicatorColor,
|
||||
isCircular: true,
|
||||
indicatorAnimation: destinationAnimation,
|
||||
child: themedIcon,
|
||||
// Split the destination spacing across the top and bottom to keep the icon centered.
|
||||
final Widget? spacing = material3 ? const SizedBox(height: _verticalDestinationSpacingM3 / 2) : null;
|
||||
|
||||
final Widget iconPart = Column(
|
||||
children: <Widget>[
|
||||
if (spacing != null) spacing,
|
||||
SizedBox(
|
||||
width: minWidth,
|
||||
height: material3 ? null : minWidth,
|
||||
child: Center(
|
||||
child: _AddIndicator(
|
||||
addIndicator: useIndicator,
|
||||
indicatorColor: indicatorColor,
|
||||
isCircular: !material3,
|
||||
indicatorAnimation: destinationAnimation,
|
||||
child: themedIcon,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (spacing != null) spacing,
|
||||
],
|
||||
);
|
||||
if (extendedTransitionAnimation.value == 0) {
|
||||
content = Padding(
|
||||
@ -643,11 +641,15 @@ class _RailDestination extends StatelessWidget {
|
||||
final double verticalPadding = lerpDouble(_verticalDestinationPaddingNoLabel, _verticalDestinationPaddingWithLabel, appearingAnimationValue)!;
|
||||
final Interval interval = selected ? const Interval(0.25, 0.75) : const Interval(0.75, 1.0);
|
||||
final Animation<double> labelFadeAnimation = destinationAnimation.drive(CurveTween(curve: interval));
|
||||
final double minHeight = material3 ? 0 : minWidth;
|
||||
final Widget topSpacing = SizedBox(height: material3 ? 0 : verticalPadding);
|
||||
final Widget labelSpacing = SizedBox(height: material3 ? lerpDouble(0, _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0);
|
||||
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
|
||||
|
||||
content = Container(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: minWidth,
|
||||
minHeight: minWidth,
|
||||
minHeight: minHeight,
|
||||
),
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: _horizontalDestinationPadding),
|
||||
child: ClipRect(
|
||||
@ -655,7 +657,7 @@ class _RailDestination extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
SizedBox(height: verticalPadding),
|
||||
topSpacing,
|
||||
_AddIndicator(
|
||||
addIndicator: useIndicator,
|
||||
indicatorColor: indicatorColor,
|
||||
@ -663,6 +665,7 @@ class _RailDestination extends StatelessWidget {
|
||||
indicatorAnimation: destinationAnimation,
|
||||
child: themedIcon,
|
||||
),
|
||||
labelSpacing,
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
heightFactor: appearingAnimationValue,
|
||||
@ -673,22 +676,26 @@ class _RailDestination extends StatelessWidget {
|
||||
child: styledLabel,
|
||||
),
|
||||
),
|
||||
SizedBox(height: verticalPadding),
|
||||
bottomSpacing,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case NavigationRailLabelType.all:
|
||||
final double minHeight = material3 ? 0 : minWidth;
|
||||
final Widget topSpacing = SizedBox(height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
|
||||
final Widget labelSpacing = SizedBox(height: material3 ? _verticalIconLabelSpacingM3 : 0);
|
||||
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
|
||||
content = Container(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: minWidth,
|
||||
minHeight: minWidth,
|
||||
minHeight: minHeight,
|
||||
),
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: _horizontalDestinationPadding),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
const SizedBox(height: _verticalDestinationPaddingWithLabel),
|
||||
topSpacing,
|
||||
_AddIndicator(
|
||||
addIndicator: useIndicator,
|
||||
indicatorColor: indicatorColor,
|
||||
@ -696,8 +703,9 @@ class _RailDestination extends StatelessWidget {
|
||||
indicatorAnimation: destinationAnimation,
|
||||
child: themedIcon,
|
||||
),
|
||||
labelSpacing,
|
||||
styledLabel,
|
||||
const SizedBox(height: _verticalDestinationPaddingWithLabel),
|
||||
bottomSpacing,
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -716,7 +724,7 @@ class _RailDestination extends StatelessWidget {
|
||||
onTap: onTap,
|
||||
onHover: (_) {},
|
||||
highlightShape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.all(Radius.circular(minWidth / 2.0)),
|
||||
borderRadius: material3 ? null : BorderRadius.all(Radius.circular(minWidth / 2.0)),
|
||||
containedInkWell: true,
|
||||
splashColor: colors.primary.withOpacity(0.12),
|
||||
hoverColor: colors.primary.withOpacity(0.04),
|
||||
@ -772,7 +780,7 @@ class _AddIndicator extends StatelessWidget {
|
||||
indicator = NavigationIndicator(
|
||||
animation: indicatorAnimation,
|
||||
width: 56,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
shape: const StadiumBorder(),
|
||||
color: indicatorColor,
|
||||
);
|
||||
}
|
||||
@ -880,9 +888,107 @@ class _ExtendedNavigationRailAnimation extends InheritedWidget {
|
||||
bool updateShouldNotify(_ExtendedNavigationRailAnimation old) => animation != old.animation;
|
||||
}
|
||||
|
||||
const double _minRailWidth = 72.0;
|
||||
const double _minExtendedRailWidth = 256.0;
|
||||
// There don't appear to be tokens for these values, but they are
|
||||
// shown in the spec.
|
||||
const double _horizontalDestinationPadding = 8.0;
|
||||
const double _verticalDestinationPaddingNoLabel = 24.0;
|
||||
const double _verticalDestinationPaddingWithLabel = 16.0;
|
||||
const Widget _verticalSpacer = SizedBox(height: 8.0);
|
||||
const double _verticalIconLabelSpacingM3 = 4.0;
|
||||
const double _verticalDestinationSpacingM3 = 12.0;
|
||||
|
||||
class _DefaultsM2 extends NavigationRailThemeData {
|
||||
_DefaultsM2(BuildContext context)
|
||||
: _theme = Theme.of(context),
|
||||
_colors = Theme.of(context).colorScheme,
|
||||
super(
|
||||
elevation: 0,
|
||||
groupAlignment: -1,
|
||||
labelType: NavigationRailLabelType.none,
|
||||
useIndicator: false,
|
||||
minWidth: 72.0,
|
||||
minExtendedWidth: 256,
|
||||
);
|
||||
|
||||
final ThemeData _theme;
|
||||
final ColorScheme _colors;
|
||||
|
||||
@override Color? get backgroundColor => _colors.surface;
|
||||
|
||||
@override TextStyle? get unselectedLabelTextStyle {
|
||||
return _theme.textTheme.bodyText1!.copyWith(color: _colors.onSurface.withOpacity(0.64));
|
||||
}
|
||||
|
||||
@override TextStyle? get selectedLabelTextStyle {
|
||||
return _theme.textTheme.bodyText1!.copyWith(color: _colors.primary);
|
||||
}
|
||||
|
||||
@override IconThemeData? get unselectedIconTheme {
|
||||
return IconThemeData(
|
||||
size: 24.0,
|
||||
color: _colors.onSurface,
|
||||
opacity: 0.64,
|
||||
);
|
||||
}
|
||||
|
||||
@override IconThemeData? get selectedIconTheme {
|
||||
return IconThemeData(
|
||||
size: 24.0,
|
||||
color: _colors.primary,
|
||||
opacity: 1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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_90
|
||||
class _TokenDefaultsM3 extends NavigationRailThemeData {
|
||||
_TokenDefaultsM3(BuildContext context)
|
||||
: _theme = Theme.of(context),
|
||||
_colors = Theme.of(context).colorScheme,
|
||||
super(
|
||||
elevation: 0.0,
|
||||
groupAlignment: -1,
|
||||
labelType: NavigationRailLabelType.none,
|
||||
useIndicator: true,
|
||||
minWidth: 80.0,
|
||||
minExtendedWidth: 256,
|
||||
);
|
||||
|
||||
final ThemeData _theme;
|
||||
final ColorScheme _colors;
|
||||
|
||||
@override Color? get backgroundColor => _colors.surface;
|
||||
|
||||
@override TextStyle? get unselectedLabelTextStyle {
|
||||
return _theme.textTheme.labelMedium!.copyWith(color: _colors.onSurface);
|
||||
}
|
||||
|
||||
@override TextStyle? get selectedLabelTextStyle {
|
||||
return _theme.textTheme.labelMedium!.copyWith(color: _colors.onSurface);
|
||||
}
|
||||
|
||||
@override IconThemeData? get unselectedIconTheme {
|
||||
return IconThemeData(
|
||||
size: 24.0,
|
||||
color: _colors.onSurfaceVariant,
|
||||
);
|
||||
}
|
||||
|
||||
@override IconThemeData? get selectedIconTheme {
|
||||
return IconThemeData(
|
||||
size: 24.0,
|
||||
color: _colors.onSecondaryContainer,
|
||||
);
|
||||
}
|
||||
|
||||
@override Color? get indicatorColor => _colors.secondaryContainer;
|
||||
|
||||
}
|
||||
|
||||
// END GENERATED TOKEN PROPERTIES
|
||||
|
@ -46,6 +46,8 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
this.labelType,
|
||||
this.useIndicator,
|
||||
this.indicatorColor,
|
||||
this.minWidth,
|
||||
this.minExtendedWidth,
|
||||
});
|
||||
|
||||
/// Color to be used for the [NavigationRail]'s background.
|
||||
@ -86,6 +88,14 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
/// when [useIndicator] is true.
|
||||
final Color? indicatorColor;
|
||||
|
||||
/// Overrides the default value of [NavigationRail]'s minimum width when it
|
||||
/// is not extended.
|
||||
final double? minWidth;
|
||||
|
||||
/// Overrides the default value of [NavigationRail]'s minimum width when it
|
||||
/// is extended.
|
||||
final double? minExtendedWidth;
|
||||
|
||||
/// Creates a copy of this object with the given fields replaced with the
|
||||
/// new values.
|
||||
NavigationRailThemeData copyWith({
|
||||
@ -99,6 +109,8 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
NavigationRailLabelType? labelType,
|
||||
bool? useIndicator,
|
||||
Color? indicatorColor,
|
||||
double? minWidth,
|
||||
double? minExtendedWidth,
|
||||
}) {
|
||||
return NavigationRailThemeData(
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
@ -111,6 +123,8 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
labelType: labelType ?? this.labelType,
|
||||
useIndicator: useIndicator ?? this.useIndicator,
|
||||
indicatorColor: indicatorColor ?? this.indicatorColor,
|
||||
minWidth: minWidth ?? this.minWidth,
|
||||
minExtendedWidth: minExtendedWidth ?? this.minExtendedWidth,
|
||||
);
|
||||
}
|
||||
|
||||
@ -134,6 +148,9 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
labelType: t < 0.5 ? a?.labelType : b?.labelType,
|
||||
useIndicator: t < 0.5 ? a?.useIndicator : b?.useIndicator,
|
||||
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
|
||||
minWidth: lerpDouble(a?.minWidth, b?.minWidth, t),
|
||||
minExtendedWidth: lerpDouble(a?.minExtendedWidth, b?.minExtendedWidth, t),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
@ -149,6 +166,8 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
labelType,
|
||||
useIndicator,
|
||||
indicatorColor,
|
||||
minWidth,
|
||||
minExtendedWidth,
|
||||
);
|
||||
|
||||
@override
|
||||
@ -167,7 +186,9 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
&& other.groupAlignment == groupAlignment
|
||||
&& other.labelType == labelType
|
||||
&& other.useIndicator == useIndicator
|
||||
&& other.indicatorColor == indicatorColor;
|
||||
&& other.indicatorColor == indicatorColor
|
||||
&& other.minWidth == minWidth
|
||||
&& other.minExtendedWidth == minExtendedWidth;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -185,6 +206,8 @@ class NavigationRailThemeData with Diagnosticable {
|
||||
properties.add(DiagnosticsProperty<NavigationRailLabelType>('labelType', labelType, defaultValue: defaultData.labelType));
|
||||
properties.add(DiagnosticsProperty<bool>('useIndicator', useIndicator, defaultValue: defaultData.useIndicator));
|
||||
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: defaultData.indicatorColor));
|
||||
properties.add(DoubleProperty('minWidth', minWidth, defaultValue: defaultData.minWidth));
|
||||
properties.add(DoubleProperty('minExtendedWidth', minExtendedWidth, defaultValue: defaultData.minExtendedWidth));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1087,8 +1087,12 @@ class ThemeData with Diagnosticable {
|
||||
/// start using new colors, typography and other features of Material 3.
|
||||
/// If false, they will use the Material 2 look and feel.
|
||||
///
|
||||
/// If true, the default Typography will be [Typography.material2021],
|
||||
/// otherwise it will default to [Typography.material2014].
|
||||
/// If a [ThemeData] is constructed with [useMaterial3] set to true,
|
||||
/// the default [typography] will be [Typography.material2021],
|
||||
/// otherwise it will be [Typography.material2014].
|
||||
///
|
||||
/// However, just copying a [ThemeData] with [useMaterial3] set to true will
|
||||
/// not change the typography of the resulting ThemeData.
|
||||
///
|
||||
/// During the migration to Material 3, turning this on may yield
|
||||
/// inconsistent look and feel in your app. Some components will be migrated
|
||||
@ -1102,10 +1106,11 @@ class ThemeData with Diagnosticable {
|
||||
///
|
||||
/// Components that have been migrated to Material 3 are:
|
||||
///
|
||||
/// * [AlertDialog]
|
||||
/// * [Dialog]
|
||||
/// * [FloatingActionButton]
|
||||
/// * [NavigationBar]
|
||||
/// * [Dialog]
|
||||
/// * [AlertDialog]
|
||||
/// * [NavigationRail]
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,8 +13,10 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('Default values are used when no NavigationRail or NavigationRailThemeData properties are specified', (WidgetTester tester) async {
|
||||
// Material 3 defaults
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData.light().copyWith(useMaterial3: true),
|
||||
home: Scaffold(
|
||||
body: NavigationRail(
|
||||
selectedIndex: 0,
|
||||
@ -26,6 +28,37 @@ void main() {
|
||||
|
||||
expect(_railMaterial(tester).color, ThemeData().colorScheme.surface);
|
||||
expect(_railMaterial(tester).elevation, 0);
|
||||
expect(_destinationSize(tester).width, 80.0);
|
||||
expect(_selectedIconTheme(tester).size, 24.0);
|
||||
expect(_selectedIconTheme(tester).color, ThemeData().colorScheme.onSecondaryContainer);
|
||||
expect(_selectedIconTheme(tester).opacity, null);
|
||||
expect(_unselectedIconTheme(tester).size, 24.0);
|
||||
expect(_unselectedIconTheme(tester).color, ThemeData().colorScheme.onSurface);
|
||||
expect(_unselectedIconTheme(tester).opacity, null);
|
||||
expect(_selectedLabelStyle(tester).fontSize, 14.0);
|
||||
expect(_unselectedLabelStyle(tester).fontSize, 14.0);
|
||||
expect(_destinationsAlign(tester).alignment, Alignment.topCenter);
|
||||
expect(_labelType(tester), NavigationRailLabelType.none);
|
||||
expect(find.byType(NavigationIndicator), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('Default values are used when no NavigationRail or NavigationRailThemeData properties are specified (Material 2)', (WidgetTester tester) async {
|
||||
// This test can be removed when `useMaterial3` is deprecated.
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: ThemeData.light().copyWith(useMaterial3: false),
|
||||
home: Scaffold(
|
||||
body: NavigationRail(
|
||||
selectedIndex: 0,
|
||||
destinations: _destinations(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(_railMaterial(tester).color, ThemeData().colorScheme.surface);
|
||||
expect(_railMaterial(tester).elevation, 0);
|
||||
expect(_destinationSize(tester).width, 72.0);
|
||||
expect(_selectedIconTheme(tester).size, 24.0);
|
||||
expect(_selectedIconTheme(tester).color, ThemeData().colorScheme.primary);
|
||||
expect(_selectedIconTheme(tester).opacity, 1.0);
|
||||
@ -308,6 +341,16 @@ TextStyle _unselectedLabelStyle(WidgetTester tester) {
|
||||
).text.style!;
|
||||
}
|
||||
|
||||
Size _destinationSize(WidgetTester tester) {
|
||||
return tester.getSize(
|
||||
find.ancestor(
|
||||
of: find.byIcon(Icons.favorite),
|
||||
matching: find.byType(Material),
|
||||
)
|
||||
.first
|
||||
);
|
||||
}
|
||||
|
||||
Align _destinationsAlign(WidgetTester tester) {
|
||||
// The first Expanded widget is the one within the main Column for the rail
|
||||
// content.
|
||||
|
Loading…
x
Reference in New Issue
Block a user