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 { ...@@ -252,6 +252,7 @@ class SnackBar extends StatefulWidget {
this.duration = _snackBarDisplayDuration, this.duration = _snackBarDisplayDuration,
this.animation, this.animation,
this.onVisible, this.onVisible,
this.dismissDirection = DismissDirection.down,
}) : assert(elevation == null || elevation >= 0.0), }) : assert(elevation == null || elevation >= 0.0),
assert(content != null), assert(content != null),
assert( assert(
...@@ -368,6 +369,11 @@ class SnackBar extends StatefulWidget { ...@@ -368,6 +369,11 @@ class SnackBar extends StatefulWidget {
/// Called the first time that the snackbar is visible within a [Scaffold]. /// Called the first time that the snackbar is visible within a [Scaffold].
final VoidCallback? onVisible; 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(): // API for ScaffoldMessengerState.showSnackBar():
/// Creates an animation controller useful for driving a snack bar's entrance and exit animation. /// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
...@@ -398,6 +404,7 @@ class SnackBar extends StatefulWidget { ...@@ -398,6 +404,7 @@ class SnackBar extends StatefulWidget {
duration: duration, duration: duration,
animation: newAnimation, animation: newAnimation,
onVisible: onVisible, onVisible: onVisible,
dismissDirection: dismissDirection,
); );
} }
...@@ -593,7 +600,7 @@ class _SnackBarState extends State<SnackBar> { ...@@ -593,7 +600,7 @@ class _SnackBarState extends State<SnackBar> {
}, },
child: Dismissible( child: Dismissible(
key: const Key('dismissible'), key: const Key('dismissible'),
direction: DismissDirection.down, direction: widget.dismissDirection,
resizeDuration: null, resizeDuration: null,
onDismissed: (DismissDirection direction) { onDismissed: (DismissDirection direction) {
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe); Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
......
...@@ -421,12 +421,17 @@ void main() { ...@@ -421,12 +421,17 @@ void main() {
}); });
testWidgets('SnackBar dismiss test', (WidgetTester tester) async { testWidgets('SnackBar dismiss test', (WidgetTester tester) async {
int snackBarCount = 0;
const Key tapTarget = Key('tap-target'); const Key tapTarget = Key('tap-target');
late DismissDirection dismissDirection;
late double width;
int snackBarCount = 0;
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
body: Builder( body: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
width = MediaQuery.of(context).size.width;
return GestureDetector( return GestureDetector(
key: tapTarget, key: tapTarget,
onTap: () { onTap: () {
...@@ -434,6 +439,7 @@ void main() { ...@@ -434,6 +439,7 @@ void main() {
Scaffold.of(context).showSnackBar(SnackBar( Scaffold.of(context).showSnackBar(SnackBar(
content: Text('bar$snackBarCount'), content: Text('bar$snackBarCount'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
dismissDirection: dismissDirection,
)); ));
}, },
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
...@@ -446,32 +452,28 @@ void main() { ...@@ -446,32 +452,28 @@ void main() {
), ),
), ),
)); ));
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing); await _testSnackBarDismiss(
await tester.tap(find.byKey(tapTarget)); // queue bar1 tester: tester,
await tester.tap(find.byKey(tapTarget)); // queue bar2 tapTarget: tapTarget,
expect(find.text('bar1'), findsNothing); scaffoldWidth: width,
expect(find.text('bar2'), findsNothing); onDismissDirectionChange: (DismissDirection dir) => dismissDirection = dir,
await tester.pump(); // schedule animation for bar1 onDragGestureChange: () => snackBarCount = 0,
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);
}); });
testWidgets('SnackBar dismiss test - ScaffoldMessenger', (WidgetTester tester) async { testWidgets('SnackBar dismiss test - ScaffoldMessenger', (WidgetTester tester) async {
int snackBarCount = 0;
const Key tapTarget = Key('tap-target'); const Key tapTarget = Key('tap-target');
late DismissDirection dismissDirection;
late double width;
int snackBarCount = 0;
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Scaffold( home: Scaffold(
body: Builder( body: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
width = MediaQuery.of(context).size.width;
return GestureDetector( return GestureDetector(
key: tapTarget, key: tapTarget,
onTap: () { onTap: () {
...@@ -479,6 +481,7 @@ void main() { ...@@ -479,6 +481,7 @@ void main() {
ScaffoldMessenger.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('bar$snackBarCount'), content: Text('bar$snackBarCount'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
dismissDirection: dismissDirection,
)); ));
}, },
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
...@@ -491,23 +494,14 @@ void main() { ...@@ -491,23 +494,14 @@ void main() {
), ),
), ),
)); ));
expect(find.text('bar1'), findsNothing);
expect(find.text('bar2'), findsNothing); await _testSnackBarDismiss(
await tester.tap(find.byKey(tapTarget)); // queue bar1 tester: tester,
await tester.tap(find.byKey(tapTarget)); // queue bar2 tapTarget: tapTarget,
expect(find.text('bar1'), findsNothing); scaffoldWidth: width,
expect(find.text('bar2'), findsNothing); onDismissDirectionChange: (DismissDirection dir) => dismissDirection = dir,
await tester.pump(); // schedule animation for bar1 onDragGestureChange: () => snackBarCount = 0,
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);
}); });
testWidgets('SnackBar cannot be tapped twice', (WidgetTester tester) async { testWidgets('SnackBar cannot be tapped twice', (WidgetTester tester) async {
...@@ -2371,3 +2365,82 @@ void main() { ...@@ -2371,3 +2365,82 @@ void main() {
expect(find.text(snackBars[2]), findsNothing); 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