Skip to content

Commit

Permalink
Adds Missing onHover & onFocusChange for OutlinedButton.icon (#…
Browse files Browse the repository at this point in the history
…144374)

Adds Missing `onHover` & `onFocusChange` for `OutlinedButton.icon`.

I've copy-pasted the tests of OutlinedButton for OutlinedButton.icon.

Also, `onHover` & `onFocusChange` are already there in ElevatedButton.icon, FilledButton.icon & TextButton.icon, but tests files for these 3 doesn't test for icon variants.

Fixes #144256
  • Loading branch information
piedcipher committed May 21, 2024
1 parent 1635e64 commit 059189e
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 0 deletions.
8 changes: 8 additions & 0 deletions packages/flutter/lib/src/material/outlined_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ class OutlinedButton extends ButtonStyleButton {
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
ValueChanged<bool>? onFocusChange,
ButtonStyle? style,
FocusNode? focusNode,
bool? autofocus,
Expand All @@ -109,6 +111,8 @@ class OutlinedButton extends ButtonStyleButton {
key: key,
onPressed: onPressed,
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: style,
focusNode: focusNode,
autofocus: autofocus ?? false,
Expand All @@ -121,6 +125,8 @@ class OutlinedButton extends ButtonStyleButton {
key: key,
onPressed: onPressed,
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: style,
focusNode: focusNode,
autofocus: autofocus ?? false,
Expand Down Expand Up @@ -454,6 +460,8 @@ class _OutlinedButtonWithIcon extends OutlinedButton {
super.key,
required super.onPressed,
super.onLongPress,
super.onHover,
super.onFocusChange,
super.style,
super.focusNode,
bool? autofocus,
Expand Down
167 changes: 167 additions & 0 deletions packages/flutter/test/material/outlined_button_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2467,6 +2467,173 @@ void main() {
// The icon is aligned to the left of the button.
expect(buttonTopLeft.dx, iconTopLeft.dx - 24.0); // 24.0 - padding between icon and button edge.
});

testWidgets("OutlinedButton.icon response doesn't hover when disabled", (WidgetTester tester) async {
FocusManager.instance.highlightStrategy = FocusHighlightStrategy.alwaysTouch;
final FocusNode focusNode = FocusNode(debugLabel: 'OutlinedButton.icon Focus');
final GlobalKey childKey = GlobalKey();
bool hovering = false;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: SizedBox(
width: 100,
height: 100,
child: OutlinedButton.icon(
autofocus: true,
onPressed: () {},
onLongPress: () {},
onHover: (bool value) { hovering = value; },
focusNode: focusNode,
label: SizedBox(key: childKey),
icon: const Icon(Icons.add),
),
),
),
);
await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isTrue);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tester.getCenter(find.byKey(childKey)));
await tester.pumpAndSettle();
expect(hovering, isTrue);

await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: SizedBox(
width: 100,
height: 100,
child: OutlinedButton.icon(
focusNode: focusNode,
onHover: (bool value) { hovering = value; },
onPressed: null,
label: SizedBox(key: childKey),
icon: const Icon(Icons.add),
),
),
),
);

await tester.pumpAndSettle();
expect(focusNode.hasPrimaryFocus, isFalse);
focusNode.dispose();
});

testWidgets('disabled and hovered OutlinedButton.icon responds to mouse-exit', (WidgetTester tester) async {
int onHoverCount = 0;
late bool hover;
const Key key = Key('OutlinedButton.icon');
Widget buildFrame({ required bool enabled }) {
return Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
width: 100,
height: 100,
child: OutlinedButton.icon(
key: key,
onPressed: enabled ? () { } : null,
onHover: (bool value) {
onHoverCount += 1;
hover = value;
},
label: const Text('OutlinedButton'),
icon: const Icon(Icons.add),
),
),
),
);
}

await tester.pumpWidget(buildFrame(enabled: true));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();

await gesture.moveTo(tester.getCenter(find.byKey(key)));
await tester.pumpAndSettle();
expect(onHoverCount, 1);
expect(hover, true);

await tester.pumpWidget(buildFrame(enabled: false));
await tester.pumpAndSettle();
await gesture.moveTo(Offset.zero);
// Even though the OutlinedButton has been disabled, the mouse-exit still
// causes onHover(false) to be called.
expect(onHoverCount, 2);
expect(hover, false);

await gesture.moveTo(tester.getCenter(find.byKey(key)));
await tester.pumpAndSettle();
// We no longer see hover events because the OutlinedButton is disabled
// and it's no longer in the "hovering" state.
expect(onHoverCount, 2);
expect(hover, false);

await tester.pumpWidget(buildFrame(enabled: true));
await tester.pumpAndSettle();
// The OutlinedButton was enabled while it contained the mouse, however
// we do not call onHover() because it may call setState().
expect(onHoverCount, 2);
expect(hover, false);

await gesture.moveTo(tester.getCenter(find.byKey(key)) - const Offset(1, 1));
await tester.pumpAndSettle();
// Moving the mouse a little within the OutlinedButton doesn't change anything.
expect(onHoverCount, 2);
expect(hover, false);
});

testWidgets('Can set OutlinedButton.icon focus and Can set unFocus.', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'OutlinedButton.icon Focus');
bool gotFocus = false;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: OutlinedButton.icon(
focusNode: node,
onFocusChange: (bool focused) => gotFocus = focused,
onPressed: () { },
label: const SizedBox(),
icon: const Icon(Icons.add),
),
),
);

node.requestFocus();
await tester.pump();
expect(gotFocus, isTrue);
expect(node.hasFocus, isTrue);
node.unfocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
node.dispose();
});

testWidgets('When OutlinedButton.icon disable, Can not set OutlinedButton.icon focus.', (WidgetTester tester) async {
final FocusNode node = FocusNode(debugLabel: 'OutlinedButton.icon Focus');
bool gotFocus = false;
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: OutlinedButton.icon(
focusNode: node,
onFocusChange: (bool focused) => gotFocus = focused,
onPressed: null,
label: const SizedBox(),
icon: const Icon(Icons.add),
),
),
);

node.requestFocus();
await tester.pump();
expect(gotFocus, isFalse);
expect(node.hasFocus, isFalse);
node.dispose();
});
}

TextStyle _iconStyle(WidgetTester tester, IconData icon) {
Expand Down

0 comments on commit 059189e

Please sign in to comment.