Skip to content

Commit 86c17f5

Browse files
committed
add a bool to close on scroll
1 parent 004e8eb commit 86c17f5

File tree

3 files changed

+81
-23
lines changed

3 files changed

+81
-23
lines changed

example/lib/main.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class _MyHomePageState extends State<MyHomePage> {
125125
color: Colors.grey.shade200,
126126
icon: Icons.more_horiz,
127127
onTap: () => _showSnackBar(context, 'More'),
128-
closeAfterTap: false,
128+
closeOnTap: false,
129129
),
130130
new IconSlideAction(
131131
caption: 'Delete',
@@ -174,7 +174,7 @@ class _MyHomePageState extends State<MyHomePage> {
174174
color: Colors.grey.shade200.withOpacity(animation.value),
175175
icon: Icons.more_horiz,
176176
onTap: () => _showSnackBar(context, 'More'),
177-
closeAfterTap: false,
177+
closeOnTap: false,
178178
);
179179
} else {
180180
return new IconSlideAction(

lib/src/widgets/slidable.dart

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class SlidableDelegateContext {
104104
const SlidableDelegateContext(
105105
this.slidable,
106106
this.showActions,
107-
this.dragExtent,
107+
this.dragSign,
108108
this.controller,
109109
);
110110

@@ -122,7 +122,7 @@ class SlidableDelegateContext {
122122
/// Whether the actions have to be shown.
123123
final bool showActions;
124124

125-
final double dragExtent;
125+
final double dragSign;
126126

127127
/// The animation controller which value depends on `dragExtent`.
128128
final AnimationController controller;
@@ -193,10 +193,10 @@ abstract class SlidableStackDelegate extends SlidableDelegate {
193193
Widget buildActions(BuildContext context, SlidableDelegateContext ctx) {
194194
final animation = new Tween(
195195
begin: Offset.zero,
196-
end: ctx.createOffset(ctx.totalActionsExtent * ctx.dragExtent.sign),
196+
end: ctx.createOffset(ctx.totalActionsExtent * ctx.dragSign),
197197
).animate(ctx.controller);
198198

199-
if (ctx.controller.value != .0 && ctx.dragExtent != .0) {
199+
if (ctx.controller.value != .0) {
200200
return new Container(
201201
child: new Stack(
202202
children: <Widget>[
@@ -231,7 +231,7 @@ class SlidableStrechDelegate extends SlidableStackDelegate {
231231
Widget buildStackActions(BuildContext context, SlidableDelegateContext ctx) {
232232
final animation = new Tween(
233233
begin: Offset.zero,
234-
end: ctx.createOffset(ctx.totalActionsExtent * ctx.dragExtent.sign),
234+
end: ctx.createOffset(ctx.totalActionsExtent * ctx.dragSign),
235235
).animate(ctx.controller);
236236

237237
return new Positioned.fill(
@@ -375,12 +375,12 @@ class SlidableDrawerDelegate extends SlidableStackDelegate {
375375
///
376376
/// By sliding in one of these direction, slide actions will appear.
377377
class Slidable extends StatefulWidget {
378-
/// Creates a widget that can be dismissed.
378+
/// Creates a widget that can be slid.
379379
///
380380
/// The [actions] contains the slide actions that appears when the child has been dragged down or to the right.
381381
/// The [secondaryActions] contains the slide actions that appears when the child has been dragged up or to the left.
382382
///
383-
/// The [delegate] argument must not be null. The [actionExtentRatio]
383+
/// The [delegate] and [closeOnScroll] arguments must not be null. The [actionExtentRatio]
384384
/// and [showAllActionsThreshold] arguments must be greater or equal than 0 and less or equal than 1.
385385
Slidable({
386386
Key key,
@@ -392,6 +392,7 @@ class Slidable extends StatefulWidget {
392392
double actionExtentRatio = _kActionsExtentRatio,
393393
Duration movementDuration = const Duration(milliseconds: 200),
394394
Axis direction = Axis.horizontal,
395+
bool closeOnScroll = true,
395396
}) : this.builder(
396397
key: key,
397398
child: child,
@@ -402,8 +403,16 @@ class Slidable extends StatefulWidget {
402403
actionExtentRatio: actionExtentRatio,
403404
movementDuration: movementDuration,
404405
direction: direction,
406+
closeOnScroll: closeOnScroll,
405407
);
406408

409+
/// Creates a widget that can be slid.
410+
///
411+
/// The [actionDelegate] is a delegate that builds the slide actions that appears when the child has been dragged down or to the right.
412+
/// The [secondaryActionDelegate] is a delegate that builds the slide actions that appears when the child has been dragged up or to the left.
413+
///
414+
/// The [delegate] and [closeOnScroll] arguments must not be null. The [actionExtentRatio]
415+
/// and [showAllActionsThreshold] arguments must be greater or equal than 0 and less or equal than 1.
407416
Slidable.builder({
408417
Key key,
409418
@required this.child,
@@ -414,11 +423,13 @@ class Slidable extends StatefulWidget {
414423
this.actionExtentRatio = _kActionsExtentRatio,
415424
this.movementDuration = const Duration(milliseconds: 200),
416425
this.direction = Axis.horizontal,
426+
this.closeOnScroll = true,
417427
}) : assert(delegate != null),
418428
assert(direction != null),
419429
assert(showAllActionsThreshold != null && showAllActionsThreshold >= .0 && showAllActionsThreshold <= 1.0,
420430
'showAllActionsThreshold must be between 0.0 and 1.0'),
421431
assert(actionExtentRatio != null && actionExtentRatio >= .0 && actionExtentRatio <= 1.0, 'actionExtentRatio must be between 0.0 and 1.0'),
432+
assert(closeOnScroll != null),
422433
super(key: key);
423434

424435
/// The widget below this widget in the tree.
@@ -451,6 +462,11 @@ class Slidable extends StatefulWidget {
451462
/// Defines the duration for card to go to final position or to come back to original position if threshold not reached.
452463
final Duration movementDuration;
453464

465+
/// Specifies to close this slidable after the closest [Scrollable]'s position changed.
466+
///
467+
/// Defaults to true.
468+
final bool closeOnScroll;
469+
454470
/// The state from the closest instance of this class that encloses the given context.
455471
static SlidableState of(BuildContext context) {
456472
return context.ancestorStateOfType(const TypeMatcher<SlidableState>());
@@ -471,6 +487,8 @@ class SlidableState extends State<Slidable> with TickerProviderStateMixin, Autom
471487

472488
double _dragExtent = 0.0;
473489

490+
ScrollPosition _scrollPosition;
491+
474492
bool get _showActions {
475493
return _dragExtent > 0;
476494
}
@@ -489,9 +507,40 @@ class SlidableState extends State<Slidable> with TickerProviderStateMixin, Autom
489507
return (_directionIsXAxis ? context.size.width : context.size.height) * widget.actionExtentRatio * (actionDelegate?.actionCount ?? 0);
490508
}
491509

510+
@override
511+
void didChangeDependencies() {
512+
super.didChangeDependencies();
513+
_removeScrollingNotifierListener();
514+
_addScrollingNotifierListener();
515+
}
516+
517+
@override
518+
void didUpdateWidget(Slidable oldWidget) {
519+
super.didUpdateWidget(oldWidget);
520+
521+
if (widget.closeOnScroll != oldWidget.closeOnScroll) {
522+
_removeScrollingNotifierListener();
523+
_addScrollingNotifierListener();
524+
}
525+
}
526+
527+
void _addScrollingNotifierListener() {
528+
if (widget.closeOnScroll) {
529+
_scrollPosition = Scrollable.of(context)?.position;
530+
if (_scrollPosition != null) _scrollPosition.isScrollingNotifier.addListener(_isScrollingListener);
531+
}
532+
}
533+
534+
void _removeScrollingNotifierListener() {
535+
if (_scrollPosition != null) {
536+
_scrollPosition.isScrollingNotifier.removeListener(_isScrollingListener);
537+
}
538+
}
539+
492540
@override
493541
void dispose() {
494542
_controller.dispose();
543+
_removeScrollingNotifierListener();
495544
super.dispose();
496545
}
497546

@@ -503,6 +552,15 @@ class SlidableState extends State<Slidable> with TickerProviderStateMixin, Autom
503552
_controller.fling(velocity: -1.0);
504553
}
505554

555+
void _isScrollingListener() {
556+
if (!widget.closeOnScroll || _scrollPosition == null) return;
557+
558+
// When a scroll starts close this.
559+
if (_scrollPosition.isScrollingNotifier.value) {
560+
close();
561+
}
562+
}
563+
506564
void _handleDragStart(DragStartDetails details) {
507565
_dragExtent = _controller.value * _overallDragAxisExtent * _dragExtent.sign;
508566
if (_controller.isAnimating) {
@@ -555,7 +613,7 @@ class SlidableState extends State<Slidable> with TickerProviderStateMixin, Autom
555613
new SlidableDelegateContext(
556614
widget,
557615
_showActions,
558-
_dragExtent,
616+
_dragExtent.sign,
559617
_controller,
560618
),
561619
);

lib/src/widgets/slide_action.dart

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ import 'package:flutter/material.dart';
22
import 'package:flutter/widgets.dart';
33
import 'package:flutter_slidable/src/widgets/slidable.dart';
44

5-
const bool _kCloseAfterTap = true;
5+
const bool _kCloseOnTap = true;
66

77
/// Abstract class for slide actions that can close after [onTap] occurred.
88
abstract class ClosableSlideAction extends StatelessWidget {
9-
/// Creates a slide that closes after tap occurred if [closeAfterTap] is [true].
9+
/// Creates a slide that closes when a tap has occurred if [closeOnTap] is [true].
1010
///
11-
/// The [closeAfterTap] argument must not be null.
11+
/// The [closeOnTap] argument must not be null.
1212
const ClosableSlideAction({
1313
Key key,
1414
this.onTap,
15-
this.closeAfterTap = _kCloseAfterTap,
16-
}) : assert(closeAfterTap != null),
15+
this.closeOnTap = _kCloseOnTap,
16+
}) : assert(closeOnTap != null),
1717
super(key: key);
1818

1919
/// A tap has occurred.
@@ -22,7 +22,7 @@ abstract class ClosableSlideAction extends StatelessWidget {
2222
/// Whether close this after tap occurred.
2323
///
2424
/// Defaults to true.
25-
final bool closeAfterTap;
25+
final bool closeOnTap;
2626

2727
/// Calls [onTap] if not null and closes the closest [Slidable] that encloses the given context.
2828
void _handleCloseAfterTap(BuildContext context) {
@@ -35,7 +35,7 @@ abstract class ClosableSlideAction extends StatelessWidget {
3535

3636
Widget build(BuildContext context) {
3737
return GestureDetector(
38-
onTap: !closeAfterTap ? onTap : () => _handleCloseAfterTap(context),
38+
onTap: !closeOnTap ? onTap : () => _handleCloseAfterTap(context),
3939
child: buildAction(context),
4040
);
4141
}
@@ -54,14 +54,14 @@ class SlideAction extends ClosableSlideAction {
5454
/// `decoration`, you can pass the color as the `color` argument to the
5555
/// `BoxDecoration`.
5656
///
57-
/// The [closeAfterTap] argument must not be null.
57+
/// The [closeOnTap] argument must not be null.
5858
SlideAction({
5959
Key key,
6060
@required this.child,
6161
VoidCallback onTap,
6262
Color color,
6363
Decoration decoration,
64-
bool closeAfterTap = _kCloseAfterTap,
64+
bool closeOnTap = _kCloseOnTap,
6565
}) : assert(child != null),
6666
assert(decoration == null || decoration.debugAssertIsValid()),
6767
assert(
@@ -72,7 +72,7 @@ class SlideAction extends ClosableSlideAction {
7272
super(
7373
key: key,
7474
onTap: onTap,
75-
closeAfterTap: closeAfterTap,
75+
closeOnTap: closeOnTap,
7676
);
7777

7878
final Decoration decoration;
@@ -95,19 +95,19 @@ class IconSlideAction extends ClosableSlideAction {
9595
/// Creates a slide action with an icon, a [caption] if set and a
9696
/// background color.
9797
///
98-
/// The [closeAfterTap] argument must not be null.
98+
/// The [closeOnTap] argument must not be null.
9999
const IconSlideAction({
100100
Key key,
101101
@required this.icon,
102102
this.caption,
103103
Color color,
104104
VoidCallback onTap,
105-
bool closeAfterTap = _kCloseAfterTap,
105+
bool closeOnTap = _kCloseOnTap,
106106
}) : color = color ?? Colors.white,
107107
super(
108108
key: key,
109109
onTap: onTap,
110-
closeAfterTap: closeAfterTap,
110+
closeOnTap: closeOnTap,
111111
);
112112

113113
final IconData icon;

0 commit comments

Comments
 (0)