Widget Dismissible en Flutter - swipe y más
Índice de contenido
Cuando trabajas con listas en Flutter llega un momento en el que necesitas permitir acciones rápidas: borrar, archivar, editar… Y ahí es donde entra en juego Dismissible, un widget que, sinceramente, uso muchísimo. Los típicos CRUDs que siempre tenemos que implementar, en apps móviles en vez de estar colocando botones, lo cual es complicado ya que no es lo mismo diseñar para una app móvil a una app web, ya que, en las apps móviles son pantallas pequeñas y usualmente se emplean muchos tipos de eventos como el swipe y este widget viene de perlas para esto.
Acompáñame paso a paso: qué es, cómo funciona, qué errores evitar, y un código final que puedes copiar y adaptar hoy mismo.
Recuerda que anteriormente nos quedamos en el Widget de tipo Slider en Flutter.
Qué es el widget Dismissible y para qué se usa
El widget Dismissible permite envolver un elemento (generalmente un item de lista) y habilitarle gestos de swipe para eliminarlo o ejecutar acciones. Es decir, permite que el usuario “deslice para borrar” o “deslice para editar”.
Un widget que podemos arrastrar en la dirección especificada, generalmente de derecha a izquierda o de izquierda a derecha, también se conoce como el efecto swipe.
Este widget envuelve otro widget, que puede ser cualquier tipo de widget y buala, ya tiene estos comportamientos que mencionamos antes.
En una de mis implementaciones —la misma que muestro en mi vídeo de YouTube— lo uso para listas sencillas donde cada ítem desaparece al deslizarlo, y Flutter se encarga de animarlo y removerlo sin complicaciones.
✅ Implementación básica del widget Dismissible
Aquí te muestro la estructura mínima:
Dismissible(
key: UniqueKey(),
background: Container(color: Colors.green),
secondaryBackground: Container(color: Colors.red),
onDismissed: (direction) {
// acción al eliminar
},
child: ListTile(
title: Text("Item"),
),
);Lo más importante al principio es recordar que el Dismissible necesita un key único, porque de lo contrario no funcionará.
Ejemplo simple paso a paso
Un uso básico con eliminación directa quedaría así:
Dismissible(
key: Key(items[index]),
onDismissed: (direction) {
setState(() {
items.removeAt(index);
});
},
background: Container(color: Colors.red),
child: ListTile(title: Text(items[index])),
);Uso correcto de keys y errores comunes
Uno de los errores más comunes (yo también lo sufrí) es:
“A dismissed Dismissible widget is still part of the tree.”
Esto pasa cuando eliminamos visualmente el widget, pero no lo quitamos de la lista interna, provocando que Flutter detecte un Dismissible huérfano.
Solución: remover el item dentro de onDismissed.
ListView y Dismissible
Este ejemplo muestra el uso típico de un ListView y el Dismissible:
ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
onDismissed: (DismissDirection direction) {
setState(() {
data.removeAt(index);
});
},
secondaryBackground: Container(
child: Center(
child: Text(
'Borrar',
style: TextStyle(color: Colors.white),
),
),
color: Colors.red,
),
background: Container(),
child: _card(item: data[index]),
key: UniqueKey(),
direction: DismissDirection.endToStart,
);
},
),Como elemento padre tenemos nuestro StatefulWidget y un Scaffold.
En esta pantalla, tenemos una lista de elementos que hemos hecho con el método ItemBuilder. Y en esto, hemos utilizado el widget Flutter Dismissible. Recibe un método en onDismissed junto con DismissDirection:
ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
onDismissed: (DismissDirection direction) {
setState(() {
data.removeAt(index);
});
},
secondaryBackground: Container(
child: Center(
child: Text(
'Borrar',
style: TextStyle(color: Colors.white),
),
),
color: Colors.red,
),
background: Container(),
child: _card(item: data[index]),
key: UniqueKey(),
direction: DismissDirection.endToStart,
);
},
),Hemos establecido el color del fondo. El item que es de color rojo visible en el tiempo de eliminación.
Dismissible(
onDismissed: (DismissDirection direction) {
setState(() {
data.removeAt(index);
});
},
secondaryBackground: Container(
child: Center(
child: Text(
'Borrar',
style: TextStyle(color: Colors.white),
),
),
color: Colors.red,
),
background: Container(),
child: _card(item: data[index]),
key: UniqueKey(),
direction: DismissDirection.endToStart,
)Cuando ejecutamos la aplicación, deberíamos obtener el resultado de la pantalla como la captura de pantalla debajo.

Puntos a tener en cuenta
La data puede ser cualquier cosa, en este caso, es un array de String, pero puede ser de un objeto, enteros o lo que necesite tu aplicación; el código completo de la app:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final data = [
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5",
"Item 6",
"Item 7"
];
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flip',
home: Scaffold(
appBar: AppBar(
title: Text("Flip"),
),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Dismissible(
onDismissed: (DismissDirection direction) {
setState(() {
data.removeAt(index);
});
},
secondaryBackground: Container(
child: Center(
child: Text(
'Borrar',
style: TextStyle(color: Colors.white),
),
),
color: Colors.red,
),
background: Container(),
child: _card(item: data[index]),
key: UniqueKey(),
direction: DismissDirection.endToStart,
);
},
),
));
}
Widget _card({String item}) {
return Card(
child: ListTile(
title: Text(item),
),
);
}
}
✅ Swipe avanzado: acciones distintas a izquierda y derecha
Si quieres ofrecer más funciones (borrar, editar, archivar, etc.), lo ideal es mostrar un fondo diferente en cada lado y ejecutar acciones diferentes.
Cuando necesitaba ofrecer “editar” y “borrar” desde el mismo ítem, usé este enfoque:
- Swipe para editar vs swipe para borrar
- background: slideRightBackground(), // Editar
- secondaryBackground: slideLeftBackground(), // Borrar
Confirmación con confirmDismiss
Si la acción es destructiva (ej. borrar), lo ideal es pedir confirmación.
confirmDismiss: (direction) async {
if (direction == DismissDirection.endToStart) { // Borrar
return await showDialog(
context: context,
builder: (_) => AlertDialog(
content: Text("¿Seguro que deseas borrar este elemento?"),
actions: [
TextButton(child: Text("Cancelar"), onPressed: () => Navigator.pop(context, false)),
TextButton(child: Text("Borrar"), onPressed: () => Navigator.pop(context, true)),
],
),
);
}
// swipe opuesto → editar, navegar, etc.
return false;
},✅ Conclusión
El widget Dismissible es una herramienta clave cuando buscas mejorar la experiencia de usuario en listas. Permite acciones rápidas, intuitivas y visuales. Con un buen uso de background, secondaryBackground, confirmDismiss y claves únicas, puedes crear interfaces modernas como Gmail o cualquier app productiva.
Además, al integrar swipe en ambas direcciones, la interacción se vuelve mucho más rica. Personalmente, es uno de esos widgets que hago aparecer en mis proyectos más de lo que admito.
❓ FAQs rápidas
- ¿Es obligatorio usar UniqueKey()?
- No, pero sí necesitas un key único por ítem para evitar problemas.
- ¿Puedo evitar que un ítem se borre realmente?
- Sí: usa confirmDismiss y devuelve false.
- ¿Puedo usarlo fuera de un ListView?
- Sí, pero es donde más sentido tiene.
El siguiente widget que vamos a revisar sería como crear un menú lateral o Drawer en Flutter.
Acepto recibir anuncios de interes sobre este Blog.
Vamos a conocer el Widget Disimissble, un widget arrastrable generalmente de derecha a izquierda o viceversa que nos permite eliminar elementos generalmente de un listado.