Unverified Commit 57d7946b authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

Implement DropdownButton and DropdownButtonFormField onTap callback (#53105)

parent b6655330
...@@ -787,6 +787,7 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -787,6 +787,7 @@ class DropdownButton<T> extends StatefulWidget {
this.hint, this.hint,
this.disabledHint, this.disabledHint,
@required this.onChanged, @required this.onChanged,
this.onTap,
this.elevation = 8, this.elevation = 8,
this.style, this.style,
this.underline, this.underline,
...@@ -858,6 +859,14 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -858,6 +859,14 @@ class DropdownButton<T> extends StatefulWidget {
/// {@endtemplate} /// {@endtemplate}
final ValueChanged<T> onChanged; final ValueChanged<T> onChanged;
/// Called when the dropdown button is tapped.
///
/// This is distinct from [onChanged], which is called when the user
/// selects an item from the dropdown.
///
/// The callback will not be invoked if the dropdown button is disabled.
final VoidCallback onTap;
/// A builder to customize the dropdown buttons corresponding to the /// A builder to customize the dropdown buttons corresponding to the
/// [DropdownMenuItem]s in [items]. /// [DropdownMenuItem]s in [items].
/// ///
...@@ -1179,6 +1188,10 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1179,6 +1188,10 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
if (widget.onChanged != null) if (widget.onChanged != null)
widget.onChanged(newValue.result); widget.onChanged(newValue.result);
}); });
if (widget.onTap != null) {
widget.onTap();
}
} }
Action _createAction() { Action _createAction() {
...@@ -1402,6 +1415,7 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1402,6 +1415,7 @@ class DropdownButtonFormField<T> extends FormField<T> {
DropdownButtonBuilder selectedItemBuilder, DropdownButtonBuilder selectedItemBuilder,
Widget hint, Widget hint,
@required this.onChanged, @required this.onChanged,
VoidCallback onTap,
this.decoration = const InputDecoration(), this.decoration = const InputDecoration(),
FormFieldSetter<T> onSaved, FormFieldSetter<T> onSaved,
FormFieldValidator<T> validator, FormFieldValidator<T> validator,
...@@ -1452,6 +1466,7 @@ class DropdownButtonFormField<T> extends FormField<T> { ...@@ -1452,6 +1466,7 @@ class DropdownButtonFormField<T> extends FormField<T> {
selectedItemBuilder: selectedItemBuilder, selectedItemBuilder: selectedItemBuilder,
hint: hint, hint: hint,
onChanged: onChanged == null ? null : state.didChange, onChanged: onChanged == null ? null : state.didChange,
onTap: onTap,
disabledHint: disabledHint, disabledHint: disabledHint,
elevation: elevation, elevation: elevation,
style: style, style: style,
......
...@@ -31,6 +31,7 @@ Widget buildFormFrame({ ...@@ -31,6 +31,7 @@ Widget buildFormFrame({
int elevation = 8, int elevation = 8,
String value = 'two', String value = 'two',
ValueChanged<String> onChanged, ValueChanged<String> onChanged,
VoidCallback onTap,
Widget icon, Widget icon,
Color iconDisabledColor, Color iconDisabledColor,
Color iconEnabledColor, Color iconEnabledColor,
...@@ -58,6 +59,7 @@ Widget buildFormFrame({ ...@@ -58,6 +59,7 @@ Widget buildFormFrame({
hint: hint, hint: hint,
disabledHint: disabledHint, disabledHint: disabledHint,
onChanged: onChanged, onChanged: onChanged,
onTap: onTap,
icon: icon, icon: icon,
iconSize: iconSize, iconSize: iconSize,
iconDisabledColor: iconDisabledColor, iconDisabledColor: iconDisabledColor,
...@@ -669,4 +671,48 @@ void main() { ...@@ -669,4 +671,48 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(find.text('Two as an Arabic numeral: 2'), findsOneWidget); expect(find.text('Two as an Arabic numeral: 2'), findsOneWidget);
}); });
testWidgets('DropdownButton onTap callback is called when defined', (WidgetTester tester) async {
int dropdownButtonTapCounter = 0;
String value = 'one';
void onChanged(String newValue) { value = newValue; }
void onTap() { dropdownButtonTapCounter += 1; }
Widget build() => buildFormFrame(
value: value,
onChanged: onChanged,
onTap: onTap,
);
await tester.pumpWidget(build());
expect(dropdownButtonTapCounter, 0);
// Tap dropdown button.
await tester.tap(find.text('one'));
await tester.pumpAndSettle();
expect(value, equals('one'));
expect(dropdownButtonTapCounter, 1); // Should update counter.
// Tap dropdown menu item.
await tester.tap(find.text('three').last);
await tester.pumpAndSettle();
expect(value, equals('three'));
expect(dropdownButtonTapCounter, 1); // Should not change.
// Tap dropdown button again.
await tester.tap(find.text('three'));
await tester.pumpAndSettle();
expect(value, equals('three'));
expect(dropdownButtonTapCounter, 2); // Should update counter.
// Tap dropdown menu item.
await tester.tap(find.text('two').last);
await tester.pumpAndSettle();
expect(value, equals('two'));
expect(dropdownButtonTapCounter, 2); // Should not change.
});
} }
...@@ -34,6 +34,7 @@ Widget buildFrame({ ...@@ -34,6 +34,7 @@ Widget buildFrame({
Key buttonKey, Key buttonKey,
String value = 'two', String value = 'two',
ValueChanged<String> onChanged, ValueChanged<String> onChanged,
VoidCallback onTap,
Widget icon, Widget icon,
Color iconDisabledColor, Color iconDisabledColor,
Color iconEnabledColor, Color iconEnabledColor,
...@@ -66,6 +67,7 @@ Widget buildFrame({ ...@@ -66,6 +67,7 @@ Widget buildFrame({
hint: hint, hint: hint,
disabledHint: disabledHint, disabledHint: disabledHint,
onChanged: onChanged, onChanged: onChanged,
onTap: onTap,
icon: icon, icon: icon,
iconSize: iconSize, iconSize: iconSize,
iconDisabledColor: iconDisabledColor, iconDisabledColor: iconDisabledColor,
...@@ -2314,4 +2316,49 @@ void main() { ...@@ -2314,4 +2316,49 @@ void main() {
// tree, causing it to lose focus. // tree, causing it to lose focus.
expect(Focus.of(tester.element(find.byKey(const ValueKey<int>(91)).last)).hasPrimaryFocus, isFalse); expect(Focus.of(tester.element(find.byKey(const ValueKey<int>(91)).last)).hasPrimaryFocus, isFalse);
}, skip: kIsWeb); }, skip: kIsWeb);
testWidgets('DropdownButton onTap callback is called when defined', (WidgetTester tester) async {
int dropdownButtonTapCounter = 0;
String value = 'one';
void onChanged(String newValue) { value = newValue; }
void onTap() { dropdownButtonTapCounter += 1; }
Widget build() => buildFrame(
value: value,
onChanged: onChanged,
onTap: onTap,
);
await tester.pumpWidget(build());
expect(dropdownButtonTapCounter, 0);
// Tap dropdown button.
await tester.tap(find.text('one'));
await tester.pumpAndSettle();
expect(value, equals('one'));
expect(dropdownButtonTapCounter, 1); // Should update counter.
// Tap dropdown menu item.
await tester.tap(find.text('three').last);
await tester.pumpAndSettle();
expect(value, equals('three'));
expect(dropdownButtonTapCounter, 1); // Should not change.
// Tap dropdown button again.
await tester.tap(find.text('three'));
await tester.pumpAndSettle();
expect(value, equals('three'));
expect(dropdownButtonTapCounter, 2); // Should update counter.
// Tap dropdown menu item.
await tester.tap(find.text('two').last);
await tester.pumpAndSettle();
expect(value, equals('two'));
expect(dropdownButtonTapCounter, 2); // Should not change.
});
} }
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