Added enableFeedback property to BottomNavigationBar (#74043)
This commit is contained in:
parent
a65ce5bae7
commit
44d7bd3550
@ -275,6 +275,7 @@ class BottomNavigationBar extends StatefulWidget {
|
||||
this.showSelectedLabels,
|
||||
this.showUnselectedLabels,
|
||||
this.mouseCursor,
|
||||
this.enableFeedback,
|
||||
}) : assert(items != null),
|
||||
assert(items.length >= 2),
|
||||
assert(
|
||||
@ -410,6 +411,16 @@ class BottomNavigationBar extends StatefulWidget {
|
||||
/// If this property is null, [SystemMouseCursors.click] will be used.
|
||||
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
|
||||
_BottomNavigationBarState createState() => _BottomNavigationBarState();
|
||||
}
|
||||
@ -434,6 +445,7 @@ class _BottomNavigationTile extends StatelessWidget {
|
||||
required this.showUnselectedLabels,
|
||||
this.indexLabel,
|
||||
required this.mouseCursor,
|
||||
required this.enableFeedback,
|
||||
}) : assert(type != null),
|
||||
assert(item != null),
|
||||
assert(animation != null),
|
||||
@ -458,6 +470,7 @@ class _BottomNavigationTile extends StatelessWidget {
|
||||
final bool showSelectedLabels;
|
||||
final bool showUnselectedLabels;
|
||||
final MouseCursor mouseCursor;
|
||||
final bool enableFeedback;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -542,6 +555,7 @@ class _BottomNavigationTile extends StatelessWidget {
|
||||
Widget result = InkResponse(
|
||||
onTap: onTap,
|
||||
mouseCursor: mouseCursor,
|
||||
enableFeedback: enableFeedback,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: topPadding, bottom: bottomPadding),
|
||||
child: Column(
|
||||
@ -969,6 +983,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
|
||||
unselectedIconTheme: widget.unselectedIconTheme ?? bottomTheme.unselectedIconTheme,
|
||||
selectedLabelStyle: effectiveSelectedLabelStyle,
|
||||
unselectedLabelStyle: effectiveUnselectedLabelStyle,
|
||||
enableFeedback: widget.enableFeedback ?? bottomTheme.enableFeedback ?? true,
|
||||
onTap: () {
|
||||
if (widget.onTap != null)
|
||||
widget.onTap!(i);
|
||||
|
@ -43,6 +43,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
this.showSelectedLabels,
|
||||
this.showUnselectedLabels,
|
||||
this.type,
|
||||
this.enableFeedback,
|
||||
});
|
||||
|
||||
/// The color of the [BottomNavigationBar] itself.
|
||||
@ -114,6 +115,11 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
/// See [BottomNavigationBar.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
|
||||
/// new values.
|
||||
BottomNavigationBarThemeData copyWith({
|
||||
@ -128,6 +134,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
bool? showSelectedLabels,
|
||||
bool? showUnselectedLabels,
|
||||
BottomNavigationBarType? type,
|
||||
bool? enableFeedback,
|
||||
}) {
|
||||
return BottomNavigationBarThemeData(
|
||||
backgroundColor: backgroundColor ?? this.backgroundColor,
|
||||
@ -141,6 +148,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
showSelectedLabels: showSelectedLabels ?? this.showSelectedLabels,
|
||||
showUnselectedLabels: showUnselectedLabels ?? this.showUnselectedLabels,
|
||||
type: type ?? this.type,
|
||||
enableFeedback: enableFeedback ?? this.enableFeedback,
|
||||
);
|
||||
}
|
||||
|
||||
@ -163,6 +171,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
showSelectedLabels: t < 0.5 ? a?.showSelectedLabels : b?.showSelectedLabels,
|
||||
showUnselectedLabels: t < 0.5 ? a?.showUnselectedLabels : b?.showUnselectedLabels,
|
||||
type: t < 0.5 ? a?.type : b?.type,
|
||||
enableFeedback: t < 0.5 ? a?.enableFeedback : b?.enableFeedback,
|
||||
);
|
||||
}
|
||||
|
||||
@ -180,6 +189,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
showSelectedLabels,
|
||||
showUnselectedLabels,
|
||||
type,
|
||||
enableFeedback,
|
||||
);
|
||||
}
|
||||
|
||||
@ -200,7 +210,8 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
&& other.unselectedLabelStyle == unselectedLabelStyle
|
||||
&& other.showSelectedLabels == showSelectedLabels
|
||||
&& other.showUnselectedLabels == showUnselectedLabels
|
||||
&& other.type == type;
|
||||
&& other.type == type
|
||||
&& other.enableFeedback == enableFeedback;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -217,6 +228,7 @@ class BottomNavigationBarThemeData with Diagnosticable {
|
||||
properties.add(DiagnosticsProperty<bool>('showSelectedLabels', showSelectedLabels, defaultValue: null));
|
||||
properties.add(DiagnosticsProperty<bool>('showUnselectedLabels', showUnselectedLabels, 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;
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
import '../widgets/semantics_tester.dart';
|
||||
import 'feedback_tester.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
|
||||
@ -1828,6 +1829,94 @@ void main() {
|
||||
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',
|
||||
(WidgetTester tester) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
|
Loading…
x
Reference in New Issue
Block a user