Slidable Tile In Flutter

The flutter_slidable plugin adds a component rich slider widget to your project. Sliders like this are frequently observed in scrollable records. The Gmail application is a remarkable model, in which sliding list things offer a critical efficiency upgrade.

This plugin is packed with highlights and prepared for use, yet also profoundly adjustable if necessary. This plugin was working with iOSAndroidWeb, and Linux.

Inthis article, we will explore the Slidable In Flutter. We will also implement a swipe demo to either right or left on the tile using the flutter_slidable package in your flutter applications.

Slidable

 in an application can be utilized to play out a wide scope of tasks with simply a straightforward swipe to one or the other right or left on the tile. It makes the UI very easy to understand and recoveries a ton of time doing trifling tasks that can be riotous and repetitive to tasks whenever done in alternate manners. In this article, we will investigate the way toward planning a Slidable for your application.

We will build a basic application with tiles that swiped left to right archives and share option on the tile and when the swiped right to left erases and more option on the tile.

Features

  • Acknowledges essential (left/top) and secondary (right/base) widget records as slide actions.
  • > Can be excused.
  • Four implicit action panes
  • Two built-in slide action widgets
  • Built-in dismiss animation
  • Easily create custom layouts and animations
  • Closes when a slide action has been tapped (overridable)
  • Closes when the nearest Scrollable starts to scroll (overridable)
  • Option to disable the slide effect easily.

Implementation:

Step 1: Add the dependencies

Add dependencies to pubspec — yaml file.

dependencies:

flutter_slidable: ^0.5.7

Step 2: Import

import 'package:flutter_slidable/flutter_slidable.dart';

Step 3: Run flutter packages get in the root directory of your app.

Step 4: Enable AndriodX

Add this to your gradle.properties file:

org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true

How to implement code in dart file :

You need to implement it in your code respectively:

Create a new dart file called slidable_screen.dart inside the lib folder.

Go through a StatefulWidget to set the landing page for the application that would later hold the tiles that can be swiped one or the other way to perform undertakings allowed.

SlidableController _slidableController;

Go through the SlidableController to set the slides for the application. It tends to be finished by the utilization of either Slidable Constructor or Slidable. Builder Constructor.

Create a new dart file called home_modal.dart inside the lib folder.

import 'package:flutter/material.dart';

class HomeModal {
const HomeModal(
this.index,
this.titles,
this.subtitle,
this.color,
);

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

Then, create a list on slidable_screen.dartfile, and the name called items are equal to List. generate().

final List<HomeModal> items = List.generate(
11,
(i) => HomeModal(
i,
_position(i),
_subtitle(i),
_avatarColor(i),
),
);

In this HomeModal, we will add an index, title, subtitle, and avatar color.

Now, we will define the body part of the app

OrientationBuilder(
builder: (context, orientation) => _buildList(
context,
orientation == Orientation.portrait
? Axis.vertical
: Axis.horizontal),
),

We will use OrientationBuilder for portrait mode and landscape mode. In this builder function, there was a widget _buildList(). We will explore this widget below.

Widget _buildList(BuildContext context, Axis direction) {
return ListView.builder(
scrollDirection: direction,
itemBuilder: (context, index) {
final Axis slidableDirection =
direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
var item = items[index];
if (item.index < 5) {
return _slidableWithLists(context, index, slidableDirection);
} else {
return _slidableWithDelegates(context, index, slidableDirection);
}
},
itemCount: items.length,
);
}

In this widget, we will use ListView.builder(). In this builder, we will add direction, itemCount, and itemBuilder. If the item.index is less than 5 then return _slidableWithLists() widget and else the item.index is greater than 5 then return _slidableWithDelegates() widget.

Let’s deep explore _slidableWithLists() widget

Widget _slidableWithLists(
BuildContext context, int index, Axis direction) {
final HomeModal item = items[index];
return Slidable(
key: Key(item.titles),
controller: _slidableController,
direction: direction,
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
onDismissed: (actionType) {
_showSnackBar(
context,
actionType == SlideActionType.primary
? 'Dismiss Archive'
: 'Dimiss Delete');
setState(() {
items.removeAt(index);
});
},
),
actionPane: _actionPane(item.index),
actionExtentRatio: 0.20,
child: direction == Axis.horizontal
? VerticalList(items[index])
: HorizontalList(items[index]),
actions: <Widget>[
IconSlideAction(
caption: 'Archive',
color: Colors.blue,
icon: Icons.archive,
onTap: () => _showSnackBar(context, 'Archive'),
),
IconSlideAction(
caption: 'Share',
color: Colors.indigo,
icon: Icons.share,
onTap: () => _showSnackBar(context, 'Share'),
),
],
secondaryActions: <Widget>[
IconSlideAction(
caption: 'More',
color: Colors.grey.shade200,
icon: Icons.more_horiz,
onTap: () => _showSnackBar(context, 'More'),
closeOnTap: false,
),
IconSlideAction(
caption: 'Delete',
color: Colors.red,
icon: Icons.delete,
onTap: () => _showSnackBar(context, 'Delete'),
),
],
);
}

