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';
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();
}
// 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:
///
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -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.
......
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