Unverified Commit dc40e238 authored by Chinmoy's avatar Chinmoy Committed by GitHub

Added enableFeedback property to DropdownButton (#69880)

parent 895b317e
...@@ -97,6 +97,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget { ...@@ -97,6 +97,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget {
required this.buttonRect, required this.buttonRect,
required this.constraints, required this.constraints,
required this.itemIndex, required this.itemIndex,
required this.enableFeedback,
}) : super(key: key); }) : super(key: key);
final _DropdownRoute<T> route; final _DropdownRoute<T> route;
...@@ -104,6 +105,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget { ...@@ -104,6 +105,7 @@ class _DropdownMenuItemButton<T> extends StatefulWidget {
final Rect buttonRect; final Rect buttonRect;
final BoxConstraints constraints; final BoxConstraints constraints;
final int itemIndex; final int itemIndex;
final bool enableFeedback;
@override @override
_DropdownMenuItemButtonState<T> createState() => _DropdownMenuItemButtonState<T>(); _DropdownMenuItemButtonState<T> createState() => _DropdownMenuItemButtonState<T>();
...@@ -173,6 +175,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>> ...@@ -173,6 +175,7 @@ class _DropdownMenuItemButtonState<T> extends State<_DropdownMenuItemButton<T>>
if (dropdownMenuItem.enabled) { if (dropdownMenuItem.enabled) {
child = InkWell( child = InkWell(
autofocus: widget.itemIndex == widget.route.selectedIndex, autofocus: widget.itemIndex == widget.route.selectedIndex,
enableFeedback: widget.enableFeedback,
child: child, child: child,
onTap: _handleOnTap, onTap: _handleOnTap,
onFocusChange: _handleFocusChange, onFocusChange: _handleFocusChange,
...@@ -197,6 +200,7 @@ class _DropdownMenu<T> extends StatefulWidget { ...@@ -197,6 +200,7 @@ class _DropdownMenu<T> extends StatefulWidget {
required this.buttonRect, required this.buttonRect,
required this.constraints, required this.constraints,
this.dropdownColor, this.dropdownColor,
required this.enableFeedback,
}) : super(key: key); }) : super(key: key);
final _DropdownRoute<T> route; final _DropdownRoute<T> route;
...@@ -204,6 +208,7 @@ class _DropdownMenu<T> extends StatefulWidget { ...@@ -204,6 +208,7 @@ class _DropdownMenu<T> extends StatefulWidget {
final Rect buttonRect; final Rect buttonRect;
final BoxConstraints constraints; final BoxConstraints constraints;
final Color? dropdownColor; final Color? dropdownColor;
final bool enableFeedback;
@override @override
_DropdownMenuState<T> createState() => _DropdownMenuState<T>(); _DropdownMenuState<T> createState() => _DropdownMenuState<T>();
...@@ -253,6 +258,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> { ...@@ -253,6 +258,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
buttonRect: widget.buttonRect, buttonRect: widget.buttonRect,
constraints: widget.constraints, constraints: widget.constraints,
itemIndex: itemIndex, itemIndex: itemIndex,
enableFeedback: widget.enableFeedback,
), ),
]; ];
...@@ -411,6 +417,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> { ...@@ -411,6 +417,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
this.itemHeight, this.itemHeight,
this.dropdownColor, this.dropdownColor,
this.menuMaxHeight, this.menuMaxHeight,
required this.enableFeedback,
}) : assert(style != null), }) : assert(style != null),
itemHeights = List<double>.filled(items.length, itemHeight ?? kMinInteractiveDimension); itemHeights = List<double>.filled(items.length, itemHeight ?? kMinInteractiveDimension);
...@@ -424,7 +431,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> { ...@@ -424,7 +431,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
final double? itemHeight; final double? itemHeight;
final Color? dropdownColor; final Color? dropdownColor;
final double? menuMaxHeight; final double? menuMaxHeight;
final bool enableFeedback;
final List<double> itemHeights; final List<double> itemHeights;
ScrollController? scrollController; ScrollController? scrollController;
...@@ -456,6 +463,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> { ...@@ -456,6 +463,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
capturedThemes: capturedThemes, capturedThemes: capturedThemes,
style: style, style: style,
dropdownColor: dropdownColor, dropdownColor: dropdownColor,
enableFeedback: enableFeedback,
); );
}, },
); );
...@@ -551,6 +559,7 @@ class _DropdownRoutePage<T> extends StatelessWidget { ...@@ -551,6 +559,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
required this.capturedThemes, required this.capturedThemes,
this.style, this.style,
required this.dropdownColor, required this.dropdownColor,
required this.enableFeedback,
}) : super(key: key); }) : super(key: key);
final _DropdownRoute<T> route; final _DropdownRoute<T> route;
...@@ -563,6 +572,7 @@ class _DropdownRoutePage<T> extends StatelessWidget { ...@@ -563,6 +572,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
final CapturedThemes capturedThemes; final CapturedThemes capturedThemes;
final TextStyle? style; final TextStyle? style;
final Color? dropdownColor; final Color? dropdownColor;
final bool enableFeedback;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -586,6 +596,7 @@ class _DropdownRoutePage<T> extends StatelessWidget { ...@@ -586,6 +596,7 @@ class _DropdownRoutePage<T> extends StatelessWidget {
buttonRect: buttonRect, buttonRect: buttonRect,
constraints: constraints, constraints: constraints,
dropdownColor: dropdownColor, dropdownColor: dropdownColor,
enableFeedback: enableFeedback,
); );
return MediaQuery.removePadding( return MediaQuery.removePadding(
...@@ -854,6 +865,7 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -854,6 +865,7 @@ class DropdownButton<T> extends StatefulWidget {
this.autofocus = false, this.autofocus = false,
this.dropdownColor, this.dropdownColor,
this.menuMaxHeight, this.menuMaxHeight,
this.enableFeedback,
// When adding new arguments, consider adding similar arguments to // When adding new arguments, consider adding similar arguments to
// DropdownButtonFormField. // DropdownButtonFormField.
}) : assert(items == null || items.isEmpty || value == null || }) : assert(items == null || items.isEmpty || value == null ||
...@@ -1114,6 +1126,18 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -1114,6 +1126,18 @@ class DropdownButton<T> extends StatefulWidget {
/// and bottom of the menu by at one menu item's height. /// and bottom of the menu by at one menu item's height.
final double? menuMaxHeight; final double? menuMaxHeight;
/// Whether detected gestures should provide acoustic and/or haptic feedback.
///
/// For example, on Android a tap will produce a clicking sound and a
/// long-press will produce a short vibration, when feedback is enabled.
///
/// By default, platform-specific feedback is enabled.
///
/// See also:
///
/// * [Feedback] for providing platform-specific feedback to certain actions.
final bool? enableFeedback;
@override @override
_DropdownButtonState<T> createState() => _DropdownButtonState<T>(); _DropdownButtonState<T> createState() => _DropdownButtonState<T>();
} }
...@@ -1266,6 +1290,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -1266,6 +1290,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
itemHeight: widget.itemHeight, itemHeight: widget.itemHeight,
dropdownColor: widget.dropdownColor, dropdownColor: widget.dropdownColor,
menuMaxHeight: widget.menuMaxHeight, menuMaxHeight: widget.menuMaxHeight,
enableFeedback: widget.enableFeedback ?? true,
); );
navigator.push(_dropdownRoute!).then<void>((_DropdownRouteResult<T>? newValue) { navigator.push(_dropdownRoute!).then<void>((_DropdownRouteResult<T>? newValue) {
......
...@@ -12,6 +12,7 @@ import 'package:flutter/services.dart'; ...@@ -12,6 +12,7 @@ import 'package:flutter/services.dart';
import '../rendering/mock_canvas.dart'; import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
import 'feedback_tester.dart';
const List<String> menuItems = <String>['one', 'two', 'three', 'four']; const List<String> menuItems = <String>['one', 'two', 'three', 'four'];
void onChanged<T>(T _) { } void onChanged<T>(T _) { }
...@@ -3242,4 +3243,75 @@ void main() { ...@@ -3242,4 +3243,75 @@ void main() {
final Element disabledItem = tester.element(find.text('disabled').hitTestable()); 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'); expect(Focus.maybeOf(disabledItem), null, reason: 'Disabled menu item should not be able to request focus');
}); });
group('feedback', () {
late FeedbackTester feedback;
setUp(() {
feedback = FeedbackTester();
});
tearDown(() {
feedback.dispose();
});
Widget feedbackBoilerplate({bool? enableFeedback}) {
return MaterialApp(
home : Material(
child: DropdownButton<String>(
value: 'One',
enableFeedback: enableFeedback,
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? value) {},
items: <String>['One', 'Two'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
}
testWidgets('Dropdown with enabled feedback', (WidgetTester tester) async {
const bool enableFeedback = true;
await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback));
await tester.tap(find.text('One'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(InkWell, 'One'));
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 1);
expect(feedback.hapticCount, 0);
});
testWidgets('Dropdown with disabled feedback', (WidgetTester tester) async {
const bool enableFeedback = false;
await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback));
await tester.tap(find.text('One'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(InkWell, 'One'));
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 0);
expect(feedback.hapticCount, 0);
});
testWidgets('Dropdown with enabled feedback by default', (WidgetTester tester) async {
await tester.pumpWidget(feedbackBoilerplate());
await tester.tap(find.text('One'));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(InkWell, 'Two'));
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 1);
expect(feedback.hapticCount, 0);
});
});
} }
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