In this widget, we will return Slidable(). In this method, we will add SlidableDismissal() function; when we swipe the left to right on the tile, then the tile was deleted/archived, and they will show a message on the snackbar. We will add an actions widget like IconSlideAction archive and share. Also, add secondary actions widget like IconSlideAction more and delete. There was an actionPane option in this method, and we will make a widget called _actionPane().

static Widget _actionPane(int index) {
switch (index % 4) {
case 0:
return SlidableScrollActionPane();
case 1:
return SlidableDrawerActionPane();
case 2:
return SlidableStrechActionPane();
case 3:
return SlidableBehindActionPane();

default:
return null;
}
}

All are property of Slidable; the slide actions stay behind the item while it’s sliding, called SlidableBehindActionPane. The slide actions follow the item while it’s sliding called SlidableScrollActionPane. The slide actions animate like drawers while the item is sliding called SlidableDrawerActionPane. The slide actions stretch while the item is sliding, called SlidableStrechActionPane.

Let’s deep explore _slidableWithDelegates() widget

Widget _slidableWithDelegates(
BuildContext context, int index, Axis direction) {
final HomeModal item = items[index];
return Slidable.builder(
key: Key(item.titles),
controller: _slidableController,
direction: direction,
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
closeOnCanceled: true,
onWillDismiss: (item.index != 10)
? null
: (actionType) {
return showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Colors.redAccent,
title: Text('Delete',style: TextStyle(color: Colors.white),),
content: Text('Item will be deleted',style: TextStyle(color: Colors.white),),
actions: <Widget>[
FlatButton(
child: Text('Cancel',style: TextStyle(color: Colors.white),),
onPressed: () => Navigator.of(context).pop(false),
),
FlatButton(
child: Text('Ok',style: TextStyle(color: Colors.white),),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
);
},
onDismissed: (actionType) {
_showSnackBar(
context,
actionType == SlideActionType.primary
? 'Dismiss Archive'
: 'Dimiss Delete');
setState(() {
items.removeAt(index);
});
},
),
actionPane: _actionPane(item.index),
actionExtentRatio: 0.20,
child: direction == Axis.horizontal
? VerticalList(items[index])
: HorizontalList(items[index]),
actionDelegate: SlideActionBuilderDelegate(
actionCount: 2,
builder: (context, index, animation, renderingMode) {
if (index == 0) {
return IconSlideAction(
caption: 'Archive',
color: renderingMode == SlidableRenderingMode.slide
? Colors.blue.withOpacity(animation.value)
: (renderingMode == SlidableRenderingMode.dismiss
? Colors.blue
: Colors.green),
icon: Icons.archive,
onTap: () async {
var state = Slidable.of(context);
var dismiss = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Colors.redAccent,
title: Text('Delete',style: TextStyle(color: Colors.white),),
content: Text('Item will be deleted',style: TextStyle(color: Colors.white),),
actions: <Widget>[
FlatButton(
child: Text('Cancel',style: TextStyle(color: Colors.white),),
onPressed: () => Navigator.of(context).pop(false),
),
FlatButton(
child: Text('Ok',style: TextStyle(color: Colors.white),),
onPressed: () => Navigator.of(context).pop(true),
),
],
);
},
);

if (dismiss) {
state.dismiss();
}
},
);
} else {
return IconSlideAction(
caption: 'Share',
color: renderingMode == SlidableRenderingMode.slide
? Colors.indigo.withOpacity(animation.value)
: Colors.indigo,
icon: Icons.share,
onTap: () => _showSnackBar(context, 'Share'),
);
}
}),
secondaryActionDelegate: SlideActionBuilderDelegate(
actionCount: 2,
builder: (context, index, animation, renderingMode) {
if (index == 0) {
return 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 IconSlideAction(
caption: 'Delete',
color: renderingMode == SlidableRenderingMode.slide
? Colors.red.withOpacity(animation.value)
: Colors.red,
icon: Icons.delete,
onTap: () => _showSnackBar(context, 'Delete'),
);
}
}),
);
}

In this widget, we will return Slidable.builder(). In this method, when we swipe the left to right on the tile, the alert dialog box opens for delete/archive purposes, and they will show a message on the snackbar. We will use action delegate and secondary action delegate for IconSlideAction(). There was an actionPane option, and the same widget called _actionPane().

