Flutter Swipeable Listview | Slide To Dismiss | Slidable View

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

flutter swipeable flutter swipeable

 

Show Buttons
Hide Buttons
Read previous post:
Flutter Dark / Light Theme tutorial for beginners

  Flutter Theme : Flutter dark light modes make the user to select his / her way of theme in...

Close