Unverified Commit 336ae2de authored by Ludwik Trammer's avatar Ludwik Trammer Committed by GitHub

Add "onTap" callback to PopupMenuItem (#81686)

parent a3893aea
......@@ -219,6 +219,7 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
const PopupMenuItem({
Key? key,
this.value,
this.onTap,
this.enabled = true,
this.height = kMinInteractiveDimension,
this.padding,
......@@ -232,6 +233,9 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
/// The value that will be returned by [showMenu] if this entry is selected.
final T? value;
/// Called when the menu item is tapped.
final VoidCallback? onTap;
/// Whether the user is permitted to select this item.
///
/// Defaults to true. If this is false, then the item will not react to
......@@ -319,6 +323,8 @@ class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
/// the menu route.
@protected
void handleTap() {
widget.onTap?.call();
Navigator.pop<T>(context, widget.value);
}
......
......@@ -289,6 +289,139 @@ void main() {
expect(Focus.of(childKey.currentContext!).hasPrimaryFocus, isTrue);
});
testWidgets('PopupMenuItem onTap callback is called when defined', (WidgetTester tester) async {
final List<int> menuItemTapCounters = <int>[0, 0];
await tester.pumpWidget(
TestApp(
textDirection: TextDirection.ltr,
child: Material(
child: RepaintBoundary(
child: PopupMenuButton<void>(
child: const Text('Actions'),
itemBuilder: (BuildContext context) => <PopupMenuItem<void>>[
PopupMenuItem<void>(
child: const Text('First option'),
onTap: () {
menuItemTapCounters[0]++;
},
),
PopupMenuItem<void>(
child: const Text('Second option'),
onTap: () {
menuItemTapCounters[1]++;
},
),
const PopupMenuItem<void>(
child: Text('Option without onTap'),
),
],
),
),
),
),
);
// Tap the first tiem
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('First option'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[1, 0]);
// Tap the item again
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('First option'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[2, 0]);
// Tap a different item
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('Second option'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[2, 1]);
// Tap an iteem without onTap
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('Option without onTap'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[2, 1]);
});
testWidgets('PopupMenuItem can have both onTap and value', (WidgetTester tester) async {
final List<int> menuItemTapCounters = <int>[0, 0];
String? selected;
await tester.pumpWidget(
TestApp(
textDirection: TextDirection.ltr,
child: Material(
child: RepaintBoundary(
child: PopupMenuButton<String>(
child: const Text('Actions'),
onSelected: (String value) { selected = value; },
itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[
PopupMenuItem<String>(
child: const Text('First option'),
value: 'first',
onTap: () {
menuItemTapCounters[0]++;
},
),
PopupMenuItem<String>(
child: const Text('Second option'),
value: 'second',
onTap: () {
menuItemTapCounters[1]++;
},
),
const PopupMenuItem<String>(
child: Text('Option without onTap'),
value: 'third',
),
],
),
),
),
),
);
// Tap the first item
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('First option'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[1, 0]);
expect(selected, 'first');
// Tap the item again
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('First option'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[2, 0]);
expect(selected, 'first');
// Tap a different item
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('Second option'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[2, 1]);
expect(selected, 'second');
// Tap an iteem without onTap
await tester.tap(find.text('Actions'));
await tester.pumpAndSettle();
await tester.tap(find.text('Option without onTap'));
await tester.pumpAndSettle();
expect(menuItemTapCounters, <int>[2, 1]);
expect(selected, 'third');
});
testWidgets('PopupMenuItem is only focusable when enabled', (WidgetTester tester) async {
final Key popupButtonKey = UniqueKey();
final GlobalKey childKey = GlobalKey();
......
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