Unverified Commit beaabb70 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add `IndicatorShape` to `NavigationRailTheme` and fix indicator ripple. (#116108)

* Add `IndicatorShape` to `NavigationRailTheme` and fix  indicator ripple.

* remove unused variables
parent 215f6372
...@@ -53,6 +53,7 @@ class _${blockName}DefaultsM3 extends NavigationRailThemeData { ...@@ -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'; ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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,
); );
} }
...@@ -1009,6 +1060,7 @@ class _NavigationRailDefaultsM3 extends NavigationRailThemeData { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 @@ ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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() { ...@@ -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))');
}); });
} }
......
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