feat: 초기 프로젝트 설정 및 LunchPick 앱 구현
LunchPick(오늘 뭐 먹Z?) Flutter 앱의 초기 구현입니다. 주요 기능: - 네이버 지도 연동 맛집 추가 - 랜덤 메뉴 추천 시스템 - 날씨 기반 거리 조정 - 방문 기록 관리 - Bluetooth 맛집 공유 - 다크모드 지원 기술 스택: - Flutter 3.8.1+ - Riverpod 상태 관리 - Hive 로컬 DB - Clean Architecture 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
264
lib/presentation/providers/settings_provider.dart
Normal file
264
lib/presentation/providers/settings_provider.dart
Normal file
@@ -0,0 +1,264 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:lunchpick/domain/repositories/settings_repository.dart';
|
||||
import 'package:lunchpick/domain/entities/user_settings.dart';
|
||||
import 'package:lunchpick/presentation/providers/di_providers.dart';
|
||||
|
||||
/// 재방문 금지 일수 Provider
|
||||
final daysToExcludeProvider = FutureProvider<int>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.getDaysToExclude();
|
||||
});
|
||||
|
||||
/// 우천시 최대 거리 Provider
|
||||
final maxDistanceRainyProvider = FutureProvider<int>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.getMaxDistanceRainy();
|
||||
});
|
||||
|
||||
/// 평상시 최대 거리 Provider
|
||||
final maxDistanceNormalProvider = FutureProvider<int>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.getMaxDistanceNormal();
|
||||
});
|
||||
|
||||
/// 알림 지연 시간 Provider
|
||||
final notificationDelayMinutesProvider = FutureProvider<int>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.getNotificationDelayMinutes();
|
||||
});
|
||||
|
||||
/// 알림 활성화 여부 Provider
|
||||
final notificationEnabledProvider = FutureProvider<bool>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.isNotificationEnabled();
|
||||
});
|
||||
|
||||
/// 다크모드 활성화 여부 Provider
|
||||
final darkModeEnabledProvider = FutureProvider<bool>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.isDarkModeEnabled();
|
||||
});
|
||||
|
||||
/// 첫 실행 여부 Provider
|
||||
final isFirstRunProvider = FutureProvider<bool>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.isFirstRun();
|
||||
});
|
||||
|
||||
/// 설정 스트림 Provider
|
||||
final settingsStreamProvider = StreamProvider<Map<String, dynamic>>((ref) {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.watchSettings();
|
||||
});
|
||||
|
||||
/// UserSettings Provider
|
||||
final userSettingsProvider = FutureProvider<UserSettings>((ref) async {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.getUserSettings();
|
||||
});
|
||||
|
||||
/// UserSettings 스트림 Provider
|
||||
final userSettingsStreamProvider = StreamProvider<UserSettings>((ref) {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return repository.watchUserSettings();
|
||||
});
|
||||
|
||||
/// 설정 관리 StateNotifier
|
||||
class SettingsNotifier extends StateNotifier<AsyncValue<void>> {
|
||||
final SettingsRepository _repository;
|
||||
|
||||
SettingsNotifier(this._repository) : super(const AsyncValue.data(null));
|
||||
|
||||
/// 재방문 금지 일수 설정
|
||||
Future<void> setDaysToExclude(int days) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setDaysToExclude(days);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 우천시 최대 거리 설정
|
||||
Future<void> setMaxDistanceRainy(int meters) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setMaxDistanceRainy(meters);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 평상시 최대 거리 설정
|
||||
Future<void> setMaxDistanceNormal(int meters) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setMaxDistanceNormal(meters);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 알림 지연 시간 설정
|
||||
Future<void> setNotificationDelayMinutes(int minutes) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setNotificationDelayMinutes(minutes);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 알림 활성화 설정
|
||||
Future<void> setNotificationEnabled(bool enabled) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setNotificationEnabled(enabled);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 다크모드 설정
|
||||
Future<void> setDarkModeEnabled(bool enabled) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setDarkModeEnabled(enabled);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 첫 실행 상태 업데이트
|
||||
Future<void> setFirstRun(bool isFirst) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.setFirstRun(isFirst);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// 설정 초기화
|
||||
Future<void> resetSettings() async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.resetSettings();
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
/// UserSettings 업데이트
|
||||
Future<void> updateUserSettings(UserSettings settings) async {
|
||||
state = const AsyncValue.loading();
|
||||
try {
|
||||
await _repository.updateUserSettings(settings);
|
||||
state = const AsyncValue.data(null);
|
||||
} catch (e, stack) {
|
||||
state = AsyncValue.error(e, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SettingsNotifier Provider
|
||||
final settingsNotifierProvider = StateNotifierProvider<SettingsNotifier, AsyncValue<void>>((ref) {
|
||||
final repository = ref.watch(settingsRepositoryProvider);
|
||||
return SettingsNotifier(repository);
|
||||
});
|
||||
|
||||
/// 설정 프리셋
|
||||
enum SettingsPreset {
|
||||
normal(
|
||||
name: '일반 모드',
|
||||
daysToExclude: 7,
|
||||
maxDistanceNormal: 1000,
|
||||
maxDistanceRainy: 500,
|
||||
),
|
||||
economic(
|
||||
name: '절약 모드',
|
||||
daysToExclude: 3,
|
||||
maxDistanceNormal: 500,
|
||||
maxDistanceRainy: 300,
|
||||
),
|
||||
convenience(
|
||||
name: '편의 모드',
|
||||
daysToExclude: 14,
|
||||
maxDistanceNormal: 2000,
|
||||
maxDistanceRainy: 1000,
|
||||
);
|
||||
|
||||
final String name;
|
||||
final int daysToExclude;
|
||||
final int maxDistanceNormal;
|
||||
final int maxDistanceRainy;
|
||||
|
||||
const SettingsPreset({
|
||||
required this.name,
|
||||
required this.daysToExclude,
|
||||
required this.maxDistanceNormal,
|
||||
required this.maxDistanceRainy,
|
||||
});
|
||||
}
|
||||
|
||||
/// 프리셋 적용 Provider
|
||||
final applyPresetProvider = Provider.family<Future<void>, SettingsPreset>((ref, preset) async {
|
||||
final notifier = ref.read(settingsNotifierProvider.notifier);
|
||||
|
||||
await notifier.setDaysToExclude(preset.daysToExclude);
|
||||
await notifier.setMaxDistanceNormal(preset.maxDistanceNormal);
|
||||
await notifier.setMaxDistanceRainy(preset.maxDistanceRainy);
|
||||
});
|
||||
|
||||
/// 현재 위치 Provider
|
||||
final currentLocationProvider = StateProvider<({double latitude, double longitude})?>((ref) => null);
|
||||
|
||||
/// 선호 카테고리 Provider
|
||||
final preferredCategoriesProvider = StateProvider<List<String>>((ref) => []);
|
||||
|
||||
/// 제외 카테고리 Provider
|
||||
final excludedCategoriesProvider = StateProvider<List<String>>((ref) => []);
|
||||
|
||||
/// 언어 설정 Provider
|
||||
final languageProvider = StateProvider<String>((ref) => 'ko');
|
||||
|
||||
/// 위치 권한 상태 Provider
|
||||
final locationPermissionProvider = StateProvider<bool>((ref) => false);
|
||||
|
||||
/// 알림 권한 상태 Provider
|
||||
final notificationPermissionProvider = StateProvider<bool>((ref) => false);
|
||||
|
||||
/// 모든 설정 상태를 통합한 Provider
|
||||
final allSettingsProvider = Provider<Map<String, dynamic>>((ref) {
|
||||
final daysToExclude = ref.watch(daysToExcludeProvider).value ?? 7;
|
||||
final maxDistanceRainy = ref.watch(maxDistanceRainyProvider).value ?? 500;
|
||||
final maxDistanceNormal = ref.watch(maxDistanceNormalProvider).value ?? 1000;
|
||||
final notificationDelay = ref.watch(notificationDelayMinutesProvider).value ?? 90;
|
||||
final notificationEnabled = ref.watch(notificationEnabledProvider).value ?? false;
|
||||
final darkMode = ref.watch(darkModeEnabledProvider).value ?? false;
|
||||
final currentLocation = ref.watch(currentLocationProvider);
|
||||
final preferredCategories = ref.watch(preferredCategoriesProvider);
|
||||
final excludedCategories = ref.watch(excludedCategoriesProvider);
|
||||
final language = ref.watch(languageProvider);
|
||||
|
||||
return {
|
||||
'daysToExclude': daysToExclude,
|
||||
'maxDistanceRainy': maxDistanceRainy,
|
||||
'maxDistanceNormal': maxDistanceNormal,
|
||||
'notificationDelayMinutes': notificationDelay,
|
||||
'notificationEnabled': notificationEnabled,
|
||||
'darkModeEnabled': darkMode,
|
||||
'currentLocation': currentLocation,
|
||||
'preferredCategories': preferredCategories,
|
||||
'excludedCategories': excludedCategories,
|
||||
'language': language,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user