Merge pull request #3039 from abarth/more_snack_bar
Don't delay between dismissing a snack bar and the next snack bar
This commit is contained in:
commit
f3d95b6f28
@ -125,6 +125,7 @@ class SnackBar extends StatelessWidget {
|
||||
child: new Dismissable(
|
||||
key: new Key('dismissable'),
|
||||
direction: DismissDirection.down,
|
||||
resizeDuration: null,
|
||||
onDismissed: (DismissDirection direction) {
|
||||
Scaffold.of(context).removeCurrentSnackBar();
|
||||
},
|
||||
|
@ -8,7 +8,6 @@ import 'framework.dart';
|
||||
import 'gesture_detector.dart';
|
||||
|
||||
const Duration _kDismissDuration = const Duration(milliseconds: 200);
|
||||
const Duration _kResizeDuration = const Duration(milliseconds: 300);
|
||||
const Curve _kResizeTimeCurve = const Interval(0.4, 1.0, curve: Curves.ease);
|
||||
const double _kMinFlingVelocity = 700.0;
|
||||
const double _kMinFlingVelocityDelta = 400.0;
|
||||
@ -43,17 +42,19 @@ enum DismissDirection {
|
||||
/// Can be dismissed by dragging in the indicated [direction].
|
||||
///
|
||||
/// Dragging or flinging this widget in the [DismissDirection] causes the child
|
||||
/// to slide out of view. Following the slide animation, the Dismissable widget
|
||||
/// animates its height (or width, whichever is perpendicular to the dismiss
|
||||
/// direction) to zero.
|
||||
/// to slide out of view. Following the slide animation, if [resizeDuration] is
|
||||
/// non-null, the Dismissable widget animates its height (or width, whichever is
|
||||
/// perpendicular to the dismiss direction) to zero over the [resizeDuration].
|
||||
///
|
||||
/// Backgrounds can be used to implement the "leave-behind" idiom. If a background
|
||||
/// is specified it is stacked behind the Dismissable's child and is exposed when
|
||||
/// the child moves.
|
||||
///
|
||||
/// The [onDimissed] callback runs after Dismissable's size has collapsed to zero.
|
||||
/// If the Dismissable is a list item, it must have a key that distinguishes it from
|
||||
/// the other items and its onDismissed callback must remove the item from the list.
|
||||
/// The widget calls the [onDimissed] callback either after its size has
|
||||
/// collapsed to zero (if [resizeDuration] is non-null) or immediately after
|
||||
/// the slide animation (if [resizeDuration] is null). If the Dismissable is a
|
||||
/// list item, it must have a key that distinguishes it from the other items and
|
||||
/// its [onDismissed] callback must remove the item from the list.
|
||||
class Dismissable extends StatefulWidget {
|
||||
Dismissable({
|
||||
Key key,
|
||||
@ -62,7 +63,8 @@ class Dismissable extends StatefulWidget {
|
||||
this.secondaryBackground,
|
||||
this.onResize,
|
||||
this.onDismissed,
|
||||
this.direction: DismissDirection.horizontal
|
||||
this.direction: DismissDirection.horizontal,
|
||||
this.resizeDuration: const Duration(milliseconds: 300)
|
||||
}) : super(key: key) {
|
||||
assert(key != null);
|
||||
assert(secondaryBackground != null ? background != null : true);
|
||||
@ -90,6 +92,12 @@ class Dismissable extends StatefulWidget {
|
||||
/// The direction in which the widget can be dismissed.
|
||||
final DismissDirection direction;
|
||||
|
||||
/// The amount of time the widget will spend contracting before [onDismissed] is called.
|
||||
///
|
||||
/// If null, the widget will not contract and [onDismissed] will be called
|
||||
/// immediately after the the widget is dismissed.
|
||||
final Duration resizeDuration;
|
||||
|
||||
@override
|
||||
_DismissableState createState() => new _DismissableState();
|
||||
}
|
||||
@ -253,18 +261,23 @@ class _DismissableState extends State<Dismissable> {
|
||||
assert(_moveController != null);
|
||||
assert(_moveController.isCompleted);
|
||||
assert(_resizeController == null);
|
||||
_resizeController = new AnimationController(duration: _kResizeDuration)
|
||||
..addListener(_handleResizeProgressChanged);
|
||||
_resizeController.forward();
|
||||
setState(() {
|
||||
_resizeAnimation = new Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0
|
||||
).animate(new CurvedAnimation(
|
||||
parent: _resizeController,
|
||||
curve: _kResizeTimeCurve
|
||||
));
|
||||
});
|
||||
if (config.resizeDuration == null) {
|
||||
if (config.onDismissed != null)
|
||||
config.onDismissed(_dismissDirection);
|
||||
} else {
|
||||
_resizeController = new AnimationController(duration: config.resizeDuration)
|
||||
..addListener(_handleResizeProgressChanged);
|
||||
_resizeController.forward();
|
||||
setState(() {
|
||||
_resizeAnimation = new Tween<double>(
|
||||
begin: 1.0,
|
||||
end: 0.0
|
||||
).animate(new CurvedAnimation(
|
||||
parent: _resizeController,
|
||||
curve: _kResizeTimeCurve
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _handleResizeProgressChanged() {
|
||||
|
@ -230,4 +230,56 @@ void main() {
|
||||
expect(tester.findText('bar2'), isNull);
|
||||
});
|
||||
});
|
||||
|
||||
test('SnackBar dismiss test', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
int snackBarCount = 0;
|
||||
Key tapTarget = new Key('tap-target');
|
||||
tester.pumpWidget(new MaterialApp(
|
||||
routes: <String, WidgetBuilder>{
|
||||
'/': (BuildContext context) {
|
||||
return new Scaffold(
|
||||
body: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
return new GestureDetector(
|
||||
onTap: () {
|
||||
snackBarCount += 1;
|
||||
Scaffold.of(context).showSnackBar(new SnackBar(
|
||||
content: new Text("bar$snackBarCount"),
|
||||
duration: new Duration(seconds: 2)
|
||||
));
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: new Container(
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
key: tapTarget
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
));
|
||||
expect(tester.findText('bar1'), isNull);
|
||||
expect(tester.findText('bar2'), isNull);
|
||||
tester.tap(tester.findElementByKey(tapTarget)); // queue bar1
|
||||
tester.tap(tester.findElementByKey(tapTarget)); // queue bar2
|
||||
expect(tester.findText('bar1'), isNull);
|
||||
expect(tester.findText('bar2'), isNull);
|
||||
tester.pump(); // schedule animation for bar1
|
||||
expect(tester.findText('bar1'), isNotNull);
|
||||
expect(tester.findText('bar2'), isNull);
|
||||
tester.pump(); // begin animation
|
||||
expect(tester.findText('bar1'), isNotNull);
|
||||
expect(tester.findText('bar2'), isNull);
|
||||
tester.pump(new Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
|
||||
tester.scroll(tester.findText('bar1'), new Offset(0.0, 50.0));
|
||||
tester.pump(); // bar1 dismissed, bar2 begins animating
|
||||
expect(tester.findText('bar1'), isNull);
|
||||
expect(tester.findText('bar2'), isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user