Realizar una acción con un deslizamiento con el widget Dismissible


Avatar de Pedro Cortez

Elimina fácilmente elementos de una lista con el widget Dismissible. En esta guía, te explico rápidamente cómo configurarlo y personalizarlo según tus necesidades.


widget dismissible

¿Para qué sirve el widget Dismissible?

El widget Dismissible permite agregar una interacción de deslizamiento (swipe) en un elemento de la interfaz de usuario, generalmente para eliminarlo o realizar una acción específica. Puedes utilizarlo, por ejemplo, en una lista para permitir a los usuarios eliminar un elemento con un simple gesto.

Algunos casos de uso concretos incluyen:

  • Eliminar un elemento de una lista (ejemplo: una tarea en una aplicación de gestión de tareas).
  • Archivar un mensaje en una bandeja de entrada.
  • Mover un elemento a otra categoría (ejemplo: deslizar un correo electrónico a «Leído» o «No leído»).
  • Activar una acción rápida (ejemplo: marcar un elemento como favorito).

¿Cómo implementar el widget Dismissible?

Voy a mostrarte dos formas de implementarlo: una lista codificada manualmente o una lista basada en un documento de Firebase.

ListView con elementos estáticos (hardcoded)

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DismissibleListScreen(),
    );
  }
}

class DismissibleListScreen extends StatefulWidget {
  @override
  _DismissibleListScreenState createState() => _DismissibleListScreenState();
}

class _DismissibleListScreenState extends State<DismissibleListScreen> {
  List<String> items = ["Item 1", "Item 2", "Item 3", "Item 4"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Dismissible List")),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          final item = items[index];
          return Dismissible(
            key: Key(item),
            onDismissed: (direction) {
              setState(() {
                items.removeAt(index);
              });
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text("$item eliminado")),
              );
            },
            background: Container(color: Colors.red),
            child: ListTile(title: Text(item)),
          );
        },
      ),
    );
  }
}

Aquí, cada elemento de la lista está envuelto en un Dismissible, lo que permite eliminarlo con un deslizamiento.

ListView con datos provenientes de Firebase

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

class FirebaseDismissibleList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Dismissible con Firebase")),
      body: StreamBuilder(
        stream: FirebaseFirestore.instance.collection('tasks').snapshots(),
        builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
          if (!snapshot.hasData) return Center(child: CircularProgressIndicator());

          return ListView(
            children: snapshot.data!.docs.map((doc) {
              return Dismissible(
                key: Key(doc.id),
                onDismissed: (direction) {
                  // Elimina el documento de Firebase
                  FirebaseFirestore.instance.collection('tasks').doc(doc.id).delete();
                },
                background: Container(color: Colors.red),
                child: ListTile(title: Text(doc['title'])),
              );
            }).toList(),
          );
        },
      ),
    );
  }
}

De la misma manera, cada elemento de la lista está encapsulado en un Dismissible, permitiendo su eliminación mediante un deslizamiento. Al mismo tiempo, el elemento también se elimina de Firebase.

Personalización del widget Dismissible

Aquí hay algunas formas de personalizar el comportamiento del Dismissible según tus necesidades.

Modificar la duración de la animación

Puedes controlar cuánto tiempo tarda el elemento en desaparecer con la propiedad resizeDuration:

resizeDuration: Duration(milliseconds: 300),

Realizar diferentes acciones según la dirección del swipe

Puedes ejecutar una acción diferente dependiendo de la dirección del deslizamiento:

onDismissed: (direction) {
  if (direction == DismissDirection.startToEnd) {
    print("Archivar");
  } else {
    print("Eliminar");
  }
},

Agregar una confirmación antes de eliminar

Para mayor seguridad, puedes mostrar un mensaje de confirmación antes de eliminar el elemento usando confirmDismiss:

confirmDismiss: (direction) async {
  return await showDialog(
    context: context,
    builder: (context) => AlertDialog(
      title: Text("Confirmar"),
      content: Text("¿Seguro que quieres eliminar este elemento?"),
      actions: [
        TextButton(onPressed: () => Navigator.of(context).pop(false), child: Text("No")),
        TextButton(onPressed: () => Navigator.of(context).pop(true), child: Text("Sí")),
      ],
    ),
  );
},
Avatar de Pedro Cortez