diff options
| author | Mica White <botahamec@outlook.com> | 2026-01-13 22:51:59 -0500 |
|---|---|---|
| committer | Mica White <botahamec@outlook.com> | 2026-01-13 22:51:59 -0500 |
| commit | 359d8e07ef5cb585fff13031d075d7c949135317 (patch) | |
| tree | 0b317599d7cbbb9e5f8b4fce8e99559ad45bfd8c /lib/jotai.dart | |
| parent | d44654698cc3c65a5a458f4c2cdc3b2d868890f5 (diff) | |
Complete settings page
Diffstat (limited to 'lib/jotai.dart')
| -rw-r--r-- | lib/jotai.dart | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/lib/jotai.dart b/lib/jotai.dart new file mode 100644 index 0000000..a415e13 --- /dev/null +++ b/lib/jotai.dart @@ -0,0 +1,150 @@ +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, + ); + } +} |