floatingActionButton: FloatingActionButton(
backgroundColor: _fabColor,
onPressed: null,
child: _rotationAnimation == null
? Icon(Icons.add,color: Colors.white,)
: RotationTransition(
turns: _rotationAnimation,
child: Icon(Icons.add,color: Colors.white,),
),
),

Then, we will add FloatingActionButton(). In this button, we will add rotationAnimation; when we swipe left to right and right to left, then the floating button icon and color will be changed.

When we run the application, we ought to get the screen’s output like the underneath screen capture.

Full Code

import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_slidable_demo/home_modal.dart';
import 'package:flutter_slidable_demo/horizontal_list.dart';
import 'package:flutter_slidable_demo/vertical_list.dart';

class SlidableScreen extends StatefulWidget {

  @override
  _SlidableScreenState createState() => _SlidableScreenState();
}

class _SlidableScreenState extends State<SlidableScreen> {
  SlidableController _slidableController;
  final List<HomeModal> items = List.generate(
    11,
        (i) => HomeModal(
        i,
      _position(i),
      _subtitle(i),
      _avatarColor(i),
    ),
  );

  static Color _avatarColor(int index) {
    switch (index % 4) {
      case 0:
        return Colors.cyan[200];
      case 1:
        return Colors.teal;
      case 2:
        return Colors.red;
      case 3:
        return Colors.orange;
      default:
        return null;
    }
  }

  static String _subtitle(int index) {
    switch (index % 4) {
      case 0:
        return 'SlidableScrollActionPane';
      case 1:
        return 'SlidableDrawerActionPane';
      case 2:
        return 'SlidableStrechActionPane';
      case 3:
        return 'SlidableBehindActionPane';
      default:
        return null;
    }
  }

  static String _position(int index) {
    switch (index % 4) {
      case 0:
        return 'Software Developer';
      case 1:
        return 'Software Testing';
      case 2:
        return 'Software Designer';
      case 3:
        return 'Project Manager';
      default:
        return null;
    }
  }

  @override
  void initState() {
    _slidableController = SlidableController(
      onSlideAnimationChanged: slideAnimationChanged,
      onSlideIsOpenChanged: slideIsOpenChanged,
    );
    super.initState();
  }

