Unverified Commit 2bc7939a authored by Viren Manojkumar Khatri's avatar Viren Manojkumar Khatri Committed by GitHub

Add disable argument in DropdownMenuItem (#76968)

Co-authored-by: 's avatarShi-Hao Hong <shihaohong@google.com>
Adds a disable argument in DropdownMenuItem widget and added tests.
Design Doc: docs.google.com/document/d/13W6PupVZUt6TenoE3NaTP9OCYsKBIYg9YgdurKe1XKs
parent 56c0002c
......@@ -155,6 +155,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
@override
Widget build(BuildContext context) {
final DropdownMenuItem<T> dropdownMenuItem = widget.route.items[widget.itemIndex].item!;
final CurvedAnimation opacity;
final double unit = 0.5 / (widget.route.items.length + 1.5);
if (widget.itemIndex == widget.route.selectedIndex) {
......@@ -164,19 +165,21 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
final double end = (start + 1.5 * unit).clamp(0.0, 1.0);
opacity = CurvedAnimation(parent: widget.route.animation!, curve: Interval(start, end));
}
Widget child = FadeTransition(
opacity: opacity,
child: InkWell(
Widget child = Container(
padding: widget.padding,
child: widget.route.items[widget.itemIndex],
);
// An [InkWell] is added to the item only if it is enabled
if (dropdownMenuItem.enabled) {
child = InkWell(
autofocus: widget.itemIndex == widget.route.selectedIndex,
child: Container(
padding: widget.padding,
child: widget.route.items[widget.itemIndex],
),
child: child,
onTap: _handleOnTap,
onFocusChange: _handleFocusChange,
),
);
if (kIsWeb) {
);
}
child = FadeTransition(opacity: opacity, child: child);
if (kIsWeb && dropdownMenuItem.enabled) {
child = Shortcuts(
shortcuts: _webShortcuts,
child: child,
......@@ -685,6 +688,7 @@ class DropdownMenuItem<T> extends _DropdownMenuItemContainer {
Key? key,
this.onTap,
this.value,
this.enabled = true,
required Widget child,
}) : assert(child != null),
super(key: key, child: child);
......@@ -696,6 +700,11 @@ class DropdownMenuItem<T> extends _DropdownMenuItemContainer {
///
/// Eventually returned in a call to [DropdownButton.onChanged].
final T? value;
/// Whether or not a user can select this menu item.
///
/// Defaults to `true`.
final bool enabled;
}
/// An inherited widget that causes any descendant [DropdownButton]
......@@ -1197,7 +1206,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
|| widget.items!.isEmpty
|| (widget.value == null &&
widget.items!
.where((DropdownMenuItem<T> item) => item.value == widget.value)
.where((DropdownMenuItem<T> item) => item.enabled && item.value == widget.value)
.isEmpty)) {
_selectedIndex = null;
return;
......
......@@ -3165,4 +3165,81 @@ void main() {
..rrect(rrect: const RRect.fromLTRBXY(0.0, 0.0, 112.0, 47.0, 2.0, 2.0), color: Colors.grey[50], hasMaskFilter: false)
);
});
testWidgets('Tapping a disabled item should not close DropdownButton', (WidgetTester tester) async {
String? value = 'first';
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) => DropdownButton<String>(
value: value,
items: const <DropdownMenuItem<String>>[
DropdownMenuItem<String>(
enabled: false,
child: Text('disabled'),
),
DropdownMenuItem<String>(
value: 'first',
child: Text('first'),
),
DropdownMenuItem<String>(
value: 'second',
child: Text('second'),
),
],
onChanged: (String? newValue) {
setState(() {
value = newValue;
});
},
),
),
),
),
);
// Open dropdown.
await tester.tap(find.text('first').hitTestable());
await tester.pumpAndSettle();
// Tap on a disabled item.
await tester.tap(find.text('disabled').hitTestable());
await tester.pumpAndSettle();
// The dropdown should still be open, i.e., there should be one widget with 'second' text.
expect(find.text('second').hitTestable(), findsOneWidget);
});
testWidgets('Disabled item should not be focusable', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: DropdownButton<String>(
value: 'enabled',
onChanged: onChanged,
items: const <DropdownMenuItem<String>>[
DropdownMenuItem<String>(
enabled: false,
child: Text('disabled'),
),
DropdownMenuItem<String>(
value: 'enabled',
child: Text('enabled'),
)
],
),
),
),
);
// Open dropdown.
await tester.tap(find.text('enabled').hitTestable());
await tester.pumpAndSettle();
// The `FocusNode` of [disabledItem] should be `null` as enabled is false.
final Element disabledItem = tester.element(find.text('disabled').hitTestable());
expect(Focus.maybeOf(disabledItem), null, reason: 'Disabled menu item should not be able to request focus');
});
}
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