Skip to content

Commit

Permalink
Fix NavigationBar ripple (#115499)
Browse files Browse the repository at this point in the history
* Fix `NavigationBar` ripple

* remove unused import
  • Loading branch information
TahaTesser authored Nov 18, 2022
1 parent 3a3a0db commit ca78327
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 4 deletions.
39 changes: 35 additions & 4 deletions packages/flutter/lib/src/material/navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>[
Expand All @@ -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.
///
Expand Down Expand Up @@ -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,
});
Expand Down
119 changes: 119 additions & 0 deletions packages/flutter/test/material/navigation_bar_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit ca78327

Please sign in to comment.