  Animation<double> _rotationAnimation;
  Color _fabColor = Colors.redAccent;


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white24,
      appBar: AppBar(
        title: Text('Flutter Slidable Demo'),
        centerTitle: true,
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: OrientationBuilder(
          builder: (context, orientation) => _buildList(
              context,
              orientation == Orientation.portrait
                  ? Axis.vertical
                  : Axis.horizontal),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        backgroundColor: _fabColor,
        onPressed: null,
        child: _rotationAnimation == null
            ? Icon(Icons.add,color: Colors.white,)
            : RotationTransition(
          turns: _rotationAnimation,
          child: Icon(Icons.add,color: Colors.white,),
        ),
      ),
    );
  }

  Widget _buildList(BuildContext context, Axis direction) {
    return ListView.builder(
      scrollDirection: direction,
      itemBuilder: (context, index) {
        final Axis slidableDirection =
        direction == Axis.horizontal ? Axis.vertical : Axis.horizontal;
        var item = items[index];
        if (item.index < 5) {
          return _slidableWithLists(context, index, slidableDirection);
        } else {
          return _slidableWithDelegates(context, index, slidableDirection);
        }
      },
      itemCount: items.length,
    );
  }

  Widget _slidableWithLists(
      BuildContext context, int index, Axis direction) {
    final HomeModal item = items[index];
    return Slidable(
      key: Key(item.titles),
      controller: _slidableController,
      direction: direction,
      dismissal: SlidableDismissal(
        child: SlidableDrawerDismissal(),
        onDismissed: (actionType) {
          _showSnackBar(
              context,
              actionType == SlideActionType.primary
                  ? 'Dismiss Archive'
                  : 'Dimiss Delete');
          setState(() {
            items.removeAt(index);
          });
        },
      ),
      actionPane: _actionPane(item.index),
      actionExtentRatio: 0.20,
      child: direction == Axis.horizontal
          ? VerticalList(items[index])
          : HorizontalList(items[index]),
      actions: <Widget>[
        IconSlideAction(
          caption: 'Archive',
          color: Colors.blue,
          icon: Icons.archive,
          onTap: () => _showSnackBar(context, 'Archive'),
        ),
        IconSlideAction(
          caption: 'Share',
          color: Colors.indigo,
          icon: Icons.share,
          onTap: () => _showSnackBar(context, 'Share'),
        ),
      ],
      secondaryActions: <Widget>[
        IconSlideAction(
          caption: 'More',
          color: Colors.grey.shade200,
          icon: Icons.more_horiz,
          onTap: () => _showSnackBar(context, 'More'),
          closeOnTap: false,
        ),
        IconSlideAction(
          caption: 'Delete',
          color: Colors.red,
          icon: Icons.delete,
          onTap: () => _showSnackBar(context, 'Delete'),
        ),
      ],
    );
  }

  Widget _slidableWithDelegates(
      BuildContext context, int index, Axis direction) {
    final HomeModal item = items[index];
    return Slidable.builder(
      key: Key(item.titles),
      controller: _slidableController,
      direction: direction,
      dismissal: SlidableDismissal(
        child: SlidableDrawerDismissal(),
        closeOnCanceled: true,
        onWillDismiss: (item.index != 10)
            ? null
            : (actionType) {
          return showDialog<bool>(
            context: context,
            builder: (context) {
              return AlertDialog(
                backgroundColor: Colors.redAccent,
                title: Text('Delete',style: TextStyle(color: Colors.white),),
                content: Text('Item will be deleted',style: TextStyle(color: Colors.white),),
                actions: <Widget>[
                  FlatButton(
                    child: Text('Cancel',style: TextStyle(color: Colors.white),),
                    onPressed: () => Navigator.of(context).pop(false),
                  ),
                  FlatButton(
                    child: Text('Ok',style: TextStyle(color: Colors.white),),
                    onPressed: () => Navigator.of(context).pop(true),
                  ),
                ],
              );
            },
          );
        },
        onDismissed: (actionType) {
          _showSnackBar(
              context,
              actionType == SlideActionType.primary
                  ? 'Dismiss Archive'
                  : 'Dimiss Delete');
          setState(() {
            items.removeAt(index);
          });
        },
      ),
      actionPane: _actionPane(item.index),
      actionExtentRatio: 0.20,
      child: direction == Axis.horizontal
          ? VerticalList(items[index])
          : HorizontalList(items[index]),
      actionDelegate: SlideActionBuilderDelegate(
          actionCount: 2,
          builder: (context, index, animation, renderingMode) {
            if (index == 0) {
              return IconSlideAction(
                caption: 'Archive',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.blue.withOpacity(animation.value)
                    : (renderingMode == SlidableRenderingMode.dismiss
                    ? Colors.blue
                    : Colors.green),
                icon: Icons.archive,
                onTap: () async {
                  var state = Slidable.of(context);
                  var dismiss = await showDialog<bool>(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        backgroundColor: Colors.redAccent,
                        title: Text('Delete',style: TextStyle(color: Colors.white),),
                        content: Text('Item will be deleted',style: TextStyle(color: Colors.white),),
                        actions: <Widget>[
                          FlatButton(
                            child: Text('Cancel',style: TextStyle(color: Colors.white),),
                            onPressed: () => Navigator.of(context).pop(false),
                          ),
                          FlatButton(
                            child: Text('Ok',style: TextStyle(color: Colors.white),),
                            onPressed: () => Navigator.of(context).pop(true),
                          ),
                        ],
                      );
                    },
                  );

                  if (dismiss) {
                    state.dismiss();
                  }
                },
              );
            } else {
              return IconSlideAction(
                caption: 'Share',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.indigo.withOpacity(animation.value)
                    : Colors.indigo,
                icon: Icons.share,
                onTap: () => _showSnackBar(context, 'Share'),
              );
            }
          }),
      secondaryActionDelegate: SlideActionBuilderDelegate(
          actionCount: 2,
          builder: (context, index, animation, renderingMode) {
            if (index == 0) {
              return 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 IconSlideAction(
                caption: 'Delete',
                color: renderingMode == SlidableRenderingMode.slide
                    ? Colors.red.withOpacity(animation.value)
                    : Colors.red,
                icon: Icons.delete,
                onTap: () => _showSnackBar(context, 'Delete'),
              );
            }
          }),
    );
  }

  static Widget _actionPane(int index) {
    switch (index % 4) {
      case 0:
        return SlidableScrollActionPane();
      case 1:
        return SlidableDrawerActionPane();
      case 2:
        return SlidableStrechActionPane();
      case 3:
        return SlidableBehindActionPane();

      default:
        return null;
    }
  }



  void _showSnackBar(BuildContext context, String text) {
    Scaffold.of(context).showSnackBar(SnackBar(content: Text(text)));
  }

  void slideAnimationChanged(Animation<double> slideAnimation) {
    setState(() {
      _rotationAnimation = slideAnimation;
    });
  }

  void slideIsOpenChanged(bool isOpen) {
    setState(() {
      _fabColor = isOpen ? Colors.orange : Colors.redAccent;
    });
  }
}