Unverified Commit 457f513f authored by Rick Krystianne Lim's avatar Rick Krystianne Lim Committed by GitHub

Changing SnackBar's dismiss direction (#77514)

parent f82dedd5
......@@ -252,6 +252,7 @@ class SnackBar extends StatefulWidget {
this.duration = _snackBarDisplayDuration,
this.animation,
this.onVisible,
this.dismissDirection = DismissDirection.down,
}) : assert(elevation == null || elevation >= 0.0),
assert(content != null),
assert(
......@@ -368,6 +369,11 @@ class SnackBar extends StatefulWidget {
/// Called the first time that the snackbar is visible within a [Scaffold].
final VoidCallback? onVisible;
/// The direction in which the SnackBar can be dismissed.
///
/// Cannot be null, defaults to [DismissDirection.down].
final DismissDirection dismissDirection;
// API for ScaffoldMessengerState.showSnackBar():
/// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
......@@ -398,6 +404,7 @@ class SnackBar extends StatefulWidget {
duration: duration,
animation: newAnimation,
onVisible: onVisible,
dismissDirection: dismissDirection,
);
}
......@@ -593,7 +600,7 @@ class _SnackBarState extends State<SnackBar> {
},
child: Dismissible(
key: const Key('dismissible'),
direction: DismissDirection.down,
direction: widget.dismissDirection,
resizeDuration: null,
onDismissed: (DismissDirection direction) {
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
......
......@@ -421,12 +421,17 @@ void main() {
});
testWidgets('SnackBar dismiss test', (WidgetTester tester) async {
int snackBarCount = 0;
const Key tapTarget = Key('tap-target');
late DismissDirection dismissDirection;
late double width;
int snackBarCount = 0;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
width = MediaQuery.of(context).size.width;
return GestureDetector(
key: tapTarget,
onTap: () {
......@@ -434,6 +439,7 @@ void main() {
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('bar$snackBarCount'),
duration: const Duration(seconds: 2),
dismissDirection: dismissDirection,
));
},
behavior: HitTestBehavior.opaque,
......@@ -446,32 +452,28 @@ void main() {
),
),
));
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
await tester.tap(find.byKey(tapTarget)); // queue bar1
await tester.tap(find.byKey(tapTarget)); // queue bar2
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
await tester.pump(); // schedule animation for bar1
expect(find.text('bar1'), findsOneWidget);
expect(find.text('bar2'), findsNothing);
await tester.pump(); // begin animation
expect(find.text('bar1'), findsOneWidget);
expect(find.text('bar2'), findsNothing);
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
await tester.drag(find.text('bar1'), const Offset(0.0, 50.0));
await tester.pump(); // bar1 dismissed, bar2 begins animating
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsOneWidget);
await _testSnackBarDismiss(
tester: tester,
tapTarget: tapTarget,
scaffoldWidth: width,
onDismissDirectionChange: (DismissDirection dir) => dismissDirection = dir,
onDragGestureChange: () => snackBarCount = 0,
);
});
testWidgets('SnackBar dismiss test - ScaffoldMessenger', (WidgetTester tester) async {
int snackBarCount = 0;
const Key tapTarget = Key('tap-target');
late DismissDirection dismissDirection;
late double width;
int snackBarCount = 0;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Builder(
builder: (BuildContext context) {
width = MediaQuery.of(context).size.width;
return GestureDetector(
key: tapTarget,
onTap: () {
......@@ -479,6 +481,7 @@ void main() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('bar$snackBarCount'),
duration: const Duration(seconds: 2),
dismissDirection: dismissDirection,
));
},
behavior: HitTestBehavior.opaque,
......@@ -491,23 +494,14 @@ void main() {
),
),
));
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
await tester.tap(find.byKey(tapTarget)); // queue bar1
await tester.tap(find.byKey(tapTarget)); // queue bar2
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
await tester.pump(); // schedule animation for bar1
expect(find.text('bar1'), findsOneWidget);
expect(find.text('bar2'), findsNothing);
await tester.pump(); // begin animation
expect(find.text('bar1'), findsOneWidget);
expect(find.text('bar2'), findsNothing);
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
await tester.drag(find.text('bar1'), const Offset(0.0, 50.0));
await tester.pump(); // bar1 dismissed, bar2 begins animating
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsOneWidget);
await _testSnackBarDismiss(
tester: tester,
tapTarget: tapTarget,
scaffoldWidth: width,
onDismissDirectionChange: (DismissDirection dir) => dismissDirection = dir,
onDragGestureChange: () => snackBarCount = 0,
);
});
testWidgets('SnackBar cannot be tapped twice', (WidgetTester tester) async {
......@@ -2371,3 +2365,82 @@ void main() {
expect(find.text(snackBars[2]), findsNothing);
});
}
/// Start test for "SnackBar dismiss test".
Future<void> _testSnackBarDismiss({
required WidgetTester tester,
required Key tapTarget,
required double scaffoldWidth,
required ValueChanged<DismissDirection> onDismissDirectionChange,
required VoidCallback onDragGestureChange,
}) async {
final Map<DismissDirection, List<Offset>> dragGestures = _getDragGesturesOfDismissDirections(scaffoldWidth);
for (final DismissDirection key in dragGestures.keys) {
onDismissDirectionChange(key);
for (final Offset dragGesture in dragGestures[key]!) {
onDragGestureChange();
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
await tester.tap(find.byKey(tapTarget)); // queue bar1
await tester.tap(find.byKey(tapTarget)); // queue bar2
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
await tester.pump(); // schedule animation for bar1
expect(find.text('bar1'), findsOneWidget);
expect(find.text('bar2'), findsNothing);
await tester.pump(); // begin animation
expect(find.text('bar1'), findsOneWidget);
expect(find.text('bar2'), findsNothing);
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
await tester.drag(find.text('bar1'), dragGesture);
await tester.pump(); // bar1 dismissed, bar2 begins animating
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
await tester.drag(find.text('bar2'), dragGesture);
await tester.pump(); // bar2 dismissed
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing);
}
}
}
/// Create drag gestures for DismissDirections.
Map<DismissDirection, List<Offset>> _getDragGesturesOfDismissDirections(double scaffoldWidth) {
final Map<DismissDirection, List<Offset>> dragGestures = <DismissDirection, List<Offset>>{};
for (final DismissDirection val in DismissDirection.values) {
switch (val) {
case DismissDirection.down:
dragGestures[val] = <Offset>[const Offset(0.0, 50.0)]; // drag to bottom gesture
break;
case DismissDirection.up:
dragGestures[val] = <Offset>[const Offset(0.0, -50.0)]; // drag to top gesture
break;
case DismissDirection.vertical:
dragGestures[val] = <Offset>[
const Offset(0.0, 50.0), // drag to bottom gesture
const Offset(0.0, -50.0), // drag to top gesture
];
break;
case DismissDirection.startToEnd:
dragGestures[val] = <Offset>[Offset(scaffoldWidth, 0.0)]; // drag to right gesture
break;
case DismissDirection.endToStart:
dragGestures[val] = <Offset>[Offset(-scaffoldWidth, 0.0)]; // drag to left gesture
break;
case DismissDirection.horizontal:
dragGestures[val] = <Offset>[
Offset(scaffoldWidth, 0.0), // drag to right gesture
Offset(-scaffoldWidth, 0.0), // drag to left gesture
];
break;
default:
}
}
return dragGestures;
}
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