Add onCancelled callback to PopupMenu (#14226)
* Add onCancelled callback to PopupMenu * Fix spelling, don't call onCanceled if disposed, improve documentation.
This commit is contained in:
parent
2e449f06f0
commit
52d47751a0
@ -685,6 +685,12 @@ Future<T> showMenu<T>({
|
||||
/// Used by [PopupMenuButton.onSelected].
|
||||
typedef void PopupMenuItemSelected<T>(T value);
|
||||
|
||||
/// Signature for the callback invoked when a [PopupMenuButton] is dismissed
|
||||
/// without selecting an item.
|
||||
///
|
||||
/// Used by [PopupMenuButton.onCanceled].
|
||||
typedef void PopupMenuCanceled();
|
||||
|
||||
/// Signature used by [PopupMenuButton] to lazily construct the items shown when
|
||||
/// the button is pressed.
|
||||
///
|
||||
@ -750,6 +756,7 @@ class PopupMenuButton<T> extends StatefulWidget {
|
||||
@required this.itemBuilder,
|
||||
this.initialValue,
|
||||
this.onSelected,
|
||||
this.onCanceled,
|
||||
this.tooltip,
|
||||
this.elevation: 8.0,
|
||||
this.padding: const EdgeInsets.all(8.0),
|
||||
@ -766,8 +773,16 @@ class PopupMenuButton<T> extends StatefulWidget {
|
||||
final T initialValue;
|
||||
|
||||
/// Called when the user selects a value from the popup menu created by this button.
|
||||
///
|
||||
/// If the popup menu is dismissed without selecting a value, [onCanceled] is
|
||||
/// called instead.
|
||||
final PopupMenuItemSelected<T> onSelected;
|
||||
|
||||
/// Called when the user dismisses the popup menu without selecting an item.
|
||||
///
|
||||
/// If the user selects a value, [onSelected] is called instead.
|
||||
final PopupMenuCanceled onCanceled;
|
||||
|
||||
/// Text that describes the action that will occur when the button is pressed.
|
||||
///
|
||||
/// This text is displayed when the user long-presses on the button and is
|
||||
@ -814,8 +829,13 @@ class _PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
|
||||
position: position,
|
||||
)
|
||||
.then<void>((T newValue) {
|
||||
if (!mounted || newValue == null)
|
||||
if (!mounted)
|
||||
return null;
|
||||
if (newValue == null) {
|
||||
if (widget.onCanceled != null)
|
||||
widget.onCanceled();
|
||||
return null;
|
||||
}
|
||||
if (widget.onSelected != null)
|
||||
widget.onSelected(newValue);
|
||||
});
|
||||
|
@ -58,6 +58,72 @@ void main() {
|
||||
expect(find.text('Next'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('PopupMenuButton calls onCanceled callback when an item is not selected', (WidgetTester tester) async {
|
||||
int cancels = 0;
|
||||
BuildContext popupContext;
|
||||
final Key noCallbackKey = new UniqueKey();
|
||||
final Key withCallbackKey = new UniqueKey();
|
||||
|
||||
await tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
home: new Material(
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new PopupMenuButton<int>(
|
||||
key: noCallbackKey,
|
||||
itemBuilder: (BuildContext context) {
|
||||
return <PopupMenuEntry<int>>[
|
||||
const PopupMenuItem<int>(
|
||||
value: 1,
|
||||
child: const Text('Tap me please!'),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
new PopupMenuButton<int>(
|
||||
key: withCallbackKey,
|
||||
onCanceled: () => cancels++,
|
||||
itemBuilder: (BuildContext context) {
|
||||
popupContext = context;
|
||||
return <PopupMenuEntry<int>>[
|
||||
const PopupMenuItem<int>(
|
||||
value: 1,
|
||||
child: const Text('Tap me, too!'),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Make sure everything works if no callback is provided
|
||||
await tester.tap(find.byKey(noCallbackKey));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await tester.tapAt(const Offset(0.0, 0.0));
|
||||
await tester.pump();
|
||||
expect(cancels, equals(0));
|
||||
|
||||
// Make sure callback is called when a non-selection tap occurs
|
||||
await tester.tap(find.byKey(withCallbackKey));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
await tester.tapAt(const Offset(0.0, 0.0));
|
||||
await tester.pump();
|
||||
expect(cancels, equals(1));
|
||||
|
||||
// Make sure callback is called when back navigation occurs
|
||||
await tester.tap(find.byKey(withCallbackKey));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
Navigator.of(popupContext).pop();
|
||||
await tester.pump();
|
||||
expect(cancels, equals(2));
|
||||
});
|
||||
|
||||
testWidgets('PopupMenuButton is horizontal on iOS', (WidgetTester tester) async {
|
||||
Widget build(TargetPlatform platform) {
|
||||
return new MaterialApp(
|
||||
|
Loading…
x
Reference in New Issue
Block a user