Files
habo/lib/settings/settings_manager.dart
dazhuang aa69f2a91e feat: initial commit - Habo habit tracking app
- 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)
2026-04-13 15:02:30 +00:00

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();
}
}