Fix Tooltip implementation of PopupMenuButton (#42613)
* Fix tooltip not showing when PopupMenuButton.child was non-null
diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart
index 1a64089..2c992fc 100644
--- a/packages/flutter/lib/src/material/popup_menu.dart
+++ b/packages/flutter/lib/src/material/popup_menu.dart
@@ -19,6 +19,7 @@
import 'material_localizations.dart';
import 'popup_menu_theme.dart';
import 'theme.dart';
+import 'tooltip.dart';
// Examples can assume:
// enum Commands { heroAndScholar, hurricaneCame }
@@ -939,7 +940,8 @@
assert(offset != null),
assert(enabled != null),
assert(captureInheritedThemes != null),
- assert(!(child != null && icon != null)), // fails if passed both parameters
+ assert(!(child != null && icon != null),
+ 'You can only pass [child] or [icon], not both.'),
super(key: key);
/// Called when the button is pressed to create the items to show in the menu.
@@ -1080,16 +1082,21 @@
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
- return widget.child != null
- ? InkWell(
+
+ if (widget.child != null)
+ return Tooltip(
+ message: widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip,
+ child: InkWell(
onTap: widget.enabled ? showButtonMenu : null,
child: widget.child,
- )
- : IconButton(
- icon: widget.icon ?? _getIcon(Theme.of(context).platform),
- padding: widget.padding,
- tooltip: widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip,
- onPressed: widget.enabled ? showButtonMenu : null,
- );
+ ),
+ );
+
+ return IconButton(
+ icon: widget.icon ?? _getIcon(Theme.of(context).platform),
+ padding: widget.padding,
+ tooltip: widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip,
+ onPressed: widget.enabled ? showButtonMenu : null,
+ );
}
}
diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart
index 122a077..6b958ae 100644
--- a/packages/flutter/test/material/popup_menu_test.dart
+++ b/packages/flutter/test/material/popup_menu_test.dart
@@ -851,6 +851,129 @@
expect(tester.getTopLeft(find.text('Item 0')).dx, 72);
expect(tester.getTopLeft(find.text('Item 1')).dx, 72);
});
+
+ test("PopupMenuButton's child and icon properties cannot be simultaneously defined", () {
+ expect(() {
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) => <PopupMenuItem<int>>[],
+ child: Container(),
+ icon: const Icon(Icons.error),
+ );
+ }, throwsAssertionError);
+ });
+
+ testWidgets('PopupMenuButton default tooltip', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Material(
+ child: Column(
+ children: <Widget>[
+ // Default Tooltip should be present when [PopupMenuButton.icon]
+ // and [PopupMenuButton.child] are undefined.
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) {
+ return <PopupMenuEntry<int>>[
+ const PopupMenuItem<int>(
+ value: 1,
+ child: Text('Tap me please!'),
+ ),
+ ];
+ },
+ ),
+ // Default Tooltip should be present when
+ // [PopupMenuButton.child] is defined.
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) {
+ return <PopupMenuEntry<int>>[
+ const PopupMenuItem<int>(
+ value: 1,
+ child: Text('Tap me please!'),
+ ),
+ ];
+ },
+ child: const Text('Test text'),
+ ),
+ // Default Tooltip should be present when
+ // [PopupMenuButton.icon] is defined.
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) {
+ return <PopupMenuEntry<int>>[
+ const PopupMenuItem<int>(
+ value: 1,
+ child: Text('Tap me please!'),
+ ),
+ ];
+ },
+ icon: const Icon(Icons.check),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+
+ // The default tooltip is defined as [MaterialLocalizations.showMenuTooltip]
+ // and it is used when no tooltip is provided.
+ expect(find.byType(Tooltip), findsNWidgets(3));
+ expect(find.byTooltip(const DefaultMaterialLocalizations().showMenuTooltip), findsNWidgets(3));
+ });
+
+ testWidgets('PopupMenuButton custom tooltip', (WidgetTester tester) async {
+ await tester.pumpWidget(
+ MaterialApp(
+ home: Material(
+ child: Column(
+ children: <Widget>[
+ // Tooltip should work when [PopupMenuButton.icon]
+ // and [PopupMenuButton.child] are undefined.
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) {
+ return <PopupMenuEntry<int>>[
+ const PopupMenuItem<int>(
+ value: 1,
+ child: Text('Tap me please!'),
+ ),
+ ];
+ },
+ tooltip: 'Test tooltip',
+ ),
+ // Tooltip should work when
+ // [PopupMenuButton.child] is defined.
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) {
+ return <PopupMenuEntry<int>>[
+ const PopupMenuItem<int>(
+ value: 1,
+ child: Text('Tap me please!'),
+ ),
+ ];
+ },
+ tooltip: 'Test tooltip',
+ child: const Text('Test text'),
+ ),
+ // Tooltip should work when
+ // [PopupMenuButton.icon] is defined.
+ PopupMenuButton<int>(
+ itemBuilder: (BuildContext context) {
+ return <PopupMenuEntry<int>>[
+ const PopupMenuItem<int>(
+ value: 1,
+ child: Text('Tap me please!'),
+ ),
+ ];
+ },
+ tooltip: 'Test tooltip',
+ icon: const Icon(Icons.check),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+
+ expect(find.byType(Tooltip), findsNWidgets(3));
+ expect(find.byTooltip('Test tooltip',), findsNWidgets(3));
+ });
}
class TestApp extends StatefulWidget {