Unverified Commit 74645b43 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Fix `NavigationBar` indicator ripple doesn't account for label height (#117473)

parent 589f2eb9
...@@ -462,26 +462,17 @@ class _NavigationDestinationBuilder extends StatelessWidget { ...@@ -462,26 +462,17 @@ class _NavigationDestinationBuilder extends StatelessWidget {
final _NavigationDestinationInfo info = _NavigationDestinationInfo.of(context); final _NavigationDestinationInfo info = _NavigationDestinationInfo.of(context);
final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context); final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
final NavigationBarThemeData defaults = _defaultsFor(context); final NavigationBarThemeData defaults = _defaultsFor(context);
final GlobalKey labelKey = GlobalKey();
final bool selected = info.selectedIndex == info.index; final bool selected = info.selectedIndex == info.index;
final double labelPadding;
switch (info.labelBehavior) {
case NavigationDestinationLabelBehavior.alwaysShow:
labelPadding = 8;
break;
case NavigationDestinationLabelBehavior.onlyShowSelected:
labelPadding = selected ? 8 : 0;
break;
case NavigationDestinationLabelBehavior.alwaysHide:
labelPadding = 0;
break;
}
return _NavigationBarDestinationSemantics( return _NavigationBarDestinationSemantics(
child: _NavigationBarDestinationTooltip( child: _NavigationBarDestinationTooltip(
message: tooltip ?? label, message: tooltip ?? label,
child: _IndicatorInkWell( child: _IndicatorInkWell(
key: UniqueKey(), key: UniqueKey(),
labelPadding: labelPadding, labelKey: labelKey,
labelBehavior: info.labelBehavior,
selected: selected,
customBorder: navigationBarTheme.indicatorShape ?? defaults.indicatorShape, customBorder: navigationBarTheme.indicatorShape ?? defaults.indicatorShape,
onTap: info.onTap, onTap: info.onTap,
child: Row( child: Row(
...@@ -489,6 +480,7 @@ class _NavigationDestinationBuilder extends StatelessWidget { ...@@ -489,6 +480,7 @@ class _NavigationDestinationBuilder extends StatelessWidget {
Expanded( Expanded(
child: _NavigationBarDestinationLayout( child: _NavigationBarDestinationLayout(
icon: buildIcon(context), icon: buildIcon(context),
labelKey: labelKey,
label: buildLabel(context), label: buildLabel(context),
), ),
), ),
...@@ -503,7 +495,9 @@ class _NavigationDestinationBuilder extends StatelessWidget { ...@@ -503,7 +495,9 @@ class _NavigationDestinationBuilder extends StatelessWidget {
class _IndicatorInkWell extends InkResponse { class _IndicatorInkWell extends InkResponse {
const _IndicatorInkWell({ const _IndicatorInkWell({
super.key, super.key,
required this.labelPadding, required this.labelKey,
required this.labelBehavior,
required this.selected,
super.customBorder, super.customBorder,
super.onTap, super.onTap,
super.child, super.child,
...@@ -512,10 +506,26 @@ class _IndicatorInkWell extends InkResponse { ...@@ -512,10 +506,26 @@ class _IndicatorInkWell extends InkResponse {
highlightColor: Colors.transparent, highlightColor: Colors.transparent,
); );
final double labelPadding; final GlobalKey labelKey;
final NavigationDestinationLabelBehavior labelBehavior;
final bool selected;
@override @override
RectCallback? getRectCallback(RenderBox referenceBox) { RectCallback? getRectCallback(RenderBox referenceBox) {
final RenderBox labelBox = labelKey.currentContext!.findRenderObject()! as RenderBox;
final Rect labelRect = labelBox.localToGlobal(Offset.zero) & labelBox.size;
final double labelPadding;
switch (labelBehavior) {
case NavigationDestinationLabelBehavior.alwaysShow:
labelPadding = labelRect.height / 2;
break;
case NavigationDestinationLabelBehavior.onlyShowSelected:
labelPadding = selected ? labelRect.height / 2 : 0;
break;
case NavigationDestinationLabelBehavior.alwaysHide:
labelPadding = 0;
break;
}
final double indicatorOffsetX = referenceBox.size.width / 2; final double indicatorOffsetX = referenceBox.size.width / 2;
final double indicatorOffsetY = referenceBox.size.height / 2 - labelPadding; final double indicatorOffsetY = referenceBox.size.height / 2 - labelPadding;
...@@ -765,6 +775,7 @@ class _NavigationBarDestinationLayout extends StatelessWidget { ...@@ -765,6 +775,7 @@ class _NavigationBarDestinationLayout extends StatelessWidget {
/// 3 [NavigationBar]. /// 3 [NavigationBar].
const _NavigationBarDestinationLayout({ const _NavigationBarDestinationLayout({
required this.icon, required this.icon,
required this.labelKey,
required this.label, required this.label,
}); });
...@@ -773,6 +784,11 @@ class _NavigationBarDestinationLayout extends StatelessWidget { ...@@ -773,6 +784,11 @@ class _NavigationBarDestinationLayout extends StatelessWidget {
/// See [NavigationDestination.icon]. /// See [NavigationDestination.icon].
final Widget icon; final Widget icon;
/// The global key for the label of this destination.
///
/// This is used to determine the position of the label relative to the icon.
final GlobalKey labelKey;
/// The label widget that sits below the icon. /// The label widget that sits below the icon.
/// ///
/// This widget will sometimes be faded out, depending on /// This widget will sometimes be faded out, depending on
...@@ -782,7 +798,6 @@ class _NavigationBarDestinationLayout extends StatelessWidget { ...@@ -782,7 +798,6 @@ class _NavigationBarDestinationLayout extends StatelessWidget {
final Widget label; final Widget label;
static final Key _iconKey = UniqueKey(); static final Key _iconKey = UniqueKey();
static final Key _labelKey = UniqueKey();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -806,7 +821,7 @@ class _NavigationBarDestinationLayout extends StatelessWidget { ...@@ -806,7 +821,7 @@ class _NavigationBarDestinationLayout extends StatelessWidget {
alwaysIncludeSemantics: true, alwaysIncludeSemantics: true,
opacity: animation, opacity: animation,
child: RepaintBoundary( child: RepaintBoundary(
key: _labelKey, key: labelKey,
child: label, child: label,
), ),
), ),
......
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
// 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.
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
library;
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';
...@@ -161,6 +167,48 @@ void main() { ...@@ -161,6 +167,48 @@ void main() {
expect(_barMaterial(tester).elevation, elevation); expect(_barMaterial(tester).elevation, elevation);
expect(_labelBehavior(tester), labelBehavior); expect(_labelBehavior(tester), labelBehavior);
}); });
testWidgets('Custom label style renders ink ripple properly', (WidgetTester tester) async {
Widget buildWidget({ NavigationDestinationLabelBehavior? labelBehavior }) {
return MaterialApp(
theme: ThemeData(
navigationBarTheme: const NavigationBarThemeData(
labelTextStyle: MaterialStatePropertyAll<TextStyle>(
TextStyle(fontSize: 25, color: Color(0xff0000ff)),
),
),
useMaterial3: true,
),
home: Scaffold(
bottomNavigationBar: Center(
child: NavigationBar(
labelBehavior: labelBehavior,
destinations: const <Widget>[
NavigationDestination(
icon: SizedBox(),
label: 'AC',
),
NavigationDestination(
icon: SizedBox(),
label: 'Alarm',
),
],
onDestinationSelected: (int i) { },
),
),
),
);
}
await tester.pumpWidget(buildWidget());
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byType(NavigationDestination).last));
await tester.pumpAndSettle();
await expectLater(find.byType(NavigationBar), matchesGoldenFile('indicator_custom_label_style.png'));
});
} }
List<NavigationDestination> _destinations() { List<NavigationDestination> _destinations() {
......
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