Add enableFeedback param to MaterialButton, RawMaterialButton and IconButton (#41972)
* Wire enableFeedback parameter through MaterialButton, RawMaterialButton, and IconButton.
Co-Authored-By: Shi-Hao Hong <shihaohong@google.com>
diff --git a/packages/flutter/lib/src/material/button.dart b/packages/flutter/lib/src/material/button.dart
index 4b4c9cc..daf89a0 100644
--- a/packages/flutter/lib/src/material/button.dart
+++ b/packages/flutter/lib/src/material/button.dart
@@ -60,6 +60,7 @@
this.autofocus = false,
MaterialTapTargetSize materialTapTargetSize,
this.child,
+ this.enableFeedback = true,
}) : materialTapTargetSize = materialTapTargetSize ?? MaterialTapTargetSize.padded,
assert(shape != null),
assert(elevation != null && elevation >= 0.0),
@@ -259,6 +260,16 @@
/// Defaults to [Clip.none], and must not be null.
final Clip clipBehavior;
+ /// 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.
+ ///
+ /// See also:
+ ///
+ /// * [Feedback] for providing platform-specific feedback to certain actions.
+ final bool enableFeedback;
+
@override
_RawMaterialButtonState createState() => _RawMaterialButtonState();
}
@@ -367,6 +378,7 @@
onHover: _handleHoveredChanged,
onTap: widget.onPressed,
onLongPress: widget.onLongPress,
+ enableFeedback: widget.enableFeedback,
customBorder: effectiveShape,
child: IconTheme.merge(
data: IconThemeData(color: effectiveTextColor),
diff --git a/packages/flutter/lib/src/material/icon_button.dart b/packages/flutter/lib/src/material/icon_button.dart
index 6fe788d..ba90468 100644
--- a/packages/flutter/lib/src/material/icon_button.dart
+++ b/packages/flutter/lib/src/material/icon_button.dart
@@ -151,6 +151,7 @@
this.focusNode,
this.autofocus = false,
this.tooltip,
+ this.enableFeedback = true,
}) : assert(iconSize != null),
assert(padding != null),
assert(alignment != null),
@@ -269,6 +270,16 @@
/// used for accessibility.
final String tooltip;
+ /// 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.
+ ///
+ /// See also:
+ ///
+ /// * [Feedback] for providing platform-specific feedback to certain actions.
+ final bool enableFeedback;
+
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
@@ -314,6 +325,7 @@
autofocus: autofocus,
canRequestFocus: onPressed != null,
onTap: onPressed,
+ enableFeedback: enableFeedback,
child: result,
focusColor: focusColor ?? Theme.of(context).focusColor,
hoverColor: hoverColor ?? Theme.of(context).hoverColor,
diff --git a/packages/flutter/lib/src/material/material_button.dart b/packages/flutter/lib/src/material/material_button.dart
index 5b12358..20a2b04 100644
--- a/packages/flutter/lib/src/material/material_button.dart
+++ b/packages/flutter/lib/src/material/material_button.dart
@@ -77,6 +77,7 @@
this.animationDuration,
this.minWidth,
this.height,
+ this.enableFeedback = true,
this.child,
}) : assert(clipBehavior != null),
assert(autofocus != null),
@@ -355,6 +356,16 @@
/// Defaults to the value from the current [ButtonTheme].
final double height;
+ /// 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.
+ ///
+ /// See also:
+ ///
+ /// * [Feedback] for providing platform-specific feedback to certain actions.
+ final bool enableFeedback;
+
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
@@ -363,6 +374,7 @@
return RawMaterialButton(
onPressed: onPressed,
onLongPress: onLongPress,
+ enableFeedback: enableFeedback,
onHighlightChanged: onHighlightChanged,
fillColor: buttonTheme.getFillColor(this),
textStyle: theme.textTheme.button.copyWith(color: buttonTheme.getTextColor(this)),
diff --git a/packages/flutter/test/material/icon_button_test.dart b/packages/flutter/test/material/icon_button_test.dart
index 247441f..8cf16f3 100644
--- a/packages/flutter/test/material/icon_button_test.dart
+++ b/packages/flutter/test/material/icon_button_test.dart
@@ -10,6 +10,7 @@
import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
+import 'feedback_tester.dart';
class MockOnPressedFunction implements Function {
int called = 0;
@@ -398,6 +399,74 @@
expect(focusNode1.hasPrimaryFocus, isTrue);
expect(focusNode2.hasPrimaryFocus, isFalse);
});
+
+ group('feedback', () {
+ FeedbackTester feedback;
+
+ setUp(() {
+ feedback = FeedbackTester();
+ });
+
+ tearDown(() {
+ feedback?.dispose();
+ });
+
+ testWidgets('IconButton with disabled feedback', (WidgetTester tester) async {
+ await tester.pumpWidget(Material(
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: Center(
+ child: IconButton(
+ onPressed: () {},
+ enableFeedback: false,
+ icon: const Icon(Icons.link),
+ ),
+ ),
+ ),
+ ));
+ await tester.tap(find.byType(IconButton), pointer: 1);
+ await tester.pump(const Duration(seconds: 1));
+ expect(feedback.clickSoundCount, 0);
+ expect(feedback.hapticCount, 0);
+ });
+
+ testWidgets('IconButton with enabled feedback', (WidgetTester tester) async {
+ await tester.pumpWidget(Material(
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: Center(
+ child: IconButton(
+ onPressed: () {},
+ enableFeedback: true,
+ icon: const Icon(Icons.link),
+ ),
+ ),
+ ),
+ ));
+ await tester.tap(find.byType(IconButton), pointer: 1);
+ await tester.pump(const Duration(seconds: 1));
+ expect(feedback.clickSoundCount, 1);
+ expect(feedback.hapticCount, 0);
+ });
+
+ testWidgets('IconButton with enabled feedback by default', (WidgetTester tester) async {
+ await tester.pumpWidget(Material(
+ child: Directionality(
+ textDirection: TextDirection.ltr,
+ child: Center(
+ child: IconButton(
+ onPressed: () {},
+ icon: const Icon(Icons.link),
+ ),
+ ),
+ ),
+ ));
+ await tester.tap(find.byType(IconButton), pointer: 1);
+ await tester.pump(const Duration(seconds: 1));
+ expect(feedback.clickSoundCount, 1);
+ expect(feedback.hapticCount, 0);
+ });
+ });
}
Widget wrap({ Widget child }) {