harden SettingsStore against notify-after-dispose for fire-and-forget writes

set() doesn't await its file write, so a write in flight when the store is
disposed (app shutdown, or a closing test) would assert on a disposed
ChangeNotifier. Skip the post-write notify once disposed via a _disposed guard +
_safeNotify. Surfaced by the T-293 theme-persistence test flaking under the
loaded parallel run; also the correct behaviour for graceful shutdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 18:05:53 +02:00
co-authored by Claude Opus 4.8
parent 24364e6329
commit adbd8e285a
5 changed files with 62 additions and 5 deletions
+19 -3
View File
@@ -14,6 +14,22 @@ class SettingsStore extends ChangeNotifier {
final Map<String, Object?> _appValues = <String, Object?>{};
final Map<String, Object?> _projectValues = <String, Object?>{};
// Writes are fire-and-forget (callers don't await `set`); if the store is
// disposed while one is mid-flight (app shutdown, a closing test), skip the
// post-write notify rather than asserting on a disposed ChangeNotifier.
bool _disposed = false;
@override
void dispose() {
_disposed = true;
super.dispose();
}
void _safeNotify() {
if (_disposed) return;
notifyListeners();
}
Future<void> load() async {
_appValues
..clear()
@@ -22,7 +38,7 @@ class SettingsStore extends ChangeNotifier {
if (projectDir != null) {
_projectValues.addAll(await _readFile(_projectFile));
}
notifyListeners();
_safeNotify();
}
Future<void> setProjectDir(Directory? dir) async {
@@ -31,7 +47,7 @@ class SettingsStore extends ChangeNotifier {
if (dir != null) {
_projectValues.addAll(await _readFile(_projectFile));
}
notifyListeners();
_safeNotify();
}
File get _appFile => File('${appDir.path}/settings.yaml');
@@ -73,7 +89,7 @@ class SettingsStore extends ChangeNotifier {
_appValues[key] = value;
await _writeFile(_appFile, _appValues);
}
notifyListeners();
_safeNotify();
}
Future<Map<String, Object?>> _readFile(File f) async {