- Complete MVP with Repository Pattern, SQLite storage - Provider + ChangeNotifier state management - Navigation 2.0 with deep link support - Habit CRUD with twoDayRule, notifications, categories - Backup/Restore via JSON - Statistics with streak tracking - Material You theme support - Biometric lock support - Desktop widget support - 27 languages i18n structure - Comprehensive test suite (87/89 passing)
177 lines
5.7 KiB
Dart
177 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:habo/constants.dart';
|
|
import 'package:habo/model/settings_data.dart';
|
|
|
|
class SettingsManager extends ChangeNotifier {
|
|
late SettingsData _data;
|
|
SharedPreferences? _prefs;
|
|
bool _initialized = false;
|
|
|
|
SettingsData get data => _data;
|
|
|
|
// Convenience getters
|
|
Themes get theme => Themes.values.firstWhere(
|
|
(e) => e.name == _data.theme,
|
|
orElse: () => Themes.device,
|
|
);
|
|
String get weekStart => _data.weekStart;
|
|
bool get showMonthName => _data.showMonthName;
|
|
bool get showCategories => _data.showCategories;
|
|
bool get showDailyNot => _data.showDailyNot;
|
|
TimeOfDay get dailyNotTime => TimeOfDay(hour: _data.notTimeHour, minute: _data.notTimeMinute);
|
|
bool get soundEffects => _data.soundEffects;
|
|
double get soundVolume => _data.soundVolume;
|
|
bool get biometricLock => _data.biometricLock;
|
|
bool get oneTapCheck => _data.oneTapCheck;
|
|
bool get seenOnboarding => _data.seenOnboarding;
|
|
String get lastWhatsNewVersion => _data.lastWhatsNewVersion;
|
|
Color get checkColor => Color(_data.checkColor);
|
|
Color get failColor => Color(_data.failColor);
|
|
Color get skipColor => Color(_data.skipColor);
|
|
Color get progressColor => Color(_data.progressColor);
|
|
|
|
Future<void> init() async {
|
|
_data = SettingsData();
|
|
try {
|
|
_prefs = await SharedPreferences.getInstance();
|
|
_data.theme = _prefs!.getString('theme') ?? 'device';
|
|
_data.weekStart = _prefs!.getString('weekStart') ?? 'monday';
|
|
_data.showMonthName = _prefs!.getBool('showMonthName') ?? true;
|
|
_data.showCategories = _prefs!.getBool('showCategories') ?? true;
|
|
_data.showDailyNot = _prefs!.getBool('showDailyNot') ?? true;
|
|
_data.notTimeHour = _prefs!.getInt('dailyNotTimeHour') ?? 20;
|
|
_data.notTimeMinute = _prefs!.getInt('dailyNotTimeMinute') ?? 0;
|
|
_data.soundEffects = _prefs!.getBool('soundEffects') ?? true;
|
|
_data.soundVolume = _prefs!.getDouble('soundVolume') ?? 3.0;
|
|
_data.biometricLock = _prefs!.getBool('biometricLock') ?? false;
|
|
_data.oneTapCheck = _prefs!.getBool('oneTapCheck') ?? false;
|
|
_data.seenOnboarding = _prefs!.getBool('seenOnboarding') ?? false;
|
|
_data.lastWhatsNewVersion = _prefs!.getString('lastWhatsNewVersion') ?? '';
|
|
_data.checkColor = _prefs!.getInt('checkColor') ?? 0xFF09BF30;
|
|
_data.failColor = _prefs!.getInt('failColor') ?? 0xFFF44336;
|
|
_data.skipColor = _prefs!.getInt('skipColor') ?? 0xFFFBC02D;
|
|
_data.progressColor = _prefs!.getInt('progressColor') ?? 0xFF2196F3;
|
|
} catch (_) {
|
|
// Use defaults
|
|
}
|
|
_initialized = true;
|
|
}
|
|
|
|
Future<void> loadData() async {
|
|
await init();
|
|
}
|
|
|
|
Future<void> saveData() async {
|
|
if (_prefs == null) return;
|
|
await _prefs!.setString('theme', _data.theme);
|
|
await _prefs!.setString('weekStart', _data.weekStart);
|
|
await _prefs!.setBool('showMonthName', _data.showMonthName);
|
|
await _prefs!.setBool('showCategories', _data.showCategories);
|
|
await _prefs!.setBool('showDailyNot', _data.showDailyNot);
|
|
await _prefs!.setInt('dailyNotTimeHour', _data.notTimeHour);
|
|
await _prefs!.setInt('dailyNotTimeMinute', _data.notTimeMinute);
|
|
await _prefs!.setBool('soundEffects', _data.soundEffects);
|
|
await _prefs!.setDouble('soundVolume', _data.soundVolume);
|
|
await _prefs!.setBool('biometricLock', _data.biometricLock);
|
|
await _prefs!.setBool('oneTapCheck', _data.oneTapCheck);
|
|
await _prefs!.setBool('seenOnboarding', _data.seenOnboarding);
|
|
await _prefs!.setString('lastWhatsNewVersion', _data.lastWhatsNewVersion);
|
|
await _prefs!.setInt('checkColor', _data.checkColor);
|
|
await _prefs!.setInt('failColor', _data.failColor);
|
|
await _prefs!.setInt('skipColor', _data.skipColor);
|
|
await _prefs!.setInt('progressColor', _data.progressColor);
|
|
}
|
|
|
|
Future<void> setTheme(Themes t) async {
|
|
_data.theme = t.name;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setShowMonthName(bool v) async {
|
|
_data.showMonthName = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setShowCategories(bool v) async {
|
|
_data.showCategories = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setWeekStart(String v) async {
|
|
_data.weekStart = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setShowDailyNot(bool v) async {
|
|
_data.showDailyNot = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setDailyNotTime(TimeOfDay t) async {
|
|
_data.notTimeHour = t.hour;
|
|
_data.notTimeMinute = t.minute;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setSoundEffects(bool v) async {
|
|
_data.soundEffects = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setSoundVolume(double v) async {
|
|
_data.soundVolume = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setBiometricLock(bool v) async {
|
|
_data.biometricLock = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setOneTapCheck(bool v) async {
|
|
_data.oneTapCheck = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setSeenOnboarding(bool v) async {
|
|
_data.seenOnboarding = v;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setCheckColor(Color c) async {
|
|
_data.checkColor = c.value;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setFailColor(Color c) async {
|
|
_data.failColor = c.value;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setSkipColor(Color c) async {
|
|
_data.skipColor = c.value;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
|
|
Future<void> setProgressColor(Color c) async {
|
|
_data.progressColor = c.value;
|
|
notifyListeners();
|
|
await saveData();
|
|
}
|
|
}
|