Unverified Commit 57aa4902 authored by Darren Austin's avatar Darren Austin Committed by GitHub

Migrate NavigationRail to Material 3. (#99171)

parent 9291c8ba
...@@ -20,6 +20,7 @@ import 'dart:io'; ...@@ -20,6 +20,7 @@ import 'dart:io';
import 'package:gen_defaults/dialog_template.dart'; import 'package:gen_defaults/dialog_template.dart';
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/navigation_bar_template.dart';
import 'package:gen_defaults/navigation_rail_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) {
...@@ -45,6 +46,7 @@ Future<void> main(List<String> args) async { ...@@ -45,6 +46,7 @@ Future<void> main(List<String> args) async {
'input_chip.json', 'input_chip.json',
'motion.json', 'motion.json',
'navigation_bar.json', 'navigation_bar.json',
'navigation_rail.json',
'outlined_card.json', 'outlined_card.json',
'palette.json', 'palette.json',
'shape.json', 'shape.json',
...@@ -70,6 +72,7 @@ Future<void> main(List<String> args) async { ...@@ -70,6 +72,7 @@ Future<void> main(List<String> args) async {
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile(); FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
NavigationBarTemplate('$materialLib/navigation_bar.dart', tokens).updateFile(); NavigationBarTemplate('$materialLib/navigation_bar.dart', tokens).updateFile();
NavigationRailTemplate('$materialLib/navigation_rail.dart', tokens).updateFile();
TypographyTemplate('$materialLib/typography.dart', tokens).updateFile(); TypographyTemplate('$materialLib/typography.dart', tokens).updateFile();
DialogTemplate('$materialLib/dialog.dart', tokens).updateFile(); DialogTemplate('$materialLib/dialog.dart', tokens).updateFile();
} }
// 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 ...@@ -365,32 +365,29 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final NavigationRailThemeData navigationRailTheme = NavigationRailTheme.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 MaterialLocalizations localizations = MaterialLocalizations.of(context);
final Color backgroundColor = widget.backgroundColor ?? navigationRailTheme.backgroundColor ?? theme.colorScheme.surface; final Color backgroundColor = widget.backgroundColor ?? navigationRailTheme.backgroundColor ?? defaults.backgroundColor!;
final double elevation = widget.elevation ?? navigationRailTheme.elevation ?? 0; final double elevation = widget.elevation ?? navigationRailTheme.elevation ?? defaults.elevation!;
final double minWidth = widget.minWidth ?? _minRailWidth; final double minWidth = widget.minWidth ?? navigationRailTheme.minWidth ?? defaults.minWidth!;
final double minExtendedWidth = widget.minExtendedWidth ?? _minExtendedRailWidth; final double minExtendedWidth = widget.minExtendedWidth ?? navigationRailTheme.minExtendedWidth ?? defaults.minExtendedWidth!;
final Color baseSelectedColor = theme.colorScheme.primary; final TextStyle unselectedLabelTextStyle = widget.unselectedLabelTextStyle ?? navigationRailTheme.unselectedLabelTextStyle ?? defaults.unselectedLabelTextStyle!;
final Color baseUnselectedColor = theme.colorScheme.onSurface.withOpacity(0.64); final TextStyle selectedLabelTextStyle = widget.selectedLabelTextStyle ?? navigationRailTheme.selectedLabelTextStyle ?? defaults.selectedLabelTextStyle!;
final IconThemeData? defaultUnselectedIconTheme = widget.unselectedIconTheme ?? navigationRailTheme.unselectedIconTheme; final IconThemeData unselectedIconTheme = widget.unselectedIconTheme ?? navigationRailTheme.unselectedIconTheme ?? defaults.unselectedIconTheme!;
final IconThemeData unselectedIconTheme = IconThemeData( final IconThemeData selectedIconTheme = widget.selectedIconTheme ?? navigationRailTheme.selectedIconTheme ?? defaults.selectedIconTheme!;
size: defaultUnselectedIconTheme?.size ?? 24.0, final double groupAlignment = widget.groupAlignment ?? navigationRailTheme.groupAlignment ?? defaults.groupAlignment!;
color: defaultUnselectedIconTheme?.color ?? theme.colorScheme.onSurface, final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType!;
opacity: defaultUnselectedIconTheme?.opacity ?? 0.64, final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator!;
); final Color? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
final IconThemeData? defaultSelectedIconTheme = widget.selectedIconTheme ?? navigationRailTheme.selectedIconTheme;
final IconThemeData selectedIconTheme = IconThemeData( // For backwards compatibility, in M2 the opacity of the unselected icons needs
size: defaultSelectedIconTheme?.size ?? 24.0, // to be set to the default if it isn't in the given theme. This can be removed
color: defaultSelectedIconTheme?.color ?? theme.colorScheme.primary, // when Material 3 is the default.
opacity: defaultSelectedIconTheme?.opacity ?? 1.0, final IconThemeData effectiveUnselectedIconTheme = Theme.of(context).useMaterial3
); ? unselectedIconTheme
final TextStyle unselectedLabelTextStyle = theme.textTheme.bodyText1!.copyWith(color: baseUnselectedColor).merge(widget.unselectedLabelTextStyle ?? navigationRailTheme.unselectedLabelTextStyle); : unselectedIconTheme.copyWith(opacity: unselectedIconTheme.opacity ?? defaults.unselectedIconTheme!.opacity);
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;
return _ExtendedNavigationRailAnimation( return _ExtendedNavigationRailAnimation(
animation: _extendedAnimation, animation: _extendedAnimation,
...@@ -404,12 +401,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat ...@@ -404,12 +401,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
_verticalSpacer, _verticalSpacer,
if (widget.leading != null) if (widget.leading != null)
...<Widget>[ ...<Widget>[
ConstrainedBox( widget.leading!,
constraints: BoxConstraints(
minWidth: lerpDouble(minWidth, minExtendedWidth, _extendedAnimation.value)!,
),
child: widget.leading,
),
_verticalSpacer, _verticalSpacer,
], ],
Expanded( Expanded(
...@@ -428,11 +420,11 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat ...@@ -428,11 +420,11 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
label: widget.destinations[i].label, label: widget.destinations[i].label,
destinationAnimation: _destinationAnimations[i], destinationAnimation: _destinationAnimations[i],
labelType: labelType, labelType: labelType,
iconTheme: widget.selectedIndex == i ? selectedIconTheme : unselectedIconTheme, iconTheme: widget.selectedIndex == i ? selectedIconTheme : effectiveUnselectedIconTheme,
labelTextStyle: widget.selectedIndex == i ? selectedLabelTextStyle : unselectedLabelTextStyle, labelTextStyle: widget.selectedIndex == i ? selectedLabelTextStyle : unselectedLabelTextStyle,
padding: widget.destinations[i].padding, padding: widget.destinations[i].padding,
useIndicator: widget.useIndicator ?? navigationRailTheme.useIndicator ?? theme.useMaterial3, useIndicator: useIndicator,
indicatorColor: widget.indicatorColor ?? navigationRailTheme.indicatorColor, indicatorColor: useIndicator ? indicatorColor : null,
onTap: () { onTap: () {
if (widget.onDestinationSelected != null) if (widget.onDestinationSelected != null)
widget.onDestinationSelected!(i); widget.onDestinationSelected!(i);
...@@ -443,12 +435,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat ...@@ -443,12 +435,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
), ),
), ),
if (widget.trailing != null) if (widget.trailing != null)
ConstrainedBox( widget.trailing!,
constraints: BoxConstraints(
minWidth: lerpDouble(minWidth, minExtendedWidth, _extendedAnimation.value)!,
),
child: widget.trailing,
),
], ],
), ),
), ),
...@@ -563,6 +550,8 @@ class _RailDestination extends StatelessWidget { ...@@ -563,6 +550,8 @@ class _RailDestination extends StatelessWidget {
'[NavigationRail.indicatorColor] does not have an effect when [NavigationRail.useIndicator] is false', '[NavigationRail.indicatorColor] does not have an effect when [NavigationRail.useIndicator] is false',
); );
final bool material3 = Theme.of(context).useMaterial3;
final Widget themedIcon = IconTheme( final Widget themedIcon = IconTheme(
data: iconTheme, data: iconTheme,
child: icon, child: icon,
...@@ -576,18 +565,27 @@ class _RailDestination extends StatelessWidget { ...@@ -576,18 +565,27 @@ class _RailDestination extends StatelessWidget {
switch (labelType) { switch (labelType) {
case NavigationRailLabelType.none: case NavigationRailLabelType.none:
final Widget iconPart = SizedBox( // 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, width: minWidth,
height: minWidth, height: material3 ? null : minWidth,
child: Center( child: Center(
child: _AddIndicator( child: _AddIndicator(
addIndicator: useIndicator, addIndicator: useIndicator,
indicatorColor: indicatorColor, indicatorColor: indicatorColor,
isCircular: true, isCircular: !material3,
indicatorAnimation: destinationAnimation, indicatorAnimation: destinationAnimation,
child: themedIcon, child: themedIcon,
), ),
), ),
),
if (spacing != null) spacing,
],
); );
if (extendedTransitionAnimation.value == 0) { if (extendedTransitionAnimation.value == 0) {
content = Padding( content = Padding(
...@@ -643,11 +641,15 @@ class _RailDestination extends StatelessWidget { ...@@ -643,11 +641,15 @@ class _RailDestination extends StatelessWidget {
final double verticalPadding = lerpDouble(_verticalDestinationPaddingNoLabel, _verticalDestinationPaddingWithLabel, appearingAnimationValue)!; final double verticalPadding = lerpDouble(_verticalDestinationPaddingNoLabel, _verticalDestinationPaddingWithLabel, appearingAnimationValue)!;
final Interval interval = selected ? const Interval(0.25, 0.75) : const Interval(0.75, 1.0); 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 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( content = Container(
constraints: BoxConstraints( constraints: BoxConstraints(
minWidth: minWidth, minWidth: minWidth,
minHeight: minWidth, minHeight: minHeight,
), ),
padding: padding ?? const EdgeInsets.symmetric(horizontal: _horizontalDestinationPadding), padding: padding ?? const EdgeInsets.symmetric(horizontal: _horizontalDestinationPadding),
child: ClipRect( child: ClipRect(
...@@ -655,7 +657,7 @@ class _RailDestination extends StatelessWidget { ...@@ -655,7 +657,7 @@ class _RailDestination extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
SizedBox(height: verticalPadding), topSpacing,
_AddIndicator( _AddIndicator(
addIndicator: useIndicator, addIndicator: useIndicator,
indicatorColor: indicatorColor, indicatorColor: indicatorColor,
...@@ -663,6 +665,7 @@ class _RailDestination extends StatelessWidget { ...@@ -663,6 +665,7 @@ class _RailDestination extends StatelessWidget {
indicatorAnimation: destinationAnimation, indicatorAnimation: destinationAnimation,
child: themedIcon, child: themedIcon,
), ),
labelSpacing,
Align( Align(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
heightFactor: appearingAnimationValue, heightFactor: appearingAnimationValue,
...@@ -673,22 +676,26 @@ class _RailDestination extends StatelessWidget { ...@@ -673,22 +676,26 @@ class _RailDestination extends StatelessWidget {
child: styledLabel, child: styledLabel,
), ),
), ),
SizedBox(height: verticalPadding), bottomSpacing,
], ],
), ),
), ),
); );
break; break;
case NavigationRailLabelType.all: 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( content = Container(
constraints: BoxConstraints( constraints: BoxConstraints(
minWidth: minWidth, minWidth: minWidth,
minHeight: minWidth, minHeight: minHeight,
), ),
padding: padding ?? const EdgeInsets.symmetric(horizontal: _horizontalDestinationPadding), padding: padding ?? const EdgeInsets.symmetric(horizontal: _horizontalDestinationPadding),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
const SizedBox(height: _verticalDestinationPaddingWithLabel), topSpacing,
_AddIndicator( _AddIndicator(
addIndicator: useIndicator, addIndicator: useIndicator,
indicatorColor: indicatorColor, indicatorColor: indicatorColor,
...@@ -696,8 +703,9 @@ class _RailDestination extends StatelessWidget { ...@@ -696,8 +703,9 @@ class _RailDestination extends StatelessWidget {
indicatorAnimation: destinationAnimation, indicatorAnimation: destinationAnimation,
child: themedIcon, child: themedIcon,
), ),
labelSpacing,
styledLabel, styledLabel,
const SizedBox(height: _verticalDestinationPaddingWithLabel), bottomSpacing,
], ],
), ),
); );
...@@ -716,7 +724,7 @@ class _RailDestination extends StatelessWidget { ...@@ -716,7 +724,7 @@ class _RailDestination extends StatelessWidget {
onTap: onTap, onTap: onTap,
onHover: (_) {}, onHover: (_) {},
highlightShape: BoxShape.rectangle, highlightShape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(minWidth / 2.0)), borderRadius: material3 ? null : BorderRadius.all(Radius.circular(minWidth / 2.0)),
containedInkWell: true, containedInkWell: true,
splashColor: colors.primary.withOpacity(0.12), splashColor: colors.primary.withOpacity(0.12),
hoverColor: colors.primary.withOpacity(0.04), hoverColor: colors.primary.withOpacity(0.04),
...@@ -772,7 +780,7 @@ class _AddIndicator extends StatelessWidget { ...@@ -772,7 +780,7 @@ class _AddIndicator extends StatelessWidget {
indicator = NavigationIndicator( indicator = NavigationIndicator(
animation: indicatorAnimation, animation: indicatorAnimation,
width: 56, width: 56,
borderRadius: BorderRadius.circular(16), shape: const StadiumBorder(),
color: indicatorColor, color: indicatorColor,
); );
} }
...@@ -880,9 +888,107 @@ class _ExtendedNavigationRailAnimation extends InheritedWidget { ...@@ -880,9 +888,107 @@ class _ExtendedNavigationRailAnimation extends InheritedWidget {
bool updateShouldNotify(_ExtendedNavigationRailAnimation old) => animation != old.animation; bool updateShouldNotify(_ExtendedNavigationRailAnimation old) => animation != old.animation;
} }
const double _minRailWidth = 72.0; // There don't appear to be tokens for these values, but they are
const double _minExtendedRailWidth = 256.0; // shown in the spec.
const double _horizontalDestinationPadding = 8.0; const double _horizontalDestinationPadding = 8.0;
const double _verticalDestinationPaddingNoLabel = 24.0; const double _verticalDestinationPaddingNoLabel = 24.0;
const double _verticalDestinationPaddingWithLabel = 16.0; const double _verticalDestinationPaddingWithLabel = 16.0;
const Widget _verticalSpacer = SizedBox(height: 8.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 { ...@@ -46,6 +46,8 @@ class NavigationRailThemeData with Diagnosticable {
this.labelType, this.labelType,
this.useIndicator, this.useIndicator,
this.indicatorColor, this.indicatorColor,
this.minWidth,
this.minExtendedWidth,
}); });
/// Color to be used for the [NavigationRail]'s background. /// Color to be used for the [NavigationRail]'s background.
...@@ -86,6 +88,14 @@ class NavigationRailThemeData with Diagnosticable { ...@@ -86,6 +88,14 @@ class NavigationRailThemeData with Diagnosticable {
/// when [useIndicator] is true. /// when [useIndicator] is true.
final Color? indicatorColor; 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 /// Creates a copy of this object with the given fields replaced with the
/// new values. /// new values.
NavigationRailThemeData copyWith({ NavigationRailThemeData copyWith({
...@@ -99,6 +109,8 @@ class NavigationRailThemeData with Diagnosticable { ...@@ -99,6 +109,8 @@ class NavigationRailThemeData with Diagnosticable {
NavigationRailLabelType? labelType, NavigationRailLabelType? labelType,
bool? useIndicator, bool? useIndicator,
Color? indicatorColor, Color? indicatorColor,
double? minWidth,
double? minExtendedWidth,
}) { }) {
return NavigationRailThemeData( return NavigationRailThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
...@@ -111,6 +123,8 @@ class NavigationRailThemeData with Diagnosticable { ...@@ -111,6 +123,8 @@ class NavigationRailThemeData with Diagnosticable {
labelType: labelType ?? this.labelType, labelType: labelType ?? this.labelType,
useIndicator: useIndicator ?? this.useIndicator, useIndicator: useIndicator ?? this.useIndicator,
indicatorColor: indicatorColor ?? this.indicatorColor, indicatorColor: indicatorColor ?? this.indicatorColor,
minWidth: minWidth ?? this.minWidth,
minExtendedWidth: minExtendedWidth ?? this.minExtendedWidth,
); );
} }
...@@ -134,6 +148,9 @@ class NavigationRailThemeData with Diagnosticable { ...@@ -134,6 +148,9 @@ class NavigationRailThemeData with Diagnosticable {
labelType: t < 0.5 ? a?.labelType : b?.labelType, labelType: t < 0.5 ? a?.labelType : b?.labelType,
useIndicator: t < 0.5 ? a?.useIndicator : b?.useIndicator, useIndicator: t < 0.5 ? a?.useIndicator : b?.useIndicator,
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t), 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 { ...@@ -149,6 +166,8 @@ class NavigationRailThemeData with Diagnosticable {
labelType, labelType,
useIndicator, useIndicator,
indicatorColor, indicatorColor,
minWidth,
minExtendedWidth,
); );
@override @override
...@@ -167,7 +186,9 @@ class NavigationRailThemeData with Diagnosticable { ...@@ -167,7 +186,9 @@ class NavigationRailThemeData with Diagnosticable {
&& other.groupAlignment == groupAlignment && other.groupAlignment == groupAlignment
&& other.labelType == labelType && other.labelType == labelType
&& other.useIndicator == useIndicator && other.useIndicator == useIndicator
&& other.indicatorColor == indicatorColor; && other.indicatorColor == indicatorColor
&& other.minWidth == minWidth
&& other.minExtendedWidth == minExtendedWidth;
} }
@override @override
...@@ -185,6 +206,8 @@ class NavigationRailThemeData with Diagnosticable { ...@@ -185,6 +206,8 @@ class NavigationRailThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<NavigationRailLabelType>('labelType', labelType, defaultValue: defaultData.labelType)); properties.add(DiagnosticsProperty<NavigationRailLabelType>('labelType', labelType, defaultValue: defaultData.labelType));
properties.add(DiagnosticsProperty<bool>('useIndicator', useIndicator, defaultValue: defaultData.useIndicator)); properties.add(DiagnosticsProperty<bool>('useIndicator', useIndicator, defaultValue: defaultData.useIndicator));
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: defaultData.indicatorColor)); 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 { ...@@ -1087,8 +1087,12 @@ class ThemeData with Diagnosticable {
/// start using new colors, typography and other features of Material 3. /// start using new colors, typography and other features of Material 3.
/// If false, they will use the Material 2 look and feel. /// If false, they will use the Material 2 look and feel.
/// ///
/// If true, the default Typography will be [Typography.material2021], /// If a [ThemeData] is constructed with [useMaterial3] set to true,
/// otherwise it will default to [Typography.material2014]. /// 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 /// During the migration to Material 3, turning this on may yield
/// inconsistent look and feel in your app. Some components will be migrated /// inconsistent look and feel in your app. Some components will be migrated
...@@ -1102,10 +1106,11 @@ class ThemeData with Diagnosticable { ...@@ -1102,10 +1106,11 @@ class ThemeData with Diagnosticable {
/// ///
/// Components that have been migrated to Material 3 are: /// Components that have been migrated to Material 3 are:
/// ///
/// * [AlertDialog]
/// * [Dialog]
/// * [FloatingActionButton] /// * [FloatingActionButton]
/// * [NavigationBar] /// * [NavigationBar]
/// * [Dialog] /// * [NavigationRail]
/// * [AlertDialog]
/// ///
/// See also: /// See also:
/// ///
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -13,8 +13,10 @@ void main() { ...@@ -13,8 +13,10 @@ void main() {
}); });
testWidgets('Default values are used when no NavigationRail or NavigationRailThemeData properties are specified', (WidgetTester tester) async { testWidgets('Default values are used when no NavigationRail or NavigationRailThemeData properties are specified', (WidgetTester tester) async {
// Material 3 defaults
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
theme: ThemeData.light().copyWith(useMaterial3: true),
home: Scaffold( home: Scaffold(
body: NavigationRail( body: NavigationRail(
selectedIndex: 0, selectedIndex: 0,
...@@ -26,6 +28,37 @@ void main() { ...@@ -26,6 +28,37 @@ void main() {
expect(_railMaterial(tester).color, ThemeData().colorScheme.surface); expect(_railMaterial(tester).color, ThemeData().colorScheme.surface);
expect(_railMaterial(tester).elevation, 0); 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).size, 24.0);
expect(_selectedIconTheme(tester).color, ThemeData().colorScheme.primary); expect(_selectedIconTheme(tester).color, ThemeData().colorScheme.primary);
expect(_selectedIconTheme(tester).opacity, 1.0); expect(_selectedIconTheme(tester).opacity, 1.0);
...@@ -308,6 +341,16 @@ TextStyle _unselectedLabelStyle(WidgetTester tester) { ...@@ -308,6 +341,16 @@ TextStyle _unselectedLabelStyle(WidgetTester tester) {
).text.style!; ).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) { Align _destinationsAlign(WidgetTester tester) {
// The first Expanded widget is the one within the main Column for the rail // The first Expanded widget is the one within the main Column for the rail
// content. // content.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment