import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:speedometer/main.dart';
String _defaultToString(dynamic value) => value.toString();
void Function(T) storageObserver<T>(
String key, [
String Function(T) toString = _defaultToString,
]) {
return (T value) => SharedPreferencesAsync().setString(key, toString(value));
}
Future<T> fromString<T>(
String key,
List<T> options, [
String Function(T) toString = _defaultToString,
]) {
return SharedPreferencesAsync()
.getString(key)
.then(
(value) => options.firstWhere((option) => value == toString(option)),
);
}
Observable<T> observablePreference<T>(
String key,
T defaultValue,
List<T> options, [
String Function(T) toString = _defaultToString,
]) => Observable(
defaultValue,
loadValue: fromString(key, options, toString),
observers: [storageObserver(key, toString)],
);
final speedUnitsObservable = observablePreference('speedUnits', null, [
...SpeedUnit.values,
null,
]);
final themeModeObservable = observablePreference(
'themeMode',
ThemeMode.system,
ThemeMode.values,
);
final primaryColorObservable = observablePreference(
"primaryColor",
Colors.red,
[...Colors.primaries, Colors.grey],
(color) => color.toARGB32().toString(),
);
final showMarginOfErrorObservable = observablePreference(
"showMarginOfError",
true,
[true, false],
);
final locationAccuracyObservable = observablePreference(
"locationAccuracy",
LocationAccuracy.best,
LocationAccuracy.values,
);
class Observable<T> {
T _value;
final List<void Function(T)?> _observers = [];
Observable(
this._value, {
Future<T>? loadValue,
List<void Function(T)?>? observers,
}) {
if (observers != null) {
_observers.addAll(observers);
}
if (loadValue != null) {
loadValue.then((value) => this.value = value);
}
}
T get value => _value;
set value(T value) {
if (value == _value) {
return;
}
_value = value;
for (var observer in _observers) {
if (observer != null) {
observer(_value);
}
}
}
int subscribe(void Function(T) onChange) {
final id = _observers.length;
_observers.add(onChange);
return id;
}
void unsubscribe(int subscriberId) {
_observers[subscriberId] = null;
}
}
class ObserverBuilder<T> extends StatefulWidget {
final Observable<T> observable;
final Widget Function(BuildContext, T, void Function(T)) builder;
const ObserverBuilder({
required this.observable,
required this.builder,
super.key,
});
@override
State<ObserverBuilder<T>> createState() => _ObserverState<T>();
}
class _ObserverState<T> extends State<ObserverBuilder<T>> {
late T _value;
late int _subscriberId;
@override
void initState() {
super.initState();
_subscriberId = widget.observable.subscribe(
(value) => setState(() => _value = value),
);
_value = widget.observable.value;
}
@override
void dispose() {
super.dispose();
widget.observable.unsubscribe(_subscriberId);
}
@override
Widget build(BuildContext context) {
return widget.builder(
context,
_value,
(value) => widget.observable.value = value,
);
}
}
|