Fix NavigationRail
highlight (#117320)
This commit is contained in:
parent
cb988c7b6e
commit
5fcb48d598
@ -16,6 +16,7 @@ import 'text_theme.dart';
|
|||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
|
||||||
const double _kCircularIndicatorDiameter = 56;
|
const double _kCircularIndicatorDiameter = 56;
|
||||||
|
const double _kIndicatorHeight = 32;
|
||||||
|
|
||||||
/// 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
|
||||||
@ -590,7 +591,8 @@ class _RailDestination extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final bool material3 = Theme.of(context).useMaterial3;
|
final bool material3 = Theme.of(context).useMaterial3;
|
||||||
final double indicatorInkOffsetY;
|
final EdgeInsets destionationPadding = (padding ?? EdgeInsets.zero).resolve(Directionality.of(context));
|
||||||
|
Offset indicatorOffset;
|
||||||
|
|
||||||
final Widget themedIcon = IconTheme(
|
final Widget themedIcon = IconTheme(
|
||||||
data: iconTheme,
|
data: iconTheme,
|
||||||
@ -607,8 +609,10 @@ class _RailDestination extends StatelessWidget {
|
|||||||
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);
|
indicatorOffset = Offset(
|
||||||
|
minWidth / 2 + destionationPadding.left,
|
||||||
|
_verticalDestinationSpacingM3 / 2 + destionationPadding.top,
|
||||||
|
);
|
||||||
final Widget iconPart = Column(
|
final Widget iconPart = Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (spacing != null) spacing,
|
if (spacing != null) spacing,
|
||||||
@ -686,8 +690,12 @@ 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;
|
final double indicatorHorizontalPadding = (destionationPadding.left / 2) - (destionationPadding.right / 2);
|
||||||
|
final double indicatorVerticalPadding = destionationPadding.top;
|
||||||
|
indicatorOffset = Offset(minWidth / 2 + indicatorHorizontalPadding, indicatorVerticalPadding);
|
||||||
|
if (minWidth < _NavigationRailDefaultsM2(context).minWidth!) {
|
||||||
|
indicatorOffset = Offset(minWidth / 2 + _horizontalDestinationSpacingM3, indicatorVerticalPadding);
|
||||||
|
}
|
||||||
content = Container(
|
content = Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minWidth: minWidth,
|
minWidth: minWidth,
|
||||||
@ -730,7 +738,12 @@ 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;
|
final double indicatorHorizontalPadding = (destionationPadding.left / 2) - (destionationPadding.right / 2);
|
||||||
|
final double indicatorVerticalPadding = destionationPadding.top;
|
||||||
|
indicatorOffset = Offset(minWidth / 2 + indicatorHorizontalPadding, indicatorVerticalPadding);
|
||||||
|
if (minWidth < _NavigationRailDefaultsM2(context).minWidth!) {
|
||||||
|
indicatorOffset = Offset(minWidth / 2 + _horizontalDestinationSpacingM3, indicatorVerticalPadding);
|
||||||
|
}
|
||||||
content = Container(
|
content = Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minWidth: minWidth,
|
minWidth: minWidth,
|
||||||
@ -772,7 +785,7 @@ class _RailDestination extends StatelessWidget {
|
|||||||
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,
|
useMaterial3: material3,
|
||||||
indicatorOffsetY: indicatorInkOffsetY,
|
indicatorOffset: indicatorOffset,
|
||||||
child: content,
|
child: content,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -794,7 +807,7 @@ class _IndicatorInkWell extends InkResponse {
|
|||||||
super.splashColor,
|
super.splashColor,
|
||||||
super.hoverColor,
|
super.hoverColor,
|
||||||
required this.useMaterial3,
|
required this.useMaterial3,
|
||||||
required this.indicatorOffsetY,
|
required this.indicatorOffset,
|
||||||
}) : super(
|
}) : super(
|
||||||
containedInkWell: true,
|
containedInkWell: true,
|
||||||
highlightShape: BoxShape.rectangle,
|
highlightShape: BoxShape.rectangle,
|
||||||
@ -803,18 +816,17 @@ class _IndicatorInkWell extends InkResponse {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final bool useMaterial3;
|
final bool useMaterial3;
|
||||||
final double indicatorOffsetY;
|
final Offset indicatorOffset;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RectCallback? getRectCallback(RenderBox referenceBox) {
|
RectCallback? getRectCallback(RenderBox referenceBox) {
|
||||||
final double indicatorOffsetX = referenceBox.size.width / 2;
|
|
||||||
|
|
||||||
if (useMaterial3) {
|
if (useMaterial3) {
|
||||||
return () {
|
return () {
|
||||||
return Rect.fromCenter(
|
return Rect.fromLTWH(
|
||||||
center: Offset(indicatorOffsetX, indicatorOffsetY),
|
indicatorOffset.dx - (_kCircularIndicatorDiameter / 2),
|
||||||
width: _kCircularIndicatorDiameter,
|
indicatorOffset.dy,
|
||||||
height: 32,
|
_kCircularIndicatorDiameter,
|
||||||
|
_kIndicatorHeight,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -984,6 +996,7 @@ 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 _verticalIconLabelSpacingM3 = 4.0;
|
||||||
const double _verticalDestinationSpacingM3 = 12.0;
|
const double _verticalDestinationSpacingM3 = 12.0;
|
||||||
|
const double _horizontalDestinationSpacingM3 = 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 {
|
||||||
|
@ -2802,6 +2802,264 @@ void main() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationRail indicator renders ripple - extended', (WidgetTester tester) async {
|
||||||
|
// This is a regression test for https://github.com/flutter/flutter/issues/117126
|
||||||
|
await _pumpNavigationRail(
|
||||||
|
tester,
|
||||||
|
navigationRail: NavigationRail(
|
||||||
|
selectedIndex: 1,
|
||||||
|
extended: true,
|
||||||
|
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.none,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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, 6.0, 68.0, 38.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, 58.0, 68.0, 90.0, const Radius.circular(16)),
|
||||||
|
color: const Color(0xffe8def8),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationRail indicator renders properly when padding is applied', (WidgetTester tester) async {
|
||||||
|
// This is a regression test for https://github.com/flutter/flutter/issues/117126
|
||||||
|
await _pumpNavigationRail(
|
||||||
|
tester,
|
||||||
|
navigationRail: NavigationRail(
|
||||||
|
selectedIndex: 1,
|
||||||
|
extended: true,
|
||||||
|
destinations: const <NavigationRailDestination>[
|
||||||
|
NavigationRailDestination(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
icon: Icon(Icons.favorite_border),
|
||||||
|
selectedIcon: Icon(Icons.favorite),
|
||||||
|
label: Text('Abc'),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
padding: EdgeInsets.all(18),
|
||||||
|
icon: Icon(Icons.bookmark_border),
|
||||||
|
selectedIcon: Icon(Icons.bookmark),
|
||||||
|
label: Text('Def'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
labelType: NavigationRailLabelType.none,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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(22.0, 16.0, 78.0, 48.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(30.0, 96.0, 86.0, 128.0, const Radius.circular(16)),
|
||||||
|
color: const Color(0xffe8def8),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Indicator renders properly when NavigationRai.minWidth < default minWidth', (WidgetTester tester) async {
|
||||||
|
// This is a regression test for https://github.com/flutter/flutter/issues/117126
|
||||||
|
await _pumpNavigationRail(
|
||||||
|
tester,
|
||||||
|
navigationRail: NavigationRail(
|
||||||
|
minWidth: 50,
|
||||||
|
selectedIndex: 1,
|
||||||
|
extended: true,
|
||||||
|
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.none,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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(-3.0, 6.0, 53.0, 38.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(0.0, 58.0, 50.0, 90.0, const Radius.circular(16)),
|
||||||
|
color: const Color(0xffe8def8),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('NavigationRail indicator renders properly with custom padding and minWidth', (WidgetTester tester) async {
|
||||||
|
// This is a regression test for https://github.com/flutter/flutter/issues/117126
|
||||||
|
await _pumpNavigationRail(
|
||||||
|
tester,
|
||||||
|
navigationRail: NavigationRail(
|
||||||
|
minWidth: 300,
|
||||||
|
selectedIndex: 1,
|
||||||
|
extended: true,
|
||||||
|
destinations: const <NavigationRailDestination>[
|
||||||
|
NavigationRailDestination(
|
||||||
|
padding: EdgeInsets.all(10),
|
||||||
|
icon: Icon(Icons.favorite_border),
|
||||||
|
selectedIcon: Icon(Icons.favorite),
|
||||||
|
label: Text('Abc'),
|
||||||
|
),
|
||||||
|
NavigationRailDestination(
|
||||||
|
padding: EdgeInsets.all(18),
|
||||||
|
icon: Icon(Icons.bookmark_border),
|
||||||
|
selectedIcon: Icon(Icons.bookmark),
|
||||||
|
label: Text('Def'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
labelType: NavigationRailLabelType.none,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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(132.0, 16.0, 188.0, 48.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(140.0, 96.0, 196.0, 128.0, const Radius.circular(16)),
|
||||||
|
color: const Color(0xffe8def8),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('NavigationRail indicator scale transform', (WidgetTester tester) async {
|
testWidgets('NavigationRail indicator scale transform', (WidgetTester tester) async {
|
||||||
int selectedIndex = 0;
|
int selectedIndex = 0;
|
||||||
Future<void> buildWidget() async {
|
Future<void> buildWidget() async {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user