Tocar, arrastrar e ingresar texto
Cómo probar widgets para la interacción del usuario.
Muchos widgets no solo muestran información, sino que también responden
a la interacción del usuario. Esto incluye botones que se pueden tocar
y un TextField
para ingresar texto.
Para probar estas interacciones, necesitas una forma de simularlas
en el entorno de pruebas. Para este propósito, utiliza la
biblioteca WidgetTester.
El WidgetTester proporciona métodos para ingresar texto,
tocar y arrastrar.
En muchos casos, las interacciones del usuario actualizan el estado de la aplicación. En el entorno
de pruebas, Flutter no reconstruye automáticamente los widgets cuando cambia el estado.
Para asegurar que el árbol de widgets se reconstruya después de simular una
interacción del usuario, llama a los métodos pump()
o pumpAndSettle()
proporcionados por el WidgetTester.
Esta receta utiliza los siguientes pasos:
- Crea un widget para probar.
- Ingresar texto en el campo de texto.
- Asegurar que al tocar un botón se añada el pendiente (todo).
- Asegurar que la acción de deslizar para descartar (swipe-to-dismiss) elimine el pendiente (todo).
1. Crea un widget para probar
#Para este ejemplo, crea una aplicación de pendientes (todo) básica que pruebe tres características:
- Ingresar texto en un
TextField. - Tocar un
FloatingActionButtonpara añadir el texto a una lista de pendientes (todos). - Deslizar para descartar para eliminar el elemento de la lista.
Para mantener el enfoque en las pruebas, esta receta no proporcionará una guía detallada sobre cómo construir la aplicación de pendientes. Para obtener más información sobre cómo se construye esta aplicación, consulta las recetas correspondientes:
- Crear y dar estilo a un campo de texto
- Manejar toques
- Crear una lista básica
- Implementar deslizar para descartar
class TodoList extends StatefulWidget {
const TodoList({super.key});
@override
State<TodoList> createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
static const _appTitle = 'Todo List';
final todos = <String>[];
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _appTitle,
home: Scaffold(
appBar: AppBar(title: const Text(_appTitle)),
body: Column(
children: [
TextField(controller: controller),
Expanded(
child: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return Dismissible(
key: Key('$todo$index'),
onDismissed: (direction) => todos.removeAt(index),
background: Container(color: Colors.red),
child: ListTile(title: Text(todo)),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
todos.add(controller.text);
controller.clear();
});
},
child: const Icon(Icons.add),
),
),
);
}
}
2. Ingresar texto en el campo de texto
#Ahora que tienes una aplicación de pendientes, comienza a escribir la prueba.
Empieza por ingresar texto en el TextField.
Completa esta tarea:
- Construir el widget en el entorno de pruebas.
- Usando el método
enterText()deWidgetTester.
testWidgets('Add and remove a todo', (tester) async {
// Build the widget
await tester.pumpWidget(const TodoList());
// Enter 'hi' into the TextField.
await tester.enterText(find.byType(TextField), 'hi');
});
3. Asegurar que al tocar un botón se añada el pendiente (todo)
#Después de ingresar texto en el TextField, asegúrate de que al tocar
el FloatingActionButton se añada el elemento a la lista.
Esto implica tres pasos:
- Toca el botón de añadir usando el método
tap(). - Reconstruye el widget después de que el estado haya cambiado usando el
método
pump(). - Asegurar que el elemento de la lista aparezca en pantalla.
testWidgets('Add and remove a todo', (tester) async {
// Enter text code...
// Tap the add button.
await tester.tap(find.byType(FloatingActionButton));
// Rebuild the widget after the state has changed.
await tester.pump();
// Expect to find the item on screen.
expect(find.text('hi'), findsOneWidget);
});
4. Asegurar que la acción de deslizar para descartar (swipe-to-dismiss) elimine el pendiente (todo)
#Finalmente, asegúrate de que al realizar una acción de deslizar para descartar en el elemento pendiente lo elimine de la lista. Esto implica tres pasos:
- Usa el método
drag()para realizar una acción de deslizar para descartar. - Usa el método
pumpAndSettle()para reconstruir continuamente el árbol de widgets hasta que la animación de descarte se complete. - Asegurar que el elemento ya no aparezca en pantalla.
testWidgets('Add and remove a todo', (tester) async {
// Enter text and add the item...
// Swipe the item to dismiss it.
await tester.drag(find.byType(Dismissible), const Offset(500, 0));
// Build the widget until the dismiss animation ends.
await tester.pumpAndSettle();
// Ensure that the item is no longer on screen.
expect(find.text('hi'), findsNothing);
});
Ejemplo completo
#import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Add and remove a todo', (tester) async {
// Build the widget.
await tester.pumpWidget(const TodoList());
// Enter 'hi' into the TextField.
await tester.enterText(find.byType(TextField), 'hi');
// Tap the add button.
await tester.tap(find.byType(FloatingActionButton));
// Rebuild the widget with the new item.
await tester.pump();
// Expect to find the item on screen.
expect(find.text('hi'), findsOneWidget);
// Swipe the item to dismiss it.
await tester.drag(find.byType(Dismissible), const Offset(500, 0));
// Build the widget until the dismiss animation ends.
await tester.pumpAndSettle();
// Ensure that the item is no longer on screen.
expect(find.text('hi'), findsNothing);
});
}
class TodoList extends StatefulWidget {
const TodoList({super.key});
@override
State<TodoList> createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
static const _appTitle = 'Todo List';
final todos = <String>[];
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _appTitle,
home: Scaffold(
appBar: AppBar(title: const Text(_appTitle)),
body: Column(
children: [
TextField(controller: controller),
Expanded(
child: ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
final todo = todos[index];
return Dismissible(
key: Key('$todo$index'),
onDismissed: (direction) => todos.removeAt(index),
background: Container(color: Colors.red),
child: ListTile(title: Text(todo)),
);
},
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
todos.add(controller.text);
controller.clear();
});
},
child: const Icon(Icons.add),
),
),
);
}
}
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-05. Ver código fuente oreportar un problema.