Verifica context.mounted después de cada await
Tu app de Flutter se cae después de una llamada de red lenta. Una línea lo arregla. El patrón siempre es el mismo: un handler de botón hace await de una petición, el usuario navega a otra pantalla mientras está en vuelo, y cuando la respuesta por fin llega tu código toca un BuildContext cuyo widget ya no existe. Con una conexión rápida nunca lo ves; con un round trip celular de 3 segundos es un reporte de crash.
Por qué muere el contexto
El BuildContext de cada widget solo es válido mientras ese widget está montado en el árbol. Un await es una brecha asíncrona: entre la línea anterior y la posterior puede pasar cualquier cosa — el usuario hace pop de la ruta, el padre se reconstruye sin este hijo, la app pasa a segundo plano. Si el widget fue descartado durante la brecha, llamar Navigator.of(context), ScaffoldMessenger.of(context) o setState después golpea un elemento difunto. La documentación del framework es directa: acceder a propiedades de un BuildContext o llamar sus métodos "solo es válido mientras mounted es true".
La guarda de una línea
Desde Flutter 3.7, el propio BuildContext expone una propiedad mounted, así que el arreglo es una sola verificación entre el await y el primer uso del contexto:
Future<void> _onSave(BuildContext context) async {
await repository.save(item); // async gap — anything can happen here
if (!context.mounted) return; // the guard
Navigator.of(context).pop(); // now safe
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text('Saved')));
}
Dentro de una subclase de State, usa el mounted propio del estado — la misma idea, sin parámetro de contexto:
final data = await api.fetch();
if (!mounted) return;
setState(() => _data = data);
La guarda va después de cada await que preceda un uso del contexto — con dos awaits seguidos, la primera verificación ya está obsoleta cuando el segundo termina.
Reglas prácticas
| Situación | Guarda |
|---|---|
Función normal que recibe un BuildContext | if (!context.mounted) return; después de cada await |
Dentro de State<T> (tiene su propio contexto) | if (!mounted) return; después de cada await |
| Varios awaits antes de usar el contexto | Verifica después del último await, no solo del primero |
Contexto capturado antes del await (p. ej. final nav = Navigator.of(context) primero) | Capturar Navigator/ScaffoldMessenger antes de la brecha evita el lookup, pero igual verifica mounted antes de tocar la UI |
| No hay uso del contexto después del await | No hace falta guarda |
Deja que el linter lo atrape por ti
No dependas de la disciplina. La regla del linter de Dart use_build_context_synchronously marca exactamente este bug: un BuildContext referenciado a través de una brecha asíncrona sin verificación de mounted. Es una regla estable y forma parte del set recomendado flutter_lints, que los proyectos nuevos de Flutter (2.3.0+) incluyen por defecto vía analysis_options.yaml:
include: package:flutter_lints/flutter.yaml
Si tu CI corre flutter analyze, esta clase de crash se convierte en un fallo de build en lugar de un incidente en producción.
Notas para usuarios avanzados
- El código generado por IA tropieza con esto constantemente. Los handlers asíncronos de UI son justo donde los asistentes de código producen código plausible pero inseguro. Mantén
use_build_context_synchronouslyactivada y trata sus advertencias en diffs generados como no negociables — es un detector gratuito de bugs de ciclo de vida. return, no anides. Prefiere el retorno temprano (if (!context.mounted) return;) en vez de envolver el resto del handler enif (context.mounted) { ... }— mantiene el camino feliz plano y hace el siguienteawaitmás fácil de auditar.- Guardado significa omitido, no reintentado. Cuando la verificación falla, lo correcto suele ser no hacer nada: la pantalla ya no está, así que no hay snackbar que mostrar. Si el trabajo sigue importando (p. ej. persistir datos), haz el trabajo primero y guarda solo la parte de UI.
- Codebases pre-3.7: solo existía
State.mounted; los helpers que recibían unBuildContextcrudo no podían verificarlo. Si estás en un SDK viejo, es una gran razón para actualizar — la guarda de una línea es toda la funcionalidad.
Recursos
- use_build_context_synchronously — regla del linter de Dart
- BuildContext.mounted — documentación de la API de Flutter
- State.mounted — documentación de la API de Flutter
- flutter_lints — set de lints recomendado en pub.dev
- Notas de la versión Flutter 3.7.0 (mounted agregado a BuildContext)
¿Construyendo una funcionalidad con IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.