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( String key, [ String Function(T) toString = _defaultToString, ]) { return (T value) => SharedPreferencesAsync().setString(key, toString(value)); } Future fromString( String key, List options, [ String Function(T) toString = _defaultToString, ]) { return SharedPreferencesAsync() .getString(key) .then( (value) => options.firstWhere((option) => value == toString(option)), ); } Observable observablePreference( String key, T defaultValue, List 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 _value; final List _observers = []; Observable( this._value, { Future? loadValue, List? 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 extends StatefulWidget { final Observable observable; final Widget Function(BuildContext, T, void Function(T)) builder; const ObserverBuilder({ required this.observable, required this.builder, super.key, }); @override State> createState() => _ObserverState(); } class _ObserverState extends State> { 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, ); } }