Introduction

Using a BuildContext after its associated widget has been removed from the tree triggers errors like Looking up a deactivated widget's ancestor is unsafe or Navigator operation requested with a context that does not include a Navigator. This typically happens when an asynchronous operation completes after the user has navigated away from the screen, and the callback attempts to use the context to show a dialog, navigate, or access an inherited widget.

Symptoms

  • Console error: Looking up a deactivated widget's ancestor is unsafe
  • Error: Navigator operation requested with a context that does not include a Navigator
  • ScaffoldMessenger shows: ScaffoldMessenger requested with a context that does not include a ScaffoldMessenger
  • App crashes after navigating back quickly from a screen performing async work

Common Causes

  • setState() called after async operation completes and widget is disposed
  • Navigator.of(context).pop() or push() after user navigated away
  • ScaffoldMessenger.of(context).showSnackBar() on a disposed widget
  • Provider.of<T>(context) in a callback after widget removal

Step-by-Step Fix

  1. 1.**Check mounted before using context in async callbacks**:
  2. 2.```dart
  3. 3.Future<void> loadData() async {
  4. 4.try {
  5. 5.final data = await api.fetchData();
  6. 6.if (!mounted) return; // Widget was disposed, bail out
  7. 7.setState(() {
  8. 8._data = data;
  9. 9.});
  10. 10.} catch (e) {
  11. 11.if (!mounted) return;
  12. 12.ScaffoldMessenger.of(context).showSnackBar(
  13. 13.SnackBar(content: Text('Error: $e')),
  14. 14.);
  15. 15.}
  16. 16.}
  17. 17.`
  18. 18.**Use BuildContext.mounted (Flutter 3.7+) in State classes**:
  19. 19.```dart
  20. 20.class _MyWidgetState extends State<MyWidget> {
  21. 21.Future<void> _saveData() async {
  22. 22.final result = await _repository.save(_formData);
  23. 23.if (!mounted) return;

if (result.isSuccess) { Navigator.of(context).pop(); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(result.error)), ); } } } ```

  1. 1.**For pre-Flutter 3.7, use the mounted property on State**:
  2. 2.```dart
  3. 3.class _MyWidgetState extends State<MyWidget> {
  4. 4.Future<void> _saveData() async {
  5. 5.final result = await _repository.save(_formData);
  6. 6.if (!this.mounted) return; // State.mounted, not context.mounted

Navigator.of(context).pop(); } } ```

  1. 1.Use a cancellation token pattern with http:
  2. 2.```dart
  3. 3.class _MyWidgetState extends State<MyWidget> {
  4. 4.http.Client? _client;

@override void initState() { super.initState(); _client = http.Client(); _loadData(); }

Future<void> _loadData() async { try { final response = await _client!.get( Uri.parse('https://api.example.com/data'), ); if (!mounted) return; // Handle response } catch (e) { if (!mounted) return; // Handle error } }

@override void dispose() { _client?.close(); // Cancel pending requests super.dispose(); } } ```

  1. 1.For Riverpod or Provider, use autoDispose to clean up:
  2. 2.```dart
  3. 3.@riverpod
  4. 4.Future<Data> loadData(LoadDataRef ref) async {
  5. 5.ref.onDispose(() {
  6. 6.// Cleanup when widget is disposed
  7. 7.});
  8. 8.return await api.fetchData();
  9. 9.}
  10. 10.`

Prevention

  • Always check mounted before using context after an await
  • Use context.mounted (Flutter 3.7+) for cleaner code
  • Cancel async operations in dispose()
  • Prefer GoRouter with proper error handling over raw Navigator calls
  • Use state management libraries that handle disposal automatically