Skip to content

Commit

Permalink
perf(prefs): use completers instead of polling
Browse files Browse the repository at this point in the history
  • Loading branch information
adil192 committed May 8, 2024
1 parent 58fa3b7 commit 8ea1285
Showing 1 changed file with 43 additions and 18 deletions.
61 changes: 43 additions & 18 deletions lib/data/prefs.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
Expand Down Expand Up @@ -320,12 +321,6 @@ abstract class IPref<T> extends ValueNotifier<T> {

final T defaultValue;

bool _loaded = false;

/// Whether this pref has changes that have yet to be saved to disk.
@protected
bool _saved = true;

IPref(
this.key,
this.defaultValue, {
Expand All @@ -335,11 +330,12 @@ abstract class IPref<T> extends ValueNotifier<T> {
deprecatedKeys = deprecatedKeys ?? [],
super(defaultValue) {
if (Prefs.testingMode) {
_loaded = true;
loaded = true;

return;
} else {
_load().then((T? loadedValue) {
_loaded = true;
loaded = true;
if (loadedValue != null) {
value = loadedValue;
}
Expand Down Expand Up @@ -367,23 +363,52 @@ abstract class IPref<T> extends ValueNotifier<T> {
return super.value;
}

bool _loaded = false;
Completer<void>? _loadedCompleter = Completer();
bool get loaded => _loaded;
bool get saved => _saved;
@protected
set loaded(bool loaded) {
_loaded = loaded;

Future<void> waitUntilLoaded() async {
while (!loaded) {
await Future.delayed(const Duration(milliseconds: 10));
if (loaded) {
if (!_loadedCompleter!.isCompleted) _loadedCompleter!.complete();
_loadedCompleter = null;
}
}

final _saved = ValueNotifier<bool>(true);

/// Whether this pref has changes that have yet to be saved to disk.
bool get saved => _saved.value;
@protected
set saved(bool saved) {
_saved.value = saved;
}

Future<void> waitUntilLoaded() async {
if (loaded) return;
assert(_loadedCompleter != null,
'_loadedCompleter should be non-null until loaded');
return _loadedCompleter!.future;
}

/// Waits until the value has been saved to disk.
/// Note that there is no guarantee with shared preferences that
/// the value will actually be saved to disk.
@visibleForTesting
Future<void> waitUntilSaved() async {
while (!saved) {
await Future.delayed(const Duration(milliseconds: 10));
if (saved) return;

final completer = Completer();

void listener() {
if (!saved) return;
_saved.removeListener(listener);
completer.complete();
}

_saved.addListener(listener);
return completer.future;
}

/// Lets us use notifyListeners outside of the class
Expand Down Expand Up @@ -451,7 +476,7 @@ class PlainPref<T> extends IPref<T> {

@override
Future _save() async {
_saved = false;
saved = false;
try {
_prefs ??= await SharedPreferences.getInstance();

Expand Down Expand Up @@ -512,7 +537,7 @@ class PlainPref<T> extends IPref<T> {
return await _prefs!.setString(key, value as String);
}
} finally {
_saved = true;
saved = true;
}
}

Expand Down Expand Up @@ -637,7 +662,7 @@ class EncPref<T> extends IPref<T> {

@override
Future _save() async {
_saved = false;
saved = false;
try {
_storage ??= const FlutterSecureStorage();
if (T == String)
Expand All @@ -646,7 +671,7 @@ class EncPref<T> extends IPref<T> {
return await _storage!.write(key: key, value: jsonEncode(value));
return await _storage!.write(key: key, value: jsonEncode(value));
} finally {
_saved = true;
saved = true;
}
}

Expand Down

0 comments on commit 8ea1285

Please sign in to comment.