Skip to content

Commit

Permalink
Merge pull request #18 from letsar/feature/slide_to_dismiss
Browse files Browse the repository at this point in the history
Feature/slide to dismiss
  • Loading branch information
letsar committed Aug 2, 2018
2 parents 05c4e48 + 12006e0 commit b0584c1
Show file tree
Hide file tree
Showing 6 changed files with 719 additions and 132 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@ doc/api/
.idea/
.DS_Store
.vscode/

example/android/\.project

example/android/\.settings/org\.eclipse\.buildship\.core\.prefs

example/android/app/\.classpath

example/android/app/\.project

example/android/app/\.settings/org\.eclipse\.buildship\.core\.prefs
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 0.4.0
### Added
* The `SlidableRenderingMode` enum.
* The `SlideActionType` enum.
* The `SlideToDismissDelegate` classes.

### Modified
* Added a renderingMode parameter in the `SlideActionBuilder` signature .

## 0.3.2
### Added
* The `enabled` argument on `Slidable` constructors to enable or disable the slide effect (enabled by default).
Expand Down
80 changes: 76 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# flutter_slidable

A Flutter implementation of slidable list item with directional slide actions.
A Flutter implementation of slidable list item with directional slide actions that can be dismissed.

[![Pub](https://img.shields.io/pub/v/flutter_slidable.svg)](https://pub.dartlang.org/packages/flutter_slidable)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QTT34M25RDNL6)
Expand All @@ -10,8 +10,10 @@ A Flutter implementation of slidable list item with directional slide actions.
## Features

* Accepts left and right widget lists as slide actions.
* Can be dismissed.
* 4 built-in layouts.
* 2 built-in slide action widgets.
* 1 built-in dismiss animation.
* You can easily create you custom layouts and animations.
* You can use a builder to create your slide actions if you want special effects during animation.
* Close when a slide action has been tapped (overridable).
Expand All @@ -25,7 +27,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency:
```yaml
dependencies:
...
flutter_slidable: "^0.3.2"
flutter_slidable: "^0.4.0"
```

In your library add the following import:
Expand Down Expand Up @@ -138,12 +140,82 @@ The slide actions stretch while the item is sliding:
#### How to prevent my slide action to close after it has been tapped?

By default, `SlideAction` and `IconSlideAction` close on tap.
To prevent this, you can pass in `false` to the `closeOnTap` constructor argument.
To prevent this, you can pass in `false` to the `closeOnTap` constructor argument.

#### How to prevent my Slidable to close after my list has scrolled?

By default, a `Slidable` closes when the nearest `Scrollable` widget starts to scroll.
To prevent this, you can pass in `false` to the `closeOnScroll` constructor argument.
To prevent this, you can pass in `false` to the `closeOnScroll` constructor argument.

#### How can I dismiss my Slidable?

In order to make your `Slidable` dismissible, you have to set the `slideToDismissDelegate` argument of the `Slidable` constructor.
You can set any class that inherits `SlideToDismissDelegate`. For now there is only one built-in: `SlideToDismissDrawerDelegate`.

The `actionType` passed to the `onDismissed` callback let you know which action has been dismissed.

When a `Slidable` is dismissible, the `key` argument must not be null.

Example:

``` dart
slideToDismissDelegate: new SlideToDismissDrawerDelegate(
onDismissed: (actionType) {
_showSnackBar(
context,
actionType == SlideActionType.primary
? 'Dismiss Archive'
: 'Dimiss Delete');
setState(() {
items.removeAt(index);
});
},
),
```

#### How can I prevent to dismiss one side but not the other?

If you only want one side to be dismissible, you can set the associated threshold to 1.0 or more.
For example, if you don't want the first primary action to be dismissed, you will set the following thresholds on the `slideToDismissDelegate`:

``` dart
dismissThresholds: <SlideActionType, double>{
SlideActionType.primary: 1.0
},
```

#### How to let the user cancel a dismissal?

You can let the user confirm the dismissal by setting the `onWillDismiss` callback on the `slideToDismissDelegate`.

Example:

```dart
slideToDismissDelegate: new SlideToDismissDrawerDelegate(
onWillDismiss: (actionType) {
return showDialog<bool>(
context: context,
builder: (context) {
return new AlertDialog(
title: new Text('Delete'),
content: new Text('Item will be deleted'),
actions: <Widget>[
new FlatButton(
child: new Text('Cancel'),
onPressed: () => Navigator.of(context).pop(false),
),
new FlatButton(
child: new Text('Ok'),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
);
},
...
),
```

## Changelog

Expand Down
130 changes: 110 additions & 20 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ class MyHomePage extends StatefulWidget {
}

class _MyHomePageState extends State<MyHomePage> {
final List<_HomeItem> items = List.generate(
20,
(i) => new _HomeItem(
i,
'Tile n°$i',
_getSubtitle(i),
_getAvatarColor(i),
),
);

@override
Widget build(BuildContext context) {
return new Scaffold(
Expand All @@ -45,32 +55,35 @@ class _MyHomePageState extends State<MyHomePage> {
itemBuilder: (context, index) {
final Axis slidableDirection =
direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
if (index < 8) {
var item = items[index];
if (item.index < 8) {
return _getSlidableWithLists(context, index, slidableDirection);
} else {
return _getSlidableWithDelegates(context, index, slidableDirection);
}
},
itemCount: 20,
itemCount: items.length,
);
}

Widget _buildVerticalListItem(BuildContext context, int index) {
final _HomeItem item = items[index];
return new Container(
color: Colors.white,
child: new ListTile(
leading: new CircleAvatar(
backgroundColor: _getAvatarColor(index),
child: new Text('$index'),
backgroundColor: item.color,
child: new Text('${item.index}'),
foregroundColor: Colors.white,
),
title: new Text('Tile n°$index'),
subtitle: new Text(_getSubtitle(index)),
title: new Text(item.title),
subtitle: new Text(item.subtitle),
),
);
}

Widget _buildhorizontalListItem(BuildContext context, int index) {
final _HomeItem item = items[index];
return new Container(
color: Colors.white,
width: 160.0,
Expand All @@ -79,15 +92,15 @@ class _MyHomePageState extends State<MyHomePage> {
children: <Widget>[
new Expanded(
child: new CircleAvatar(
backgroundColor: _getAvatarColor(index),
child: new Text('$index'),
backgroundColor: item.color,
child: new Text('${item.index}'),
foregroundColor: Colors.white,
),
),
new Expanded(
child: Center(
child: new Text(
_getSubtitle(index),
item.subtitle,
),
),
),
Expand All @@ -98,9 +111,24 @@ class _MyHomePageState extends State<MyHomePage> {

Widget _getSlidableWithLists(
BuildContext context, int index, Axis direction) {
final _HomeItem item = items[index];
//final int t = index;
return new Slidable(
key: new Key(item.title),
direction: direction,
delegate: _getDelegate(index),
slideToDismissDelegate: new SlideToDismissDrawerDelegate(
onDismissed: (actionType) {
_showSnackBar(
context,
actionType == SlideActionType.primary
? 'Dismiss Archive'
: 'Dimiss Delete');
setState(() {
items.removeAt(index);
});
},
),
delegate: _getDelegate(item.index),
actionExtentRatio: 0.25,
child: direction == Axis.horizontal
? _buildVerticalListItem(context, index)
Expand Down Expand Up @@ -139,47 +167,95 @@ class _MyHomePageState extends State<MyHomePage> {

Widget _getSlidableWithDelegates(
BuildContext context, int index, Axis direction) {
final _HomeItem item = items[index];

return new Slidable.builder(
key: new Key(item.title),
direction: direction,
delegate: _getDelegate(index),
slideToDismissDelegate: new SlideToDismissDrawerDelegate(
onWillDismiss: (item.index != 10)
? null
: (actionType) {
return showDialog<bool>(
context: context,
builder: (context) {
return new AlertDialog(
title: new Text('Delete'),
content: new Text('Item will be deleted'),
actions: <Widget>[
new FlatButton(
child: new Text('Cancel'),
onPressed: () => Navigator.of(context).pop(false),
),
new FlatButton(
child: new Text('Ok'),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
);
},
onDismissed: (actionType) {
_showSnackBar(
context,
actionType == SlideActionType.primary
? 'Dismiss Archive'
: 'Dimiss Delete');
setState(() {
items.removeAt(index);
});
},
),
delegate: _getDelegate(item.index),
actionExtentRatio: 0.25,
child: direction == Axis.horizontal
? _buildVerticalListItem(context, index)
: _buildhorizontalListItem(context, index),
actionDelegate: new SlideActionBuilderDelegate(
actionCount: 2,
builder: (context, index, animation) {
builder: (context, index, animation, renderingMode) {
if (index == 0) {
return new IconSlideAction(
caption: 'Archive',
color: Colors.blue.withOpacity(animation.value),
color: renderingMode == SlidableRenderingMode.slide
? Colors.blue.withOpacity(animation.value)
: (renderingMode == SlidableRenderingMode.dismiss
? Colors.blue
: Colors.green),
icon: Icons.archive,
onTap: () => _showSnackBar(context, 'Archive'),
);
} else {
return new IconSlideAction(
caption: 'Share',
color: Colors.indigo.withOpacity(animation.value),
color: renderingMode == SlidableRenderingMode.slide
? Colors.indigo.withOpacity(animation.value)
: Colors.indigo,
icon: Icons.share,
onTap: () => _showSnackBar(context, 'Share'),
);
}
}),
secondaryActionDelegate: new SlideActionBuilderDelegate(
actionCount: 2,
builder: (context, index, animation) {
builder: (context, index, animation, renderingMode) {
if (index == 0) {
return new IconSlideAction(
caption: 'More',
color: Colors.grey.shade200.withOpacity(animation.value),
color: renderingMode == SlidableRenderingMode.slide
? Colors.grey.shade200.withOpacity(animation.value)
: Colors.grey.shade200,
icon: Icons.more_horiz,
onTap: () => _showSnackBar(context, 'More'),
closeOnTap: false,
);
} else {
return new IconSlideAction(
caption: 'Delete',
color: Colors.red.withOpacity(animation.value),
color: renderingMode == SlidableRenderingMode.slide
? Colors.red.withOpacity(animation.value)
: Colors.red,
icon: Icons.delete,
onTap: () => _showSnackBar(context, 'Delete'),
);
Expand All @@ -188,7 +264,7 @@ class _MyHomePageState extends State<MyHomePage> {
);
}

SlidableDelegate _getDelegate(int index) {
static SlidableDelegate _getDelegate(int index) {
switch (index % 4) {
case 0:
return new SlidableBehindDelegate();
Expand All @@ -203,7 +279,7 @@ class _MyHomePageState extends State<MyHomePage> {
}
}

Color _getAvatarColor(int index) {
static Color _getAvatarColor(int index) {
switch (index % 4) {
case 0:
return Colors.red;
Expand All @@ -218,7 +294,7 @@ class _MyHomePageState extends State<MyHomePage> {
}
}

String _getSubtitle(int index) {
static String _getSubtitle(int index) {
switch (index % 4) {
case 0:
return 'SlidableBehindDelegate';
Expand All @@ -237,3 +313,17 @@ class _MyHomePageState extends State<MyHomePage> {
Scaffold.of(context).showSnackBar(SnackBar(content: new Text(text)));
}
}

class _HomeItem {
const _HomeItem(
this.index,
this.title,
this.subtitle,
this.color,
);

final int index;
final String title;
final String subtitle;
final Color color;
}

0 comments on commit b0584c1

Please sign in to comment.