Added enableFeedback property to FloatingActionButton (#69826)
This commit is contained in:
parent
02efffc134
commit
d118748737
@ -147,6 +147,7 @@ class FloatingActionButton extends StatelessWidget {
|
|||||||
this.autofocus = false,
|
this.autofocus = false,
|
||||||
this.materialTapTargetSize,
|
this.materialTapTargetSize,
|
||||||
this.isExtended = false,
|
this.isExtended = false,
|
||||||
|
this.enableFeedback,
|
||||||
}) : assert(elevation == null || elevation >= 0.0),
|
}) : assert(elevation == null || elevation >= 0.0),
|
||||||
assert(focusElevation == null || focusElevation >= 0.0),
|
assert(focusElevation == null || focusElevation >= 0.0),
|
||||||
assert(hoverElevation == null || hoverElevation >= 0.0),
|
assert(hoverElevation == null || hoverElevation >= 0.0),
|
||||||
@ -189,6 +190,7 @@ class FloatingActionButton extends StatelessWidget {
|
|||||||
this.autofocus = false,
|
this.autofocus = false,
|
||||||
Widget? icon,
|
Widget? icon,
|
||||||
required Widget label,
|
required Widget label,
|
||||||
|
this.enableFeedback,
|
||||||
}) : assert(elevation == null || elevation >= 0.0),
|
}) : assert(elevation == null || elevation >= 0.0),
|
||||||
assert(focusElevation == null || focusElevation >= 0.0),
|
assert(focusElevation == null || focusElevation >= 0.0),
|
||||||
assert(hoverElevation == null || hoverElevation >= 0.0),
|
assert(hoverElevation == null || hoverElevation >= 0.0),
|
||||||
@ -409,6 +411,19 @@ class FloatingActionButton extends StatelessWidget {
|
|||||||
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
|
/// * [MaterialTapTargetSize], for a description of how this affects tap targets.
|
||||||
final MaterialTapTargetSize? materialTapTargetSize;
|
final MaterialTapTargetSize? materialTapTargetSize;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// If null, [FloatingActionButtonThemeData.enableFeedback] is used.
|
||||||
|
/// If both are null, then default value is true.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [Feedback] for providing platform-specific feedback to certain actions.
|
||||||
|
final bool? enableFeedback;
|
||||||
|
|
||||||
final BoxConstraints _sizeConstraints;
|
final BoxConstraints _sizeConstraints;
|
||||||
|
|
||||||
static const double _defaultElevation = 6;
|
static const double _defaultElevation = 6;
|
||||||
@ -455,6 +470,8 @@ class FloatingActionButton extends StatelessWidget {
|
|||||||
?? _defaultHighlightElevation;
|
?? _defaultHighlightElevation;
|
||||||
final MaterialTapTargetSize materialTapTargetSize = this.materialTapTargetSize
|
final MaterialTapTargetSize materialTapTargetSize = this.materialTapTargetSize
|
||||||
?? theme.materialTapTargetSize;
|
?? theme.materialTapTargetSize;
|
||||||
|
final bool enableFeedback = this.enableFeedback
|
||||||
|
?? floatingActionButtonTheme.enableFeedback ?? true;
|
||||||
final TextStyle textStyle = theme.textTheme.button!.copyWith(
|
final TextStyle textStyle = theme.textTheme.button!.copyWith(
|
||||||
color: foregroundColor,
|
color: foregroundColor,
|
||||||
letterSpacing: 1.2,
|
letterSpacing: 1.2,
|
||||||
@ -483,6 +500,7 @@ class FloatingActionButton extends StatelessWidget {
|
|||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
autofocus: autofocus,
|
autofocus: autofocus,
|
||||||
child: child,
|
child: child,
|
||||||
|
enableFeedback: enableFeedback,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (tooltip != null) {
|
if (tooltip != null) {
|
||||||
|
@ -42,6 +42,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
this.disabledElevation,
|
this.disabledElevation,
|
||||||
this.highlightElevation,
|
this.highlightElevation,
|
||||||
this.shape,
|
this.shape,
|
||||||
|
this.enableFeedback,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Color to be used for the unselected, enabled [FloatingActionButton]'s
|
/// Color to be used for the unselected, enabled [FloatingActionButton]'s
|
||||||
@ -89,6 +90,12 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
/// The shape to be used for the floating action button's [Material].
|
/// The shape to be used for the floating action button's [Material].
|
||||||
final ShapeBorder? shape;
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
|
/// If specified, defines the feedback property for [FloatingActionButton].
|
||||||
|
///
|
||||||
|
/// If [FloatingActionButton.enableFeedback] is provided, [enableFeedback] is
|
||||||
|
/// ignored.
|
||||||
|
final bool? enableFeedback;
|
||||||
|
|
||||||
/// Creates a copy of this object with the given fields replaced with the
|
/// Creates a copy of this object with the given fields replaced with the
|
||||||
/// new values.
|
/// new values.
|
||||||
FloatingActionButtonThemeData copyWith({
|
FloatingActionButtonThemeData copyWith({
|
||||||
@ -103,6 +110,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
double? disabledElevation,
|
double? disabledElevation,
|
||||||
double? highlightElevation,
|
double? highlightElevation,
|
||||||
ShapeBorder? shape,
|
ShapeBorder? shape,
|
||||||
|
bool? enableFeedback,
|
||||||
}) {
|
}) {
|
||||||
return FloatingActionButtonThemeData(
|
return FloatingActionButtonThemeData(
|
||||||
foregroundColor: foregroundColor ?? this.foregroundColor,
|
foregroundColor: foregroundColor ?? this.foregroundColor,
|
||||||
@ -116,6 +124,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
disabledElevation: disabledElevation ?? this.disabledElevation,
|
disabledElevation: disabledElevation ?? this.disabledElevation,
|
||||||
highlightElevation: highlightElevation ?? this.highlightElevation,
|
highlightElevation: highlightElevation ?? this.highlightElevation,
|
||||||
shape: shape ?? this.shape,
|
shape: shape ?? this.shape,
|
||||||
|
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,6 +149,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
disabledElevation: lerpDouble(a?.disabledElevation, b?.disabledElevation, t),
|
disabledElevation: lerpDouble(a?.disabledElevation, b?.disabledElevation, t),
|
||||||
highlightElevation: lerpDouble(a?.highlightElevation, b?.highlightElevation, t),
|
highlightElevation: lerpDouble(a?.highlightElevation, b?.highlightElevation, t),
|
||||||
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
|
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
|
||||||
|
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +167,7 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
disabledElevation,
|
disabledElevation,
|
||||||
highlightElevation,
|
highlightElevation,
|
||||||
shape,
|
shape,
|
||||||
|
enableFeedback,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +188,8 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
&& other.hoverElevation == hoverElevation
|
&& other.hoverElevation == hoverElevation
|
||||||
&& other.disabledElevation == disabledElevation
|
&& other.disabledElevation == disabledElevation
|
||||||
&& other.highlightElevation == highlightElevation
|
&& other.highlightElevation == highlightElevation
|
||||||
&& other.shape == shape;
|
&& other.shape == shape
|
||||||
|
&& other.enableFeedback == enableFeedback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -196,5 +208,6 @@ class FloatingActionButtonThemeData with Diagnosticable {
|
|||||||
properties.add(DoubleProperty('disabledElevation', disabledElevation, defaultValue: defaultData.disabledElevation));
|
properties.add(DoubleProperty('disabledElevation', disabledElevation, defaultValue: defaultData.disabledElevation));
|
||||||
properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: defaultData.highlightElevation));
|
properties.add(DoubleProperty('highlightElevation', highlightElevation, defaultValue: defaultData.highlightElevation));
|
||||||
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultData.shape));
|
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: defaultData.shape));
|
||||||
|
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: defaultData.enableFeedback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import 'package:flutter_test/flutter_test.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';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Floating Action Button control test', (WidgetTester tester) async {
|
testWidgets('Floating Action Button control test', (WidgetTester tester) async {
|
||||||
@ -963,6 +964,116 @@ void main() {
|
|||||||
expect(find.byKey(iconKey), findsOneWidget);
|
expect(find.byKey(iconKey), findsOneWidget);
|
||||||
expect(find.byKey(labelKey), findsNothing);
|
expect(find.byKey(labelKey), findsNothing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('feedback', () {
|
||||||
|
late FeedbackTester feedback;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
feedback = FeedbackTester();
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
feedback.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton with enabled feedback', (WidgetTester tester) async {
|
||||||
|
const bool enableFeedback = true;
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: FloatingActionButton(
|
||||||
|
onPressed: () {},
|
||||||
|
enableFeedback: enableFeedback,
|
||||||
|
child: const Icon(Icons.access_alarm),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.byType(RawMaterialButton));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(feedback.clickSoundCount, 1);
|
||||||
|
expect(feedback.hapticCount, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton with disabled feedback', (WidgetTester tester) async {
|
||||||
|
const bool enableFeedback = false;
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: FloatingActionButton(
|
||||||
|
onPressed: () {},
|
||||||
|
enableFeedback: enableFeedback,
|
||||||
|
child: const Icon(Icons.access_alarm),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.byType(RawMaterialButton));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(feedback.clickSoundCount, 0);
|
||||||
|
expect(feedback.hapticCount, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton with enabled feedback by default', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: FloatingActionButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Icon(Icons.access_alarm),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.byType(RawMaterialButton));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(feedback.clickSoundCount, 1);
|
||||||
|
expect(feedback.hapticCount, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton with disabled feedback using FloatingActionButtonTheme', (WidgetTester tester) async {
|
||||||
|
const bool enableFeedbackTheme = false;
|
||||||
|
final ThemeData theme = ThemeData(
|
||||||
|
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||||
|
enableFeedback: enableFeedbackTheme,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Theme(
|
||||||
|
data: theme,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Icon(Icons.access_alarm),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.byType(RawMaterialButton));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(feedback.clickSoundCount, 0);
|
||||||
|
expect(feedback.hapticCount, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('FloatingActionButton.enableFeedback is overriden by FloatingActionButtonThemeData.enableFeedback', (WidgetTester tester) async {
|
||||||
|
const bool enableFeedbackTheme = false;
|
||||||
|
const bool enableFeedback = true;
|
||||||
|
final ThemeData theme = ThemeData(
|
||||||
|
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||||
|
enableFeedback: enableFeedbackTheme,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(MaterialApp(
|
||||||
|
home: Theme(
|
||||||
|
data: theme,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
enableFeedback: enableFeedback,
|
||||||
|
onPressed: () {},
|
||||||
|
child: const Icon(Icons.access_alarm),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.byType(RawMaterialButton));
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
expect(feedback.clickSoundCount, 1);
|
||||||
|
expect(feedback.hapticCount, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Offset _rightEdgeOfFab(WidgetTester tester) {
|
Offset _rightEdgeOfFab(WidgetTester tester) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user