flutter_slidable 0.4.1

  • README.md
  • CHANGELOG.md
  • Example
  • Installing
  • Versions
  • 95

flutter_slidable

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

Pub Donate

Overview

Features

  • Accepts primary (left/top) and secondary (right/bottom) 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).
  • Close when the nearest Scrollable starts to scroll (overridable).
  • Option to disable the slide effect easily.

Getting started

In the pubspec.yaml of your flutter project, add the following dependency:

dependencies:
  ...
  flutter_slidable: "^0.4.1"

In your library add the following import:

import 'package:flutter_slidable/flutter_slidable.dart';

For help getting started with Flutter, view the online documentation.

Constructors

You can create a Slidable in two different ways:

  • By calling the Slidable constructor and passing a list of slide actions.
  • By calling the Slidable.builder constructor and passing slide action builders, if you want special effects during the animation.

A Slidable needs multiple things:

  • Slide actions (see below for details). They can be any widget. For convenience, this package has 2 built-in side action widgets.
  • A delegate. This is what controls the layout and the animation of the slide menu.
  • An extent ratio between a slide action extent and the item extent.
  • A child.

The actions contains the slide actions that appear when the child has been dragged down or to the right. The secondaryActions contains the slide actions that appear when the child has been dragged up or to the left.

A direction parameter lets you choose if you want actions to appear when you slide horizontally (default) or vertically.

new Slidable(
  delegate: new SlidableDrawerDelegate(),
  actionExtentRatio: 0.25,
  child: new Container(
    color: Colors.white,
    child: new ListTile(
      leading: new CircleAvatar(
        backgroundColor: Colors.indigoAccent,
        child: new Text('$3'),
        foregroundColor: Colors.white,
      ),
      title: new Text('Tile n°$3'),
      subtitle: new Text('SlidableDrawerDelegate'),
    ),
  ),
  actions: <Widget>[
    new IconSlideAction(
      caption: 'Archive',
      color: Colors.blue,
      icon: Icons.archive,
      onTap: () => _showSnackBar('Archive'),
    ),
    new IconSlideAction(
      caption: 'Share',
      color: Colors.indigo,
      icon: Icons.share,
      onTap: () => _showSnackBar('Share'),
    ),
  ],
  secondaryActions: <Widget>[
    new IconSlideAction(
      caption: 'More',
      color: Colors.black45,
      icon: Icons.more_horiz,
      onTap: () => _showSnackBar('More'),
    ),
    new IconSlideAction(
      caption: 'Delete',
      color: Colors.red,
      icon: Icons.delete,
      onTap: () => _showSnackBar('Delete'),
    ),
  ],
);

Built-in slide actions

This package comes with 2 kinds of slide actions:

  • SlideAction, which is the most flexible. You can choose a background color, or any decoration, and it takes any widget as a child.
  • IconSlideAction, which requires an icon. It can have a background color and a caption below the icon.

Built-in delegates

This package comes with 4 kinds of delegates:

SlidableBehindDelegate

The slide actions stay behind the item while it's sliding:

Overview

SlidableScrollDelegate

The slide actions follow the item while it's sliding:

Overview

SlidableDrawerDelegate

The slide actions animate like drawers while the item is sliding:

Overview

SlidableStrechDelegate

The slide actions stretch while the item is sliding:

Overview

FAQ

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.

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.

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:

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:

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:

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),
                  ),
                ],
              );
            },
          );
        },
        ...
        ),

How to let keep only one Slidable open?

You have to set the controller argument of the Slidable constructors to a SlidableController instance:

final SlidableController slidableController = new SlidableController();
...
new Slidable(
      key: new Key(item.title),
      controller: slidableController,
      ...
      );

Changelog

Please see the Changelog page to know what's recently changed.

Contributions

Feel free to contribute to this project.

If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue.
If you fixed a bug or implemented a new feature, please send a pull request.

0.4.1

Added

  • The SlidableController class.
  • The controller argument on Slidable constructors to enable keeping only one Slidable open.

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).

0.3.1

Fixed

0.3.0

Added

  • The closeOnTap argument on slide actions to close when a action has been tapped.
  • The closeOnScroll argument on Slidable to close when the nearest Scrollable starts to scroll.
  • The static Slidable.of function.

Changed

  • The dragExtent field in SlidableDelegateContext has been changed to dragSign.

0.2.0

Added

  • Slidable.builder constructor.
  • Vertical sliding.

Changed

  • The slide actions are now hosted in a SlideActionDelegate instead of List<Widget> inside the Slidable widget.
  • The leftActions have been renamed to actions.
  • The rightActions have been renamed to secondaryActions.

