Unverified Commit 44d7bd35 authored by Chinmoy's avatar Chinmoy Committed by GitHub

Added enableFeedback property to BottomNavigationBar (#74043)

parent a65ce5ba
...@@ -275,6 +275,7 @@ class BottomNavigationBar extends StatefulWidget { ...@@ -275,6 +275,7 @@ class BottomNavigationBar extends StatefulWidget {
this.showSelectedLabels, this.showSelectedLabels,
this.showUnselectedLabels, this.showUnselectedLabels,
this.mouseCursor, this.mouseCursor,
this.enableFeedback,
}) : assert(items != null), }) : assert(items != null),
assert(items.length >= 2), assert(items.length >= 2),
assert( assert(
...@@ -410,6 +411,16 @@ class BottomNavigationBar extends StatefulWidget { ...@@ -410,6 +411,16 @@ class BottomNavigationBar extends StatefulWidget {
/// If this property is null, [SystemMouseCursors.click] will be used. /// If this property is null, [SystemMouseCursors.click] will be used.
final MouseCursor? mouseCursor; final MouseCursor? mouseCursor;
/// 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 @override
_BottomNavigationBarState createState() => _BottomNavigationBarState(); _BottomNavigationBarState createState() => _BottomNavigationBarState();
} }
...@@ -434,6 +445,7 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -434,6 +445,7 @@ class _BottomNavigationTile extends StatelessWidget {
required this.showUnselectedLabels, required this.showUnselectedLabels,
this.indexLabel, this.indexLabel,
required this.mouseCursor, required this.mouseCursor,
required this.enableFeedback,
}) : assert(type != null), }) : assert(type != null),
assert(item != null), assert(item != null),
assert(animation != null), assert(animation != null),
...@@ -458,6 +470,7 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -458,6 +470,7 @@ class _BottomNavigationTile extends StatelessWidget {
final bool showSelectedLabels; final bool showSelectedLabels;
final bool showUnselectedLabels; final bool showUnselectedLabels;
final MouseCursor mouseCursor; final MouseCursor mouseCursor;
final bool enableFeedback;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
...@@ -542,6 +555,7 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -542,6 +555,7 @@ class _BottomNavigationTile extends StatelessWidget {
Widget result = InkResponse( Widget result = InkResponse(
onTap: onTap, onTap: onTap,
mouseCursor: mouseCursor, mouseCursor: mouseCursor,
enableFeedback: enableFeedback,
child: Padding( child: Padding(
padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding), padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding),
child: Column( child: Column(
...@@ -969,6 +983,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -969,6 +983,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
unselectedIconTheme: widget.unselectedIconTheme ?? bottomTheme.unselectedIconTheme, unselectedIconTheme: widget.unselectedIconTheme ?? bottomTheme.unselectedIconTheme,
selectedLabelStyle: effectiveSelectedLabelStyle, selectedLabelStyle: effectiveSelectedLabelStyle,
unselectedLabelStyle: effectiveUnselectedLabelStyle, unselectedLabelStyle: effectiveUnselectedLabelStyle,
enableFeedback: widget.enableFeedback ?? bottomTheme.enableFeedback ?? true,
onTap: () { onTap: () {
if (widget.onTap != null) if (widget.onTap != null)
widget.onTap!(i); widget.onTap!(i);
......
...@@ -43,6 +43,7 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -43,6 +43,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
this.showSelectedLabels, this.showSelectedLabels,
this.showUnselectedLabels, this.showUnselectedLabels,
this.type, this.type,
this.enableFeedback,
}); });
/// The color of the [BottomNavigationBar] itself. /// The color of the [BottomNavigationBar] itself.
...@@ -114,6 +115,11 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -114,6 +115,11 @@ class BottomNavigationBarThemeData with Diagnosticable {
/// See [BottomNavigationBar.type]. /// See [BottomNavigationBar.type].
final BottomNavigationBarType? type; final BottomNavigationBarType? type;
/// If specified, defines the feedback property for [BottomNavigationBar].
///
/// If [BottomNavigationBar.enableFeedback] is provided, [enableFeedback] is ignored.
final bool? enableFeedback;
/// Creates a copy of this object but with the given fields replaced with the /// Creates a copy of this object but with the given fields replaced with the
/// new values. /// new values.
BottomNavigationBarThemeData copyWith({ BottomNavigationBarThemeData copyWith({
...@@ -128,6 +134,7 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -128,6 +134,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
bool? showSelectedLabels, bool? showSelectedLabels,
bool? showUnselectedLabels, bool? showUnselectedLabels,
BottomNavigationBarType? type, BottomNavigationBarType? type,
bool? enableFeedback,
}) { }) {
return BottomNavigationBarThemeData( return BottomNavigationBarThemeData(
backgroundColor: backgroundColor ?? this.backgroundColor, backgroundColor: backgroundColor ?? this.backgroundColor,
...@@ -141,6 +148,7 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -141,6 +148,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
showSelectedLabels: showSelectedLabels ?? this.showSelectedLabels, showSelectedLabels: showSelectedLabels ?? this.showSelectedLabels,
showUnselectedLabels: showUnselectedLabels ?? this.showUnselectedLabels, showUnselectedLabels: showUnselectedLabels ?? this.showUnselectedLabels,
type: type ?? this.type, type: type ?? this.type,
enableFeedback: enableFeedback ?? this.enableFeedback,
); );
} }
...@@ -163,6 +171,7 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -163,6 +171,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
showSelectedLabels: t < 0.5 ? a?.showSelectedLabels : b?.showSelectedLabels, showSelectedLabels: t < 0.5 ? a?.showSelectedLabels : b?.showSelectedLabels,
showUnselectedLabels: t < 0.5 ? a?.showUnselectedLabels : b?.showUnselectedLabels, showUnselectedLabels: t < 0.5 ? a?.showUnselectedLabels : b?.showUnselectedLabels,
type: t < 0.5 ? a?.type : b?.type, type: t < 0.5 ? a?.type : b?.type,
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
); );
} }
...@@ -180,6 +189,7 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -180,6 +189,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
showSelectedLabels, showSelectedLabels,
showUnselectedLabels, showUnselectedLabels,
type, type,
enableFeedback,
); );
} }
...@@ -200,7 +210,8 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -200,7 +210,8 @@ class BottomNavigationBarThemeData with Diagnosticable {
&& other.unselectedLabelStyle == unselectedLabelStyle && other.unselectedLabelStyle == unselectedLabelStyle
&& other.showSelectedLabels == showSelectedLabels && other.showSelectedLabels == showSelectedLabels
&& other.showUnselectedLabels == showUnselectedLabels && other.showUnselectedLabels == showUnselectedLabels
&& other.type == type; && other.type == type
&& other.enableFeedback == enableFeedback;
} }
@override @override
...@@ -217,6 +228,7 @@ class BottomNavigationBarThemeData with Diagnosticable { ...@@ -217,6 +228,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<bool>('showSelectedLabels', showSelectedLabels, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('showSelectedLabels', showSelectedLabels, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('showUnselectedLabels', showUnselectedLabels, defaultValue: null)); properties.add(DiagnosticsProperty<bool>('showUnselectedLabels', showUnselectedLabels, defaultValue: null));
properties.add(DiagnosticsProperty<BottomNavigationBarType>('type', type, defaultValue: null)); properties.add(DiagnosticsProperty<BottomNavigationBarType>('type', type, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('enableFeedback', enableFeedback, defaultValue: null));
} }
} }
......
...@@ -11,6 +11,7 @@ import 'package:vector_math/vector_math_64.dart' show Vector3; ...@@ -11,6 +11,7 @@ import 'package:vector_math/vector_math_64.dart' show Vector3;
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('BottomNavigationBar callback test', (WidgetTester tester) async { testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
...@@ -1828,6 +1829,94 @@ void main() { ...@@ -1828,6 +1829,94 @@ void main() {
expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click); expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.click);
}); });
group('feedback', () {
late FeedbackTester feedback;
setUp(() {
feedback = FeedbackTester();
});
tearDown(() {
feedback.dispose();
});
Widget feedbackBoilerplate({bool? enableFeedback, bool? enableFeedbackTheme}) {
return MaterialApp(
home: Scaffold(
bottomNavigationBar: BottomNavigationBarTheme(
data: BottomNavigationBarThemeData(
enableFeedback: enableFeedbackTheme,
),
child: BottomNavigationBar(
enableFeedback: enableFeedback,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')),
BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')),
],
),
),
),
);
}
testWidgets('BottomNavigationBar with enabled feedback', (WidgetTester tester) async {
const bool enableFeedback = true;
await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback));
await tester.tap(find.byType(InkResponse).first);
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 1);
expect(feedback.hapticCount, 0);
});
testWidgets('BottomNavigationBar with disabled feedback', (WidgetTester tester) async {
const bool enableFeedback = false;
await tester.pumpWidget(feedbackBoilerplate(enableFeedback: enableFeedback));
await tester.tap(find.byType(InkResponse).first);
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 0);
expect(feedback.hapticCount, 0);
});
testWidgets('BottomNavigationBar with enabled feedback by default', (WidgetTester tester) async {
await tester.pumpWidget(feedbackBoilerplate());
await tester.tap(find.byType(InkResponse).first);
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 1);
expect(feedback.hapticCount, 0);
});
testWidgets('BottomNavigationBar with disabled feedback using BottomNavigationBarTheme', (WidgetTester tester) async {
const bool enableFeedbackTheme = false;
await tester.pumpWidget(feedbackBoilerplate(enableFeedbackTheme: enableFeedbackTheme));
await tester.tap(find.byType(InkResponse).first);
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 0);
expect(feedback.hapticCount, 0);
});
testWidgets('BottomNavigationBar.enableFeedback overrides BottomNavigationBarTheme.enableFeedback', (WidgetTester tester) async {
const bool enableFeedbackTheme = false;
const bool enableFeedback = true;
await tester.pumpWidget(feedbackBoilerplate(
enableFeedbackTheme: enableFeedbackTheme,
enableFeedback: enableFeedback
));
await tester.tap(find.byType(InkResponse).first);
await tester.pumpAndSettle();
expect(feedback.clickSoundCount, 1);
expect(feedback.hapticCount, 0);
});
});
testWidgets('BottomNavigationBar excludes semantics', testWidgets('BottomNavigationBar excludes semantics',
(WidgetTester tester) async { (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester); final SemanticsTester semantics = SemanticsTester(tester);
......
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