improved performance and battery usage on older devices

This commit is contained in:
2026-09-25 23:58:08 +02:00
parent ed8e52f475
commit 56a497985b
70 changed files with 1631 additions and 621 deletions
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import '../../../../../storage/settings.dart';
@@ -10,6 +11,8 @@ import '../../../../../view/pages/settings/data/default_settings.dart';
class SettingsCubit extends HydratedCubit<Settings> {
static const _debounceTag = 'settings_persist';
bool _emitScheduled = false;
Map<String, dynamic>? _lastEmittedJson;
Timer? _silentSave;
SettingsCubit() : super(DefaultSettings.get());
@@ -34,21 +37,53 @@ class SettingsCubit extends HydratedCubit<Settings> {
return state;
}
/// Persists in-place mutations without notifying listeners. For high-frequency
/// writes nobody renders live (chat drafts per keystroke): a regular write
/// would rebuild the app root and every settings watcher on each change.
void saveSilently() {
_silentSave?.cancel();
_silentSave = Timer(const Duration(milliseconds: 800), flushSilentSave);
}
/// Writes a pending [saveSilently] right away (e.g. when the app pauses).
void flushSilentSave() {
if (_silentSave == null) return;
_silentSave!.cancel();
_silentSave = null;
HydratedBloc.storage.write(storageToken, state.toJson());
}
void _emitFreshInstance() {
try {
emit(Settings.fromJson(state.toJson()));
final json = state.toJson();
// The debounced emit usually follows the microtask emit with identical
// content; skip it instead of rebuilding every listener a second time.
if (const DeepCollectionEquality().equals(json, _lastEmittedJson)) {
return;
}
_lastEmittedJson = json;
emit(Settings.fromJson(json));
} catch (e) {
log('Failed to refresh settings state: $e');
}
}
Future<void> reset() async {
_silentSave?.cancel();
_silentSave = null;
_lastEmittedJson = null;
emit(DefaultSettings.get());
}
// Modules missing from a stale persisted moduleOrder are handled at read
// time by AppModule.effectiveModuleOrder (inserted at their default
// position) — no healing on hydration needed.
@override
Future<void> close() {
flushSilentSave();
return super.close();
}
@override
Settings fromJson(Map<String, dynamic> json) {
try {