mirror of
https://github.com/flutter/flutter
synced 2024-08-27 12:00:58 +00:00
Fix NavigationBar
ripple (#115499)
* Fix `NavigationBar` ripple * remove unused import
This commit is contained in:
parent
3a3a0db13e
commit
ca78327402
|
@ -17,6 +17,9 @@ import 'text_theme.dart';
|
|||
import 'theme.dart';
|
||||
import 'tooltip.dart';
|
||||
|
||||
const double _kIndicatorHeight = 64;
|
||||
const double _kIndicatorWidth = 32;
|
||||
|
||||
// Examples can assume:
|
||||
// late BuildContext context;
|
||||
// late bool _isDrawerOpen;
|
||||
|
@ -429,11 +432,14 @@ class _NavigationDestinationBuilder extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _NavigationDestinationInfo info = _NavigationDestinationInfo.of(context);
|
||||
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
|
||||
final NavigationBarThemeData defaults = _defaultsFor(context);
|
||||
|
||||
return _NavigationBarDestinationSemantics(
|
||||
child: _NavigationBarDestinationTooltip(
|
||||
message: tooltip ?? label,
|
||||
child: InkWell(
|
||||
highlightColor: Colors.transparent,
|
||||
child: _IndicatorInkWell(
|
||||
customBorder: navigationBarTheme.indicatorShape ?? defaults.indicatorShape,
|
||||
onTap: info.onTap,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
|
@ -451,6 +457,31 @@ class _NavigationDestinationBuilder extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _IndicatorInkWell extends InkResponse {
|
||||
const _IndicatorInkWell({
|
||||
super.child,
|
||||
super.onTap,
|
||||
super.customBorder,
|
||||
}) : super(
|
||||
containedInkWell: true,
|
||||
highlightColor: Colors.transparent,
|
||||
);
|
||||
|
||||
@override
|
||||
RectCallback? getRectCallback(RenderBox referenceBox) {
|
||||
final double indicatorOffsetX = referenceBox.size.width / 2;
|
||||
const double indicatorOffsetY = 30.0;
|
||||
|
||||
return () {
|
||||
return Rect.fromCenter(
|
||||
center: Offset(indicatorOffsetX, indicatorOffsetY),
|
||||
width: _kIndicatorHeight,
|
||||
height: _kIndicatorWidth,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Inherited widget for passing data from the [NavigationBar] to the
|
||||
/// [NavigationBar.destinations] children widgets.
|
||||
///
|
||||
|
@ -562,8 +593,8 @@ class NavigationIndicator extends StatelessWidget {
|
|||
super.key,
|
||||
required this.animation,
|
||||
this.color,
|
||||
this.width = 64,
|
||||
this.height = 32,
|
||||
this.width = _kIndicatorHeight,
|
||||
this.height = _kIndicatorWidth,
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(16)),
|
||||
this.shape,
|
||||
});
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Navigation bar updates destinations when tapped', (WidgetTester tester) async {
|
||||
int mutatedIndex = -1;
|
||||
|
@ -553,6 +556,122 @@ void main() {
|
|||
|
||||
expect(newHeight, equals(initialHeight));
|
||||
});
|
||||
|
||||
testWidgets('Navigation indicator renders ripple', (WidgetTester tester) async {
|
||||
final Widget widget = _buildWidget(
|
||||
NavigationBar(
|
||||
destinations: const <Widget>[
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.ac_unit),
|
||||
label: 'AC',
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.access_alarm),
|
||||
label: 'Alarm',
|
||||
),
|
||||
],
|
||||
onDestinationSelected: (int i) {
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(widget);
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
await gesture.addPointer();
|
||||
await gesture.moveTo(tester.getCenter(find.byIcon(Icons.access_alarm)));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
|
||||
const Offset indicatorCenter = Offset(600, 30);
|
||||
const Size includedIndicatorSize = Size(64, 32);
|
||||
const Size excludedIndicatorSize = Size(74, 40);
|
||||
expect(
|
||||
inkFeatures,
|
||||
paints
|
||||
..clipPath(
|
||||
pathMatcher: isPathThat(
|
||||
includes: <Offset>[
|
||||
// Left center.
|
||||
Offset(indicatorCenter.dx - (includedIndicatorSize.width / 2), indicatorCenter.dy),
|
||||
// Top center.
|
||||
Offset(indicatorCenter.dx, indicatorCenter.dy - (includedIndicatorSize.height / 2)),
|
||||
// Right center.
|
||||
Offset(indicatorCenter.dx + (includedIndicatorSize.width / 2), indicatorCenter.dy),
|
||||
// Bottom center.
|
||||
Offset(indicatorCenter.dx, indicatorCenter.dy + (includedIndicatorSize.height / 2)),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
// Left center.
|
||||
Offset(indicatorCenter.dx - (excludedIndicatorSize.width / 2), indicatorCenter.dy),
|
||||
// Top center.
|
||||
Offset(indicatorCenter.dx, indicatorCenter.dy - (excludedIndicatorSize.height / 2)),
|
||||
// Right center.
|
||||
Offset(indicatorCenter.dx + (excludedIndicatorSize.width / 2), indicatorCenter.dy),
|
||||
// Bottom center.
|
||||
Offset(indicatorCenter.dx, indicatorCenter.dy + (excludedIndicatorSize.height / 2)),
|
||||
],
|
||||
),
|
||||
)
|
||||
..circle(
|
||||
x: indicatorCenter.dx,
|
||||
y: indicatorCenter.dy,
|
||||
radius: 35.0,
|
||||
color: const Color(0x0a000000),
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Navigation indicator scale transform', (WidgetTester tester) async {
|
||||
int selectedIndex = 0;
|
||||
|
||||
Widget buildNavigationBar() {
|
||||
return MaterialApp(
|
||||
theme: ThemeData.light(),
|
||||
home: Scaffold(
|
||||
bottomNavigationBar: Center(
|
||||
child: NavigationBar(
|
||||
selectedIndex: selectedIndex,
|
||||
destinations: const <Widget>[
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.ac_unit),
|
||||
label: 'AC',
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.access_alarm),
|
||||
label: 'Alarm',
|
||||
),
|
||||
],
|
||||
onDestinationSelected: (int i) { },
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildNavigationBar());
|
||||
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 tester.pumpWidget(buildNavigationBar());
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
transform = tester.widget<Transform>(transformFinder).transform;
|
||||
expect(transform.getColumn(0)[0], closeTo(0.7805849514007568, precisionErrorTolerance));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
transform = tester.widget<Transform>(transformFinder).transform;
|
||||
expect(transform.getColumn(0)[0], closeTo(0.9473570239543915, precisionErrorTolerance));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
transform = tester.widget<Transform>(transformFinder).transform;
|
||||
expect(transform.getColumn(0)[0], 1.0);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildWidget(Widget child) {
|
||||
|
|
Loading…
Reference in a new issue