Kinda Code
Home/Flutter/Flutter: Swipe to remove items from a ListView

Flutter: Swipe to remove items from a ListView

Last updated: February 04, 2023

In this tutorial, we’ll build a small Flutter project that contains a ListView to show some fiction products. When the user swipes an item from the list, they can remove it.

Overview

Which widgets we will use?

  • Dismissible (we’ll focus on this)
  • Container, ListView
  • ListTile, Icon, Card, CircleAvatar

The Dismissible widget

Constructor:

Dismissible({
  required Key key, 
  required Widget child, 
  Widget? background, 
  Widget? secondaryBackground, 
  ConfirmDismissCallback? confirmDismiss, 
  VoidCallback? onResize, 
  DismissUpdateCallback? onUpdate, 
  DismissDirectionCallback? onDismissed, 
  DismissDirection direction = DismissDirection.horizontal, 
  Duration? resizeDuration = const Duration(milliseconds: 300),
  Map<DismissDirection, double> dismissThresholds = const <DismissDirection, double>{}, 
  Duration movementDuration = const Duration(milliseconds: 200), 
  double crossAxisEndOffset = 0.0, 
  DragStartBehavior dragStartBehavior = DragStartBehavior.start,
  HitTestBehavior behavior = HitTestBehavior.opaque
})

Important properties:

  • key: You can set it by using UniqueKey() or ValueKey()
  • direction: The direction in which the widget can be dismissed
  • child: The widget below this widget in the tree
  • background: A widget that is stacked behind the child
  • onDismissed: Call a function when the widget has been dismissed.

You can see all other properties in the official docs.

Example Preview

Here’s how our app works at the end:

Getting Started

If you just want to see the final code, then skip this section and jump to the next one.

1. Create a new Flutter project:

flutter create my_app 

2. Remove all the default code in main.dart and add the following “skeleton” code:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Hide the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Kindacode.com',
      theme: ThemeData.light(),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Dummy Product Data Here
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Kindacode.com'),
        ),
        body: Container() // The ListView will be here later
    );
  }
}

3. Generate dummy data by using the List.generate() method. Add this snippet below the comment “// Dummy Product Data Here” in the “skeleton” code:

// Dummy Product Data Here
  List myProducts = List.generate(100, (index) {
    return {"id": index, "title": "Product #$index", "price": index};
  });

4. Now, we deal with the most important part of this tutorial: the ListView and its dismissible items. Replace the Container widget with ListView.builder:

ListView.builder(
          itemCount: myProducts.length,
          itemBuilder: (BuildContext ctx, index) {
            // Display the list item
            return Dismissible(
              key: UniqueKey(),

              // only allows the user swipe from right to left
              direction: DismissDirection.endToStart,

              // Remove this product from the list
              // In production enviroment, you may want to send some request to delete it on server side
              onDismissed: (_) {
                setState(() {
                  myProducts.removeAt(index);
                });
              },

              // This will show up when the user performs dismissal action
              // It is a red background and a trash icon
              background: Container(
                color: Colors.red,
                margin: const EdgeInsets.symmetric(horizontal: 15),
                alignment: Alignment.centerRight,
                child: const Icon(
                  Icons.delete,
                  color: Colors.white,
                ),
              ),

              // Display item's title, price...
              child: Card(
                margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
                child: ListTile(
                  leading: CircleAvatar(
                    child: Text(myProducts[index]["id"].toString()),
                  ),
                  title: Text(myProducts[index]["title"]),
                  subtitle: Text("\$${myProducts[index]["price"].toString()}"),
                  trailing: const Icon(Icons.arrow_back),
                ),
              ),
            );
          },
)

5. We’re done with main.dart. Launch an iOS simulator or an Android emulator and run our project:

flutter run

Check your work. Make sure you didn’t make any typos 🙂

The Final Code

The complete source code in main.dart with explanations:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Hide the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Kindacode.com',
      theme: ThemeData.light(),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Dummy Product Data Here
  final List myProducts = List.generate(100, (index) {
    return {"id": index, "title": "Product #$index", "price": index + 1};
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Kindacode.com'),
        ),
        body: ListView.builder(
          itemCount: myProducts.length,
          itemBuilder: (BuildContext ctx, index) {
            // Display the list item
            return Dismissible(
              key: UniqueKey(),

              // only allows the user swipe from right to left
              direction: DismissDirection.endToStart,

              // Remove this product from the list
              // In production enviroment, you may want to send some request to delete it on server side
              onDismissed: (_) {
                setState(() {
                  myProducts.removeAt(index);
                });
              },

              // This will show up when the user performs dismissal action
              // It is a red background and a trash icon
              background: Container(
                color: Colors.red,
                margin: const EdgeInsets.symmetric(horizontal: 15),
                alignment: Alignment.centerRight,
                child: const Icon(
                  Icons.delete,
                  color: Colors.white,
                ),
              ),

              // Display item's title, price...
              child: Card(
                margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
                child: ListTile(
                  leading: CircleAvatar(
                    child: Text(myProducts[index]["id"].toString()),
                  ),
                  title: Text(myProducts[index]["title"]),
                  subtitle: Text("\$${myProducts[index]["price"].toString()}"),
                  trailing: const Icon(Icons.arrow_back),
                ),
              ),
            );
          },
        ));
  }
}

Afterword

We’ve built a sample app that makes use of the Dismissible widget to remove items from a ListView with the swipe gesture. If you’d like to explore more awesome widgets and other interesting stuff in Flutter, take a look at the following articles:

You can also check out our Flutter category page or Dart category page for the latest tutorials and examples.