0.1.0

  • Initial Open Source release.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Slidable Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Slidable Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final SlidableController slidableController = new SlidableController();
  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(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new OrientationBuilder(
          builder: (context, orientation) => _buildList(
              context,
              orientation == Orientation.portrait
                  ? Axis.vertical
                  : Axis.horizontal),
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Widget _buildList(BuildContext context, Axis direction) {
    return new ListView.builder(
      scrollDirection: direction,
      itemBuilder: (context, index) {
        final Axis slidableDirection =
            direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
        var item = items[index];
        if (item.index < 8) {
          return _getSlidableWithLists(context, index, slidableDirection);
        } else {
          return _getSlidableWithDelegates(context, index, slidableDirection);
        }
      },
      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: item.color,
          child: new Text('${item.index}'),
          foregroundColor: Colors.white,
        ),
        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,
      child: new Column(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          new Expanded(
            child: new CircleAvatar(
              backgroundColor: item.color,
              child: new Text('${item.index}'),
              foregroundColor: Colors.white,
            ),
          ),
          new Expanded(
            child: Center(
              child: new Text(
                item.subtitle,
              ),
            ),
          ),
        ],
      ),
    );
  }

  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),
      controller: slidableController,
      direction: direction,
      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)
          : _buildhorizontalListItem(context, index),
      actions: <Widget>[
        new IconSlideAction(
          caption: 'Archive',
          color: Colors.blue,
          icon: Icons.archive,
          onTap: () => _showSnackBar(context, 'Archive'),
        ),
        new IconSlideAction(
          caption: 'Share',
          color: Colors.indigo,
          icon: Icons.share,
          onTap: () => _showSnackBar(context, 'Share'),
        ),
      ],
      secondaryActions: <Widget>[
        new IconSlideAction(
          caption: 'More',
          color: Colors.grey.shade200,
          icon: Icons.more_horiz,
          onTap: () => _showSnackBar(context, 'More'),
          closeOnTap: false,
        ),
        new IconSlideAction(
          caption: 'Delete',
          color: Colors.red,
          icon: Icons.delete,
          onTap: () => _showSnackBar(context, 'Delete'),
        ),
      ],
    );
  }

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

    return new Slidable.builder(
      key: new Key(item.title),
      controller: slidableController,
      direction: direction,
      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, renderingMode) {
            if (index == 0) {
              return new IconSlideAction(
                caption: 'Archive',
                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: 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, renderingMode) {
            if (index == 0) {
              return new IconSlideAction(
                caption: 'More',
                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: renderingMode == SlidableRenderingMode.slide
                    ? Colors.red.withOpacity(animation.value)
                    : Colors.red,
                icon: Icons.delete,
                onTap: () => _showSnackBar(context, 'Delete'),
              );
            }
          }),
    );
  }

  static SlidableDelegate _getDelegate(int index) {
    switch (index % 4) {
      case 0:
        return new SlidableBehindDelegate();
      case 1:
        return new SlidableStrechDelegate();
      case 2:
        return new SlidableScrollDelegate();
      case 3:
        return new SlidableDrawerDelegate();
      default:
        return null;
    }
  }

  static Color _getAvatarColor(int index) {
    switch (index % 4) {
      case 0:
        return Colors.red;
      case 1:
        return Colors.green;
      case 2:
        return Colors.blue;
      case 3:
        return Colors.indigoAccent;
      default:
        return null;
    }
  }

  static String _getSubtitle(int index) {
    switch (index % 4) {
      case 0:
        return 'SlidableBehindDelegate';
      case 1:
        return 'SlidableStrechDelegate';
      case 2:
        return 'SlidableScrollDelegate';
      case 3:
        return 'SlidableDrawerDelegate';
      default:
        return null;
    }
  }

  void _showSnackBar(BuildContext context, String text) {
    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;
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  flutter_slidable: ^0.4.1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter packages get

Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:flutter_slidable/flutter_slidable.dart';
  
Version Uploaded Documentation Archive
0.4.1 Aug 8, 2018 Go to the documentation of flutter_slidable 0.4.1 Download flutter_slidable 0.4.1 archive
0.4.0 Aug 2, 2018 Go to the documentation of flutter_slidable 0.4.0 Download flutter_slidable 0.4.0 archive
0.3.2 Jul 25, 2018 Go to the documentation of flutter_slidable 0.3.2 Download flutter_slidable 0.3.2 archive
0.3.1 Jul 24, 2018 Go to the documentation of flutter_slidable 0.3.1 Download flutter_slidable 0.3.1 archive
0.3.0 Jul 23, 2018 Go to the documentation of flutter_slidable 0.3.0 Download flutter_slidable 0.3.0 archive
0.2.0 Jul 22, 2018 Go to the documentation of flutter_slidable 0.2.0 Download flutter_slidable 0.2.0 archive
0.1.0 Jul 21, 2018 Go to the documentation of flutter_slidable 0.1.0 Download flutter_slidable 0.1.0 archive
Popularity:
Describes how popular the package is relative to other packages. [more]
91
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
95
Learn more about scoring.

We analyzed this package on Aug 10, 2018, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.0.0
  • pana: 0.11.8
  • Flutter: 0.5.7

Platforms

Detected platforms: Flutter

References Flutter, and has no conflicting libraries.

Suggestions

Package is pre-v1 release.

While there is nothing inherently wrong with versions of 0.*.*, it usually means that the author is still experimenting with the general direction of the API.

Format lib/src/widgets/slide_action.dart.

Run flutter format to format lib/src/widgets/slide_action.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=1.19.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.6 1.14.11
meta 1.1.5 1.1.6
sky_engine 0.0.99
typed_data 1.1.5 1.1.6
vector_math 2.0.6 2.0.8
Dev dependencies
flutter_test