implemented disposal guard in files search controller to safely handle async listener notifications
This commit is contained in:
@@ -25,6 +25,16 @@ class FilesSearchController extends ChangeNotifier {
|
||||
bool _serverLoading = false;
|
||||
Object? _serverError;
|
||||
int _serverEpoch = 0;
|
||||
bool _disposed = false;
|
||||
|
||||
/// Guards against the race where the search delegate is closed (and the
|
||||
/// controller disposed) while a debounced cache scan or server call is
|
||||
/// still in flight: their late `notifyListeners()` would otherwise throw
|
||||
/// on a disposed `ChangeNotifier`.
|
||||
void _safeNotify() {
|
||||
if (_disposed) return;
|
||||
_safeNotify();
|
||||
}
|
||||
|
||||
String get query => _query;
|
||||
List<String> get pathScope => List.unmodifiable(_pathScope);
|
||||
@@ -60,7 +70,7 @@ class FilesSearchController extends ChangeNotifier {
|
||||
_serverResults = const [];
|
||||
_serverLoading = false;
|
||||
_serverError = null;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
return;
|
||||
}
|
||||
// Show loading immediately — even before the (typically fast) cache
|
||||
@@ -68,12 +78,12 @@ class FilesSearchController extends ChangeNotifier {
|
||||
// starts typing rather than after the first await hop.
|
||||
_serverLoading = true;
|
||||
_serverError = null;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
|
||||
final cacheHits = await searchLocalCaches(_query, pathScope: _pathScope);
|
||||
if (epoch != _serverEpoch) return;
|
||||
_cacheResults = cacheHits;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
_scheduleServerCall();
|
||||
}
|
||||
|
||||
@@ -84,17 +94,17 @@ class FilesSearchController extends ChangeNotifier {
|
||||
_pathScope = const [];
|
||||
final epoch = ++_serverEpoch;
|
||||
if (_query.trim().isEmpty) {
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
return;
|
||||
}
|
||||
_serverLoading = true;
|
||||
_serverError = null;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
|
||||
final cacheHits = await searchLocalCaches(_query);
|
||||
if (epoch != _serverEpoch) return;
|
||||
_cacheResults = cacheHits;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
_scheduleServerCall();
|
||||
}
|
||||
|
||||
@@ -106,7 +116,7 @@ class FilesSearchController extends ChangeNotifier {
|
||||
Debouncer.cancel(_debounceTag);
|
||||
_serverLoading = true;
|
||||
_serverError = null;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
_runServerCall();
|
||||
}
|
||||
|
||||
@@ -128,18 +138,19 @@ class FilesSearchController extends ChangeNotifier {
|
||||
.toList();
|
||||
_serverLoading = false;
|
||||
_serverError = null;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
} on Object catch (e) {
|
||||
if (epoch != _serverEpoch) return;
|
||||
_serverResults = const [];
|
||||
_serverLoading = false;
|
||||
_serverError = e;
|
||||
notifyListeners();
|
||||
_safeNotify();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposed = true;
|
||||
Debouncer.cancel(_debounceTag);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user