Add IndicatorShape
to NavigationRailTheme
and fix indicator ripple. (#116108)
* Add `IndicatorShape` to `NavigationRailTheme` and fix indicator ripple. * remove unused variables
This commit is contained in:
parent
215f6372c6
commit
beaabb70c5
@ -14,14 +14,14 @@ class NavigationRailTemplate extends TokenTemplate {
|
|||||||
String generate() => '''
|
String generate() => '''
|
||||||
class _${blockName}DefaultsM3 extends NavigationRailThemeData {
|
class _${blockName}DefaultsM3 extends NavigationRailThemeData {
|
||||||
_${blockName}DefaultsM3(this.context)
|
_${blockName}DefaultsM3(this.context)
|
||||||
: super(
|
: super(
|
||||||
elevation: ${elevation("md.comp.navigation-rail.container")},
|
elevation: ${elevation("md.comp.navigation-rail.container")},
|
||||||
groupAlignment: -1,
|
groupAlignment: -1,
|
||||||
labelType: NavigationRailLabelType.none,
|
labelType: NavigationRailLabelType.none,
|
||||||
useIndicator: true,
|
useIndicator: true,
|
||||||
minWidth: ${tokens["md.comp.navigation-rail.container.width"]},
|
minWidth: ${tokens["md.comp.navigation-rail.container.width"]},
|
||||||
minExtendedWidth: 256,
|
minExtendedWidth: 256,
|
||||||
);
|
);
|
||||||
|
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||||
@ -53,6 +53,7 @@ class _${blockName}DefaultsM3 extends NavigationRailThemeData {
|
|||||||
|
|
||||||
@override Color? get indicatorColor => ${componentColor("md.comp.navigation-rail.active-indicator")};
|
@override Color? get indicatorColor => ${componentColor("md.comp.navigation-rail.active-indicator")};
|
||||||
|
|
||||||
|
@override ShapeBorder? get indicatorShape => ${shape("md.comp.navigation-rail.active-indicator")};
|
||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ import 'navigation_rail_theme.dart';
|
|||||||
import 'text_theme.dart';
|
import 'text_theme.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
|
const double _kCircularIndicatorDiameter = 56;
|
||||||
|
|
||||||
/// A Material Design widget that is meant to be displayed at the left or right of an
|
/// A Material Design widget that is meant to be displayed at the left or right of an
|
||||||
/// app to navigate between a small number of views, typically between three and
|
/// app to navigate between a small number of views, typically between three and
|
||||||
/// five.
|
/// five.
|
||||||
@ -394,6 +396,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
|
|||||||
final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType!;
|
final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType!;
|
||||||
final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator!;
|
final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator!;
|
||||||
final Color? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
|
final Color? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
|
||||||
|
final ShapeBorder? indicatorShape = navigationRailTheme.indicatorShape ?? defaults.indicatorShape;
|
||||||
|
|
||||||
// For backwards compatibility, in M2 the opacity of the unselected icons needs
|
// 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
|
// to be set to the default if it isn't in the given theme. This can be removed
|
||||||
@ -443,6 +446,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
|
|||||||
padding: widget.destinations[i].padding,
|
padding: widget.destinations[i].padding,
|
||||||
useIndicator: useIndicator,
|
useIndicator: useIndicator,
|
||||||
indicatorColor: useIndicator ? indicatorColor : null,
|
indicatorColor: useIndicator ? indicatorColor : null,
|
||||||
|
indicatorShape: useIndicator ? indicatorShape : null,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (widget.onDestinationSelected != null) {
|
if (widget.onDestinationSelected != null) {
|
||||||
widget.onDestinationSelected!(i);
|
widget.onDestinationSelected!(i);
|
||||||
@ -529,6 +533,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
this.padding,
|
this.padding,
|
||||||
required this.useIndicator,
|
required this.useIndicator,
|
||||||
this.indicatorColor,
|
this.indicatorColor,
|
||||||
|
this.indicatorShape,
|
||||||
}) : assert(minWidth != null),
|
}) : assert(minWidth != null),
|
||||||
assert(minExtendedWidth != null),
|
assert(minExtendedWidth != null),
|
||||||
assert(icon != null),
|
assert(icon != null),
|
||||||
@ -562,6 +567,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry? padding;
|
final EdgeInsetsGeometry? padding;
|
||||||
final bool useIndicator;
|
final bool useIndicator;
|
||||||
final Color? indicatorColor;
|
final Color? indicatorColor;
|
||||||
|
final ShapeBorder? indicatorShape;
|
||||||
|
|
||||||
final Animation<double> _positionAnimation;
|
final Animation<double> _positionAnimation;
|
||||||
|
|
||||||
@ -573,6 +579,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final bool material3 = Theme.of(context).useMaterial3;
|
final bool material3 = Theme.of(context).useMaterial3;
|
||||||
|
final double indicatorInkOffsetY;
|
||||||
|
|
||||||
final Widget themedIcon = IconTheme(
|
final Widget themedIcon = IconTheme(
|
||||||
data: iconTheme,
|
data: iconTheme,
|
||||||
@ -583,12 +590,13 @@ class _RailDestination extends StatelessWidget {
|
|||||||
child: label,
|
child: label,
|
||||||
);
|
);
|
||||||
|
|
||||||
final Widget content;
|
Widget content;
|
||||||
|
|
||||||
switch (labelType) {
|
switch (labelType) {
|
||||||
case NavigationRailLabelType.none:
|
case NavigationRailLabelType.none:
|
||||||
// Split the destination spacing across the top and bottom to keep the icon centered.
|
// 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? spacing = material3 ? const SizedBox(height: _verticalDestinationSpacingM3 / 2) : null;
|
||||||
|
indicatorInkOffsetY = _verticalDestinationPaddingNoLabel - (_verticalIconLabelSpacingM3 / 2);
|
||||||
|
|
||||||
final Widget iconPart = Column(
|
final Widget iconPart = Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -600,6 +608,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
child: _AddIndicator(
|
child: _AddIndicator(
|
||||||
addIndicator: useIndicator,
|
addIndicator: useIndicator,
|
||||||
indicatorColor: indicatorColor,
|
indicatorColor: indicatorColor,
|
||||||
|
indicatorShape: indicatorShape,
|
||||||
isCircular: !material3,
|
isCircular: !material3,
|
||||||
indicatorAnimation: destinationAnimation,
|
indicatorAnimation: destinationAnimation,
|
||||||
child: themedIcon,
|
child: themedIcon,
|
||||||
@ -666,6 +675,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
final Widget topSpacing = SizedBox(height: material3 ? 0 : verticalPadding);
|
final Widget topSpacing = SizedBox(height: material3 ? 0 : verticalPadding);
|
||||||
final Widget labelSpacing = SizedBox(height: material3 ? lerpDouble(0, _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0);
|
final Widget labelSpacing = SizedBox(height: material3 ? lerpDouble(0, _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0);
|
||||||
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
|
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
|
||||||
|
indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
|
||||||
|
|
||||||
content = Container(
|
content = Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
@ -682,6 +692,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
_AddIndicator(
|
_AddIndicator(
|
||||||
addIndicator: useIndicator,
|
addIndicator: useIndicator,
|
||||||
indicatorColor: indicatorColor,
|
indicatorColor: indicatorColor,
|
||||||
|
indicatorShape: indicatorShape,
|
||||||
isCircular: false,
|
isCircular: false,
|
||||||
indicatorAnimation: destinationAnimation,
|
indicatorAnimation: destinationAnimation,
|
||||||
child: themedIcon,
|
child: themedIcon,
|
||||||
@ -708,6 +719,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
final Widget topSpacing = SizedBox(height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
|
final Widget topSpacing = SizedBox(height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
|
||||||
final Widget labelSpacing = SizedBox(height: material3 ? _verticalIconLabelSpacingM3 : 0);
|
final Widget labelSpacing = SizedBox(height: material3 ? _verticalIconLabelSpacingM3 : 0);
|
||||||
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
|
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
|
||||||
|
indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
|
||||||
content = Container(
|
content = Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minWidth: minWidth,
|
minWidth: minWidth,
|
||||||
@ -720,6 +732,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
_AddIndicator(
|
_AddIndicator(
|
||||||
addIndicator: useIndicator,
|
addIndicator: useIndicator,
|
||||||
indicatorColor: indicatorColor,
|
indicatorColor: indicatorColor,
|
||||||
|
indicatorShape: indicatorShape,
|
||||||
isCircular: false,
|
isCircular: false,
|
||||||
indicatorAnimation: destinationAnimation,
|
indicatorAnimation: destinationAnimation,
|
||||||
child: themedIcon,
|
child: themedIcon,
|
||||||
@ -741,14 +754,14 @@ class _RailDestination extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Material(
|
Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: InkResponse(
|
child: _IndicatorInkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onHover: (_) {},
|
borderRadius: BorderRadius.all(Radius.circular(minWidth / 2.0)),
|
||||||
highlightShape: BoxShape.rectangle,
|
customBorder: indicatorShape,
|
||||||
borderRadius: material3 ? null : BorderRadius.all(Radius.circular(minWidth / 2.0)),
|
|
||||||
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),
|
||||||
|
useMaterial3: material3,
|
||||||
|
indicatorOffsetY: indicatorInkOffsetY,
|
||||||
child: content,
|
child: content,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -761,6 +774,43 @@ class _RailDestination extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _IndicatorInkWell extends InkResponse {
|
||||||
|
const _IndicatorInkWell({
|
||||||
|
super.child,
|
||||||
|
super.onTap,
|
||||||
|
ShapeBorder? customBorder,
|
||||||
|
BorderRadius? borderRadius,
|
||||||
|
super.splashColor,
|
||||||
|
super.hoverColor,
|
||||||
|
required this.useMaterial3,
|
||||||
|
required this.indicatorOffsetY,
|
||||||
|
}) : super(
|
||||||
|
containedInkWell: true,
|
||||||
|
highlightShape: BoxShape.rectangle,
|
||||||
|
borderRadius: useMaterial3 ? null : borderRadius,
|
||||||
|
customBorder: useMaterial3 ? customBorder : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
final bool useMaterial3;
|
||||||
|
final double indicatorOffsetY;
|
||||||
|
|
||||||
|
@override
|
||||||
|
RectCallback? getRectCallback(RenderBox referenceBox) {
|
||||||
|
final double indicatorOffsetX = referenceBox.size.width / 2;
|
||||||
|
|
||||||
|
if (useMaterial3) {
|
||||||
|
return () {
|
||||||
|
return Rect.fromCenter(
|
||||||
|
center: Offset(indicatorOffsetX, indicatorOffsetY),
|
||||||
|
width: _kCircularIndicatorDiameter,
|
||||||
|
height: 32,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// When [addIndicator] is `true`, puts [child] center aligned in a [Stack] with
|
/// When [addIndicator] is `true`, puts [child] center aligned in a [Stack] with
|
||||||
/// a [NavigationIndicator] behind it, otherwise returns [child].
|
/// a [NavigationIndicator] behind it, otherwise returns [child].
|
||||||
///
|
///
|
||||||
@ -771,6 +821,7 @@ class _AddIndicator extends StatelessWidget {
|
|||||||
required this.addIndicator,
|
required this.addIndicator,
|
||||||
required this.isCircular,
|
required this.isCircular,
|
||||||
required this.indicatorColor,
|
required this.indicatorColor,
|
||||||
|
required this.indicatorShape,
|
||||||
required this.indicatorAnimation,
|
required this.indicatorAnimation,
|
||||||
required this.child,
|
required this.child,
|
||||||
});
|
});
|
||||||
@ -778,6 +829,7 @@ class _AddIndicator extends StatelessWidget {
|
|||||||
final bool addIndicator;
|
final bool addIndicator;
|
||||||
final bool isCircular;
|
final bool isCircular;
|
||||||
final Color? indicatorColor;
|
final Color? indicatorColor;
|
||||||
|
final ShapeBorder? indicatorShape;
|
||||||
final Animation<double> indicatorAnimation;
|
final Animation<double> indicatorAnimation;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@ -788,19 +840,18 @@ class _AddIndicator extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
late final Widget indicator;
|
late final Widget indicator;
|
||||||
if (isCircular) {
|
if (isCircular) {
|
||||||
const double circularIndicatorDiameter = 56;
|
|
||||||
indicator = NavigationIndicator(
|
indicator = NavigationIndicator(
|
||||||
animation: indicatorAnimation,
|
animation: indicatorAnimation,
|
||||||
height: circularIndicatorDiameter,
|
height: _kCircularIndicatorDiameter,
|
||||||
width: circularIndicatorDiameter,
|
width: _kCircularIndicatorDiameter,
|
||||||
borderRadius: BorderRadius.circular(circularIndicatorDiameter / 2),
|
borderRadius: BorderRadius.circular(_kCircularIndicatorDiameter / 2),
|
||||||
color: indicatorColor,
|
color: indicatorColor,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
indicator = NavigationIndicator(
|
indicator = NavigationIndicator(
|
||||||
animation: indicatorAnimation,
|
animation: indicatorAnimation,
|
||||||
width: 56,
|
width: _kCircularIndicatorDiameter,
|
||||||
shape: const StadiumBorder(),
|
shape: indicatorShape,
|
||||||
color: indicatorColor,
|
color: indicatorColor,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -918,16 +969,16 @@ const double _verticalDestinationSpacingM3 = 12.0;
|
|||||||
// Hand coded defaults based on Material Design 2.
|
// Hand coded defaults based on Material Design 2.
|
||||||
class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
|
class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
|
||||||
_NavigationRailDefaultsM2(BuildContext context)
|
_NavigationRailDefaultsM2(BuildContext context)
|
||||||
: _theme = Theme.of(context),
|
: _theme = Theme.of(context),
|
||||||
_colors = Theme.of(context).colorScheme,
|
_colors = Theme.of(context).colorScheme,
|
||||||
super(
|
super(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
groupAlignment: -1,
|
groupAlignment: -1,
|
||||||
labelType: NavigationRailLabelType.none,
|
labelType: NavigationRailLabelType.none,
|
||||||
useIndicator: false,
|
useIndicator: false,
|
||||||
minWidth: 72.0,
|
minWidth: 72.0,
|
||||||
minExtendedWidth: 256,
|
minExtendedWidth: 256,
|
||||||
);
|
);
|
||||||
|
|
||||||
final ThemeData _theme;
|
final ThemeData _theme;
|
||||||
final ColorScheme _colors;
|
final ColorScheme _colors;
|
||||||
@ -970,14 +1021,14 @@ class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
|
|||||||
|
|
||||||
class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
|
class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
|
||||||
_NavigationRailDefaultsM3(this.context)
|
_NavigationRailDefaultsM3(this.context)
|
||||||
: super(
|
: super(
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
groupAlignment: -1,
|
groupAlignment: -1,
|
||||||
labelType: NavigationRailLabelType.none,
|
labelType: NavigationRailLabelType.none,
|
||||||
useIndicator: true,
|
useIndicator: true,
|
||||||
minWidth: 80.0,
|
minWidth: 80.0,
|
||||||
minExtendedWidth: 256,
|
minExtendedWidth: 256,
|
||||||
);
|
);
|
||||||
|
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
late final ColorScheme _colors = Theme.of(context).colorScheme;
|
||||||
@ -1009,6 +1060,7 @@ class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
|
|||||||
|
|
||||||
@override Color? get indicatorColor => _colors.secondaryContainer;
|
@override Color? get indicatorColor => _colors.secondaryContainer;
|
||||||
|
|
||||||
|
@override ShapeBorder? get indicatorShape => const StadiumBorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
// END GENERATED TOKEN PROPERTIES - NavigationRail
|
// END GENERATED TOKEN PROPERTIES - NavigationRail
|
||||||
|
@ -49,6 +49,7 @@ class NavigationRailThemeData with Diagnosticable {
|
|||||||
this.labelType,
|
this.labelType,
|
||||||
this.useIndicator,
|
this.useIndicator,
|
||||||
this.indicatorColor,
|
this.indicatorColor,
|
||||||
|
this.indicatorShape,
|
||||||
this.minWidth,
|
this.minWidth,
|
||||||
this.minExtendedWidth,
|
this.minExtendedWidth,
|
||||||
});
|
});
|
||||||
@ -91,6 +92,9 @@ class NavigationRailThemeData with Diagnosticable {
|
|||||||
/// when [useIndicator] is true.
|
/// when [useIndicator] is true.
|
||||||
final Color? indicatorColor;
|
final Color? indicatorColor;
|
||||||
|
|
||||||
|
/// Overrides the default shape of the [NavigationRail]'s selection indicator.
|
||||||
|
final ShapeBorder? indicatorShape;
|
||||||
|
|
||||||
/// Overrides the default value of [NavigationRail]'s minimum width when it
|
/// Overrides the default value of [NavigationRail]'s minimum width when it
|
||||||
/// is not extended.
|
/// is not extended.
|
||||||
final double? minWidth;
|
final double? minWidth;
|
||||||
@ -112,6 +116,7 @@ class NavigationRailThemeData with Diagnosticable {
|
|||||||
NavigationRailLabelType? labelType,
|
NavigationRailLabelType? labelType,
|
||||||
bool? useIndicator,
|
bool? useIndicator,
|
||||||
Color? indicatorColor,
|
Color? indicatorColor,
|
||||||
|
ShapeBorder? indicatorShape,
|
||||||
double? minWidth,
|
double? minWidth,
|
||||||
double? minExtendedWidth,
|
double? minExtendedWidth,
|
||||||
}) {
|
}) {
|
||||||
@ -126,6 +131,7 @@ 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,
|
||||||
|
indicatorShape: indicatorShape ?? this.indicatorShape,
|
||||||
minWidth: minWidth ?? this.minWidth,
|
minWidth: minWidth ?? this.minWidth,
|
||||||
minExtendedWidth: minExtendedWidth ?? this.minExtendedWidth,
|
minExtendedWidth: minExtendedWidth ?? this.minExtendedWidth,
|
||||||
);
|
);
|
||||||
@ -152,6 +158,7 @@ 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),
|
||||||
|
indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t),
|
||||||
minWidth: lerpDouble(a?.minWidth, b?.minWidth, t),
|
minWidth: lerpDouble(a?.minWidth, b?.minWidth, t),
|
||||||
minExtendedWidth: lerpDouble(a?.minExtendedWidth, b?.minExtendedWidth, t),
|
minExtendedWidth: lerpDouble(a?.minExtendedWidth, b?.minExtendedWidth, t),
|
||||||
|
|
||||||
@ -170,6 +177,7 @@ class NavigationRailThemeData with Diagnosticable {
|
|||||||
labelType,
|
labelType,
|
||||||
useIndicator,
|
useIndicator,
|
||||||
indicatorColor,
|
indicatorColor,
|
||||||
|
indicatorShape,
|
||||||
minWidth,
|
minWidth,
|
||||||
minExtendedWidth,
|
minExtendedWidth,
|
||||||
);
|
);
|
||||||
@ -193,6 +201,7 @@ class NavigationRailThemeData with Diagnosticable {
|
|||||||
&& other.labelType == labelType
|
&& other.labelType == labelType
|
||||||
&& other.useIndicator == useIndicator
|
&& other.useIndicator == useIndicator
|
||||||
&& other.indicatorColor == indicatorColor
|
&& other.indicatorColor == indicatorColor
|
||||||
|
&& other.indicatorShape == indicatorShape
|
||||||
&& other.minWidth == minWidth
|
&& other.minWidth == minWidth
|
||||||
&& other.minExtendedWidth == minExtendedWidth;
|
&& other.minExtendedWidth == minExtendedWidth;
|
||||||
}
|
}
|
||||||
@ -212,6 +221,7 @@ 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(DiagnosticsProperty<ShapeBorder>('indicatorShape', indicatorShape, defaultValue: null));
|
||||||
properties.add(DoubleProperty('minWidth', minWidth, defaultValue: defaultData.minWidth));
|
properties.add(DoubleProperty('minWidth', minWidth, defaultValue: defaultData.minWidth));
|
||||||
properties.add(DoubleProperty('minExtendedWidth', minExtendedWidth, defaultValue: defaultData.minExtendedWidth));
|
properties.add(DoubleProperty('minExtendedWidth', minExtendedWidth, defaultValue: defaultData.minExtendedWidth));
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../rendering/mock_canvas.dart';
|
||||||
import '../widgets/semantics_tester.dart';
|
import '../widgets/semantics_tester.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -2314,14 +2317,24 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final Iterable<Widget> indicatorInkWells = tester.allWidgets.where((Widget object) => object.runtimeType.toString() == '_IndicatorInkWell');
|
||||||
final Padding firstItem = tester.widget<Padding>(
|
final Padding firstItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Abc'), matching: find.widgetWithText(Padding, 'Abc'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(0).runtimeType, 'Abc'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Abc'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
final Padding secondItem = tester.widget<Padding>(
|
final Padding secondItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Def'), matching: find.widgetWithText(Padding, 'Def'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(1).runtimeType, 'Def'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Def'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
final Padding thirdItem = tester.widget<Padding>(
|
final Padding thirdItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Ghi'), matching: find.widgetWithText(Padding, 'Ghi'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(2).runtimeType, 'Ghi'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Ghi'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(firstItem.padding, defaultPadding);
|
expect(firstItem.padding, defaultPadding);
|
||||||
@ -2361,14 +2374,24 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final Iterable<Widget> indicatorInkWells = tester.allWidgets.where((Widget object) => object.runtimeType.toString() == '_IndicatorInkWell');
|
||||||
final Padding firstItem = tester.widget<Padding>(
|
final Padding firstItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Abc'), matching: find.widgetWithText(Padding, 'Abc'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(0).runtimeType, 'Abc'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Abc'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
final Padding secondItem = tester.widget<Padding>(
|
final Padding secondItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Def'), matching: find.widgetWithText(Padding, 'Def'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(1).runtimeType, 'Def'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Def'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
final Padding thirdItem = tester.widget<Padding>(
|
final Padding thirdItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Ghi'), matching: find.widgetWithText(Padding, 'Ghi'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(2).runtimeType, 'Ghi'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Ghi'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(firstItem.padding, defaultPadding);
|
expect(firstItem.padding, defaultPadding);
|
||||||
@ -2408,14 +2431,24 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final Iterable<Widget> indicatorInkWells = tester.allWidgets.where((Widget object) => object.runtimeType.toString() == '_IndicatorInkWell');
|
||||||
final Padding firstItem = tester.widget<Padding>(
|
final Padding firstItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Abc'), matching: find.widgetWithText(Padding, 'Abc'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(0).runtimeType, 'Abc'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Abc'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
final Padding secondItem = tester.widget<Padding>(
|
final Padding secondItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Def'), matching: find.widgetWithText(Padding, 'Def'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(1).runtimeType, 'Def'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Def'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
final Padding thirdItem = tester.widget<Padding>(
|
final Padding thirdItem = tester.widget<Padding>(
|
||||||
find.descendant(of: find.widgetWithText(InkResponse, 'Ghi'), matching: find.widgetWithText(Padding, 'Ghi'))
|
find.descendant(
|
||||||
|
of: find.widgetWithText(indicatorInkWells.elementAt(2).runtimeType, 'Ghi'),
|
||||||
|
matching: find.widgetWithText(Padding, 'Ghi'),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(firstItem.padding, defaultPadding);
|
expect(firstItem.padding, defaultPadding);
|
||||||
@ -2708,6 +2741,111 @@ void main() {
|
|||||||
expect(updatedWidthRTL, defaultWidth + safeAreaPadding);
|
expect(updatedWidthRTL, defaultWidth + safeAreaPadding);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationRail indicator renders ripple', (WidgetTester tester) async {
|
||||||
|
await _pumpNavigationRail(
|
||||||
|
tester,
|
||||||
|
navigationRail: NavigationRail(
|
||||||
|
selectedIndex: 1,
|
||||||
|
destinations: const <NavigationRailDestination>[
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.favorite_border),
|
||||||
|
selectedIcon: Icon(Icons.favorite),
|
||||||
|
label: Text('Abc'),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.bookmark_border),
|
||||||
|
selectedIcon: Icon(Icons.bookmark),
|
||||||
|
label: Text('Def'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
labelType: NavigationRailLabelType.all,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||||
|
await gesture.addPointer();
|
||||||
|
await gesture.moveTo(tester.getCenter(find.byIcon(Icons.favorite_border)));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
||||||
|
const Rect indicatorRect = Rect.fromLTRB(12.0, 0.0, 68.0, 32.0);
|
||||||
|
const Rect includedRect = indicatorRect;
|
||||||
|
final Rect excludedRect = includedRect.inflate(10);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
inkFeatures,
|
||||||
|
paints
|
||||||
|
..clipPath(
|
||||||
|
pathMatcher: isPathThat(
|
||||||
|
includes: <Offset>[
|
||||||
|
includedRect.centerLeft,
|
||||||
|
includedRect.topCenter,
|
||||||
|
includedRect.centerRight,
|
||||||
|
includedRect.bottomCenter,
|
||||||
|
],
|
||||||
|
excludes: <Offset>[
|
||||||
|
excludedRect.centerLeft,
|
||||||
|
excludedRect.topCenter,
|
||||||
|
excludedRect.centerRight,
|
||||||
|
excludedRect.bottomCenter,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..rect(
|
||||||
|
rect: indicatorRect,
|
||||||
|
color: const Color(0x0a6750a4),
|
||||||
|
)
|
||||||
|
..rrect(
|
||||||
|
rrect: RRect.fromLTRBR(12.0, 72.0, 68.0, 104.0, const Radius.circular(16)),
|
||||||
|
color: const Color(0xffe8def8),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationRail indicator scale transform', (WidgetTester tester) async {
|
||||||
|
int selectedIndex = 0;
|
||||||
|
Future<void> buildWidget() async {
|
||||||
|
await _pumpNavigationRail(
|
||||||
|
tester,
|
||||||
|
navigationRail: NavigationRail(
|
||||||
|
selectedIndex: selectedIndex,
|
||||||
|
destinations: const <NavigationRailDestination>[
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.favorite_border),
|
||||||
|
selectedIcon: Icon(Icons.favorite),
|
||||||
|
label: Text('Abc'),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
icon: Icon(Icons.bookmark_border),
|
||||||
|
selectedIcon: Icon(Icons.bookmark),
|
||||||
|
label: Text('Def'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
labelType: NavigationRailLabelType.all,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await buildWidget();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
final Finder transformFinder = find.descendant(
|
||||||
|
of: find.byType(NavigationIndicator),
|
||||||
|
matching: find.byType(Transform),
|
||||||
|
).last;
|
||||||
|
Matrix4 transform = tester.widget<Transform>(transformFinder).transform;
|
||||||
|
expect(transform.getColumn(0)[0], 0.0);
|
||||||
|
|
||||||
|
selectedIndex = 1;
|
||||||
|
await buildWidget();
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
transform = tester.widget<Transform>(transformFinder).transform;
|
||||||
|
expect(transform.getColumn(0)[0], closeTo(0.9705023956298828, precisionErrorTolerance));
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 100));
|
||||||
|
transform = tester.widget<Transform>(transformFinder).transform;
|
||||||
|
expect(transform.getColumn(0)[0], 1.0);
|
||||||
|
});
|
||||||
|
|
||||||
group('Material 2', () {
|
group('Material 2', () {
|
||||||
// Original Material 2 tests. Remove this group after `useMaterial3` has been deprecated.
|
// Original Material 2 tests. Remove this group after `useMaterial3` has been deprecated.
|
||||||
testWidgets('Renders at the correct default width - [labelType]=none (default)', (WidgetTester tester) async {
|
testWidgets('Renders at the correct default width - [labelType]=none (default)', (WidgetTester tester) async {
|
||||||
|
@ -40,6 +40,10 @@ void main() {
|
|||||||
expect(_destinationsAlign(tester).alignment, Alignment.topCenter);
|
expect(_destinationsAlign(tester).alignment, Alignment.topCenter);
|
||||||
expect(_labelType(tester), NavigationRailLabelType.none);
|
expect(_labelType(tester), NavigationRailLabelType.none);
|
||||||
expect(find.byType(NavigationIndicator), findsWidgets);
|
expect(find.byType(NavigationIndicator), findsWidgets);
|
||||||
|
expect(_indicatorDecoration(tester)?.color, ThemeData().colorScheme.secondaryContainer);
|
||||||
|
expect(_indicatorDecoration(tester)?.shape, const StadiumBorder());
|
||||||
|
final InkResponse inkResponse = tester.allWidgets.firstWhere((Widget object) => object.runtimeType.toString() == '_IndicatorInkWell') as InkResponse;
|
||||||
|
expect(inkResponse.customBorder, const StadiumBorder());
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Default values are used when no NavigationRail or NavigationRailThemeData properties are specified (Material 2)', (WidgetTester tester) async {
|
testWidgets('Default values are used when no NavigationRail or NavigationRailThemeData properties are specified (Material 2)', (WidgetTester tester) async {
|
||||||
@ -87,6 +91,7 @@ void main() {
|
|||||||
const NavigationRailLabelType labelType = NavigationRailLabelType.all;
|
const NavigationRailLabelType labelType = NavigationRailLabelType.all;
|
||||||
const bool useIndicator = true;
|
const bool useIndicator = true;
|
||||||
const Color indicatorColor = Color(0x00000004);
|
const Color indicatorColor = Color(0x00000004);
|
||||||
|
const ShapeBorder indicatorShape = RoundedRectangleBorder();
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@ -111,6 +116,7 @@ void main() {
|
|||||||
labelType: labelType,
|
labelType: labelType,
|
||||||
useIndicator: useIndicator,
|
useIndicator: useIndicator,
|
||||||
indicatorColor: indicatorColor,
|
indicatorColor: indicatorColor,
|
||||||
|
indicatorShape: indicatorShape,
|
||||||
),
|
),
|
||||||
child: NavigationRail(
|
child: NavigationRail(
|
||||||
selectedIndex: 0,
|
selectedIndex: 0,
|
||||||
@ -135,6 +141,7 @@ void main() {
|
|||||||
expect(_labelType(tester), labelType);
|
expect(_labelType(tester), labelType);
|
||||||
expect(find.byType(NavigationIndicator), findsWidgets);
|
expect(find.byType(NavigationIndicator), findsWidgets);
|
||||||
expect(_indicatorDecoration(tester)?.color, indicatorColor);
|
expect(_indicatorDecoration(tester)?.color, indicatorColor);
|
||||||
|
expect(_indicatorDecoration(tester)?.shape, indicatorShape);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('NavigationRail values take priority over NavigationRailThemeData values when both properties are specified', (WidgetTester tester) async {
|
testWidgets('NavigationRail values take priority over NavigationRailThemeData values when both properties are specified', (WidgetTester tester) async {
|
||||||
@ -245,6 +252,7 @@ void main() {
|
|||||||
labelType: NavigationRailLabelType.selected,
|
labelType: NavigationRailLabelType.selected,
|
||||||
useIndicator: true,
|
useIndicator: true,
|
||||||
indicatorColor: Color(0x00000096),
|
indicatorColor: Color(0x00000096),
|
||||||
|
indicatorShape: CircleBorder(),
|
||||||
).debugFillProperties(builder);
|
).debugFillProperties(builder);
|
||||||
|
|
||||||
final List<String> description = builder.properties
|
final List<String> description = builder.properties
|
||||||
@ -267,6 +275,7 @@ void main() {
|
|||||||
expect(description[7], 'labelType: NavigationRailLabelType.selected');
|
expect(description[7], 'labelType: NavigationRailLabelType.selected');
|
||||||
expect(description[8], 'useIndicator: true');
|
expect(description[8], 'useIndicator: true');
|
||||||
expect(description[9], 'indicatorColor: Color(0x00000096)');
|
expect(description[9], 'indicatorColor: Color(0x00000096)');
|
||||||
|
expect(description[10], 'indicatorShape: CircleBorder(BorderSide(width: 0.0, style: none))');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user