¿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í")),
],
),
);
},