Flutter Swipeable :
Flutter swipeable listview will make it easier for you to add the options like delete, more, and so on… accessible easily on every row so that user can choose them accordingly.
You might have experienced this swipe view in answering calls, and also in applications like gmail where you can delete the mail or archive them.
We can add more than one option on a single slide as shown in this tutorial on once slide you can see different options opened up for you with different colors and specifications.
Instead of long click menu this flutter swipeable view is much easier and flexible way to target the row and make accurate transactions also in terms of design it makes it easier because it doesn’t need any extra space and can fit in any available design aspects.
Flutter Swipeable Video Tutorial :
Go through the below tutorial for more detailed implementation details.
pubspec.yaml :
Add the dependency flutter_slidable and update the version accordingly to the latest available.
dependencies: flutter: sdk: flutter flutter_slidable: 0.5.7
item.dart :
Now we need to handle the list data to be populated so define a model class to access the data where we have the a parameters index, title, subtitle and color.
import 'dart:ui'; class Item { const Item( this.index, this.title, this.subtitle, this.color, ); final int index; final String title; final String subtitle; final Color color; }
main.dart :
Initialize with void main() and considering a default class MyApp()
void main() => runApp(MyApp());
Return a material app with theme and declare a class in home
MaterialApp( title: 'Flutter Slidable ListView', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Slidable ListView'), );
Declare a variable slidable controller
SlidableController slidableController;
List of items to be populated
final List<Item> items = List.generate(10, (i) => Item(i, 'Item $i', 'Description $i', Colors.blue, ), );
Initialize the slidable Controller
slidableController = SlidableController( onSlideAnimationChanged: handleSlideAnimationChanged, onSlideIsOpenChanged: handleSlideIsOpenChanged, );
in initstate
@protected void initState() { slidableController = SlidableController( onSlideAnimationChanged: handleSlideAnimationChanged, onSlideIsOpenChanged: handleSlideIsOpenChanged, ); super.initState(); }
Add a orientation builder to know the orientation changes and switch accordingly
body: Center( child: OrientationBuilder( builder: (context, orientation) => _buildList( context, orientation == Orientation.portrait ? Axis.vertical : Axis.horizontal), ), ),
List builder to handle the list data
ListView.builder( scrollDirection: direction, itemBuilder: (context, index) { final Axis slidableDirection = direction == Axis.horizontal ? Axis.vertical : Axis.horizontal; var item = items[index]; return _getSlidableWithLists(context, index, slidableDirection); }, itemCount: items.length, );
Now we need to define the slidable components on list
Widget _getSlidableWithLists( BuildContext context, int index, Axis direction) { final Item item = items[index]; //final int t = index; return Slidable( key: Key(item.title), controller: slidableController, direction: direction, dismissal: SlidableDismissal( child: SlidableDrawerDismissal(), onDismissed: (actionType) { _showSnackBar( context, actionType == SlideActionType.primary ? 'Dismiss Archive' : 'Dimiss Delete'); setState(() { items.removeAt(index); }); }, ), actionPane: _getActionPane(item.index), actionExtentRatio: 0.25, child: direction == Axis.horizontal ? VerticalListItem(items[index]) : HorizontalListItem(items[index]), actions: <Widget>[ IconSlideAction( caption: 'Share', color: Colors.indigo, icon: Icons.share, onTap: () => _showSnackBar(context, 'Share'), ), IconSlideAction( caption: 'Help', color: Colors.purple, icon: Icons.help, onTap: () => _showSnackBar(context, 'Help'), ), ], 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'), ), ], ); }
Show a snack bar on action button click
void _showSnackBar(BuildContext context, String text) { Scaffold.of(context).showSnackBar(SnackBar(content: Text(text))); }
When the list is in horizontal mode then
class HorizontalListItem extends StatelessWidget { HorizontalListItem(this.item); final Item item; @override Widget build(BuildContext context) { return Container( color: Colors.white, width: 160.0, child: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Expanded( child: CircleAvatar( backgroundColor: item.color, child: Text('${item.index}'), foregroundColor: Colors.white, ), ), Expanded( child: Center( child: Text( item.subtitle, ), ), ), ], ), ); } }
When the list is in vertical mode then
class VerticalListItem extends StatelessWidget { VerticalListItem(this.item); final Item item; @override Widget build(BuildContext context) { return GestureDetector( onTap: () => Slidable.of(context)?.renderingMode == SlidableRenderingMode.none ? Slidable.of(context)?.open() : Slidable.of(context)?.close(), child: Container( color: Colors.white, child: ListTile( leading: CircleAvatar( backgroundColor: item.color, child: Text('${item.index}'), foregroundColor: Colors.white, ), title: Text(item.title), subtitle: Text(item.subtitle), ), ), ); } }
FullCode :
Providing the complete code for flutter swipeable list view
import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'item.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Slidable ListView', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(title: 'Flutter Slidable ListView'), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { SlidableController slidableController; final List<Item> items = List.generate(10, (i) => Item(i, 'Item $i', 'Description $i', Colors.blue, ), ); @protected void initState() { slidableController = SlidableController( onSlideAnimationChanged: handleSlideAnimationChanged, onSlideIsOpenChanged: handleSlideIsOpenChanged, ); super.initState(); } Animation<double> _rotationAnimation; Color _fabColor = Colors.blue; void handleSlideAnimationChanged(Animation<double> slideAnimation) { setState(() { _rotationAnimation = slideAnimation; }); } void handleSlideIsOpenChanged(bool isOpen) { setState(() { _fabColor = isOpen ? Colors.green : Colors.blue; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: OrientationBuilder( builder: (context, orientation) => _buildList( context, orientation == Orientation.portrait ? Axis.vertical : Axis.horizontal), ), ), ); } 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]; return _getSlidableWithLists(context, index, slidableDirection); }, itemCount: items.length, ); } Widget _getSlidableWithLists( BuildContext context, int index, Axis direction) { final Item item = items[index]; //final int t = index; return Slidable( key: Key(item.title), controller: slidableController, direction: direction, dismissal: SlidableDismissal( child: SlidableDrawerDismissal(), onDismissed: (actionType) { _showSnackBar( context, actionType == SlideActionType.primary ? 'Dismiss Archive' : 'Dimiss Delete'); setState(() { items.removeAt(index); }); }, ), actionPane: _getActionPane(item.index), actionExtentRatio: 0.25, child: direction == Axis.horizontal ? VerticalListItem(items[index]) : HorizontalListItem(items[index]), actions: <Widget>[ IconSlideAction( caption: 'Share', color: Colors.indigo, icon: Icons.share, onTap: () => _showSnackBar(context, 'Share'), ), IconSlideAction( caption: 'Help', color: Colors.purple, icon: Icons.help, onTap: () => _showSnackBar(context, 'Help'), ), ], 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'), ), ], ); } static Widget _getActionPane(int index) { switch (index % 4) { case 0: return SlidableBehindActionPane(); case 1: return SlidableStrechActionPane(); case 2: return SlidableScrollActionPane(); case 3: return SlidableDrawerActionPane(); default: return null; } } void _showSnackBar(BuildContext context, String text) { Scaffold.of(context).showSnackBar(SnackBar(content: Text(text))); } } class HorizontalListItem extends StatelessWidget { HorizontalListItem(this.item); final Item item; @override Widget build(BuildContext context) { return Container( color: Colors.white, width: 160.0, child: Column( mainAxisSize: MainAxisSize.max, children: <Widget>[ Expanded( child: CircleAvatar( backgroundColor: item.color, child: Text('${item.index}'), foregroundColor: Colors.white, ), ), Expanded( child: Center( child: Text( item.subtitle, ), ), ), ], ), ); } } class VerticalListItem extends StatelessWidget { VerticalListItem(this.item); final Item item; @override Widget build(BuildContext context) { return GestureDetector( onTap: () => Slidable.of(context)?.renderingMode == SlidableRenderingMode.none ? Slidable.of(context)?.open() : Slidable.of(context)?.close(), child: Container( color: Colors.white, child: ListTile( leading: CircleAvatar( backgroundColor: item.color, child: Text('${item.index}'), foregroundColor: Colors.white, ), title: Text(item.title), subtitle: Text(item.subtitle), ), ), ); } }
Output :
This screen below depicts the implementation of flutter swipeable view on list