187 lines
5.1 KiB
Dart
187 lines
5.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:provider/provider.dart';
|
|
import '../theme/adaptive_theme.dart';
|
|
|
|
/// 테마 관리 Provider
|
|
class ThemeProvider extends ChangeNotifier {
|
|
static const String _themeBoxName = 'theme_settings';
|
|
static const String _themeKey = 'theme_settings';
|
|
|
|
late Box<Map> _themeBox;
|
|
ThemeSettings _themeSettings = const ThemeSettings();
|
|
|
|
ThemeSettings get themeSettings => _themeSettings;
|
|
|
|
AppThemeMode get themeMode => _themeSettings.mode;
|
|
bool get useSystemColors => _themeSettings.useSystemColors;
|
|
bool get largeText => _themeSettings.largeText;
|
|
bool get reduceMotion => _themeSettings.reduceMotion;
|
|
bool get highContrast => _themeSettings.highContrast;
|
|
|
|
/// Provider 초기화
|
|
Future<void> initialize() async {
|
|
_themeBox = await Hive.openBox<Map>(_themeBoxName);
|
|
await _loadThemeSettings();
|
|
}
|
|
|
|
/// 저장된 테마 설정 로드
|
|
Future<void> _loadThemeSettings() async {
|
|
final savedSettings = _themeBox.get(_themeKey);
|
|
if (savedSettings != null) {
|
|
_themeSettings = ThemeSettings.fromJson(
|
|
Map<String, dynamic>.from(savedSettings),
|
|
);
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
/// 테마 설정 저장
|
|
Future<void> _saveThemeSettings() async {
|
|
await _themeBox.put(_themeKey, _themeSettings.toJson());
|
|
}
|
|
|
|
/// 테마 모드 변경
|
|
Future<void> setThemeMode(AppThemeMode mode) async {
|
|
_themeSettings = _themeSettings.copyWith(mode: mode);
|
|
await _saveThemeSettings();
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 시스템 색상 사용 설정
|
|
Future<void> setUseSystemColors(bool value) async {
|
|
_themeSettings = _themeSettings.copyWith(useSystemColors: value);
|
|
await _saveThemeSettings();
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 큰 텍스트 설정
|
|
Future<void> setLargeText(bool value) async {
|
|
_themeSettings = _themeSettings.copyWith(largeText: value);
|
|
await _saveThemeSettings();
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 모션 감소 설정
|
|
Future<void> setReduceMotion(bool value) async {
|
|
_themeSettings = _themeSettings.copyWith(reduceMotion: value);
|
|
await _saveThemeSettings();
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 고대비 설정
|
|
Future<void> setHighContrast(bool value) async {
|
|
_themeSettings = _themeSettings.copyWith(highContrast: value);
|
|
await _saveThemeSettings();
|
|
notifyListeners();
|
|
}
|
|
|
|
/// 현재 설정에 따른 테마 가져오기
|
|
ThemeData getTheme(BuildContext context) {
|
|
final platformBrightness = MediaQuery.of(context).platformBrightness;
|
|
|
|
ThemeData baseTheme;
|
|
|
|
switch (_themeSettings.mode) {
|
|
case AppThemeMode.light:
|
|
baseTheme = AdaptiveTheme.lightTheme;
|
|
break;
|
|
case AppThemeMode.dark:
|
|
baseTheme = AdaptiveTheme.darkTheme;
|
|
break;
|
|
case AppThemeMode.oled:
|
|
baseTheme = AdaptiveTheme.oledTheme;
|
|
break;
|
|
case AppThemeMode.system:
|
|
baseTheme = platformBrightness == Brightness.dark
|
|
? AdaptiveTheme.darkTheme
|
|
: AdaptiveTheme.lightTheme;
|
|
break;
|
|
}
|
|
|
|
// 접근성 설정 적용
|
|
return AdaptiveTheme.getAccessibleTheme(
|
|
baseTheme,
|
|
largeText: _themeSettings.largeText,
|
|
reduceMotion: _themeSettings.reduceMotion,
|
|
highContrast: _themeSettings.highContrast,
|
|
);
|
|
}
|
|
|
|
/// 현재 테마가 다크 모드인지 확인
|
|
bool isDarkMode(BuildContext context) {
|
|
final platformBrightness = MediaQuery.of(context).platformBrightness;
|
|
|
|
switch (_themeSettings.mode) {
|
|
case AppThemeMode.light:
|
|
return false;
|
|
case AppThemeMode.dark:
|
|
case AppThemeMode.oled:
|
|
return true;
|
|
case AppThemeMode.system:
|
|
return platformBrightness == Brightness.dark;
|
|
}
|
|
}
|
|
|
|
/// 테마 토글 (라이트/다크)
|
|
Future<void> toggleTheme() async {
|
|
if (_themeSettings.mode == AppThemeMode.light) {
|
|
await setThemeMode(AppThemeMode.dark);
|
|
} else {
|
|
await setThemeMode(AppThemeMode.light);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 테마 전환 애니메이션 위젯
|
|
class AnimatedThemeBuilder extends StatelessWidget {
|
|
final Widget Function(BuildContext, ThemeData) builder;
|
|
final Duration duration;
|
|
|
|
const AnimatedThemeBuilder({
|
|
super.key,
|
|
required this.builder,
|
|
this.duration = const Duration(milliseconds: 300),
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final themeProvider = context.watch<ThemeProvider>();
|
|
final theme = themeProvider.getTheme(context);
|
|
|
|
return AnimatedTheme(
|
|
data: theme,
|
|
duration: duration,
|
|
child: Builder(
|
|
builder: (context) => builder(context, theme),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 테마별 색상 위젯
|
|
class ThemedColor extends StatelessWidget {
|
|
final Color lightColor;
|
|
final Color darkColor;
|
|
final Widget child;
|
|
|
|
const ThemedColor({
|
|
super.key,
|
|
required this.lightColor,
|
|
required this.darkColor,
|
|
required this.child,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final isDark = context.read<ThemeProvider>().isDarkMode(context);
|
|
|
|
return Theme(
|
|
data: Theme.of(context).copyWith(
|
|
primaryColor: isDark ? darkColor : lightColor,
|
|
),
|
|
child: child,
|
|
);
|
|
}
|
|
}
|