Initial commit: SubManager Flutter App

주요 구현 완료 기능:
- 구독 관리 (추가/편집/삭제/카테고리 분류)
- 이벤트 할인 시스템 (기본값 자동 설정)
- SMS 자동 스캔 및 구독 정보 추출
- 알림 시스템 (타임존 처리 안정화)
- 환율 변환 지원 (KRW/USD)
- 반응형 UI 및 애니메이션
- 다국어 지원 (한국어/영어)

버그 수정:
- NotificationService tz.local 초기화 오류 해결
- MainScreenSummaryCard 레이아웃 오버플로우 수정
- 구독 추가 시 LateInitializationError 완전 해결

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-07-09 14:29:53 +09:00
commit 8619e96739
177 changed files with 23085 additions and 0 deletions

126
lib/main.dart Normal file
View File

@@ -0,0 +1,126 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'models/subscription_model.dart';
import 'models/category_model.dart';
import 'providers/subscription_provider.dart';
import 'providers/app_lock_provider.dart';
import 'providers/notification_provider.dart';
import 'screens/main_screen.dart';
import 'screens/app_lock_screen.dart';
import 'services/notification_service.dart';
import 'providers/category_provider.dart';
import 'providers/locale_provider.dart';
import 'l10n/app_localizations.dart';
import 'theme/app_theme.dart';
import 'screens/splash_screen.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io' show Platform;
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 구글 모바일 광고 SDK 초기화 (웹이 아니고, Android/iOS에서만)
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
await MobileAds.instance.initialize();
}
// 앱 시작 시 이미지 캐시 관리
try {
// 메모리 이미지 캐시는 유지하지만 필요한 경우 삭제할 수 있도록 준비
final cache = PaintingBinding.instance.imageCache;
// 오래된 디스크 캐시 파일만 지우기 (새로운 것은 유지)
await DefaultCacheManager().emptyCache();
print('이미지 캐시 관리 초기화 완료');
} catch (e) {
print('캐시 초기화 오류: $e');
}
// Hive 초기화
await Hive.initFlutter();
Hive.registerAdapter(SubscriptionModelAdapter());
Hive.registerAdapter(CategoryModelAdapter());
await Hive.openBox<SubscriptionModel>('subscriptions');
await Hive.openBox<CategoryModel>('categories');
final appLockBox = await Hive.openBox<bool>('app_lock');
// 알림 서비스를 가장 먼저 초기화
await NotificationService.init();
final subscriptionProvider = SubscriptionProvider();
final categoryProvider = CategoryProvider();
final localeProvider = LocaleProvider();
final notificationProvider = NotificationProvider();
await subscriptionProvider.init();
await categoryProvider.init();
await localeProvider.init();
await notificationProvider.init();
// NotificationProvider에 SubscriptionProvider 연결 (알림 재예약용)
// SRP 원칙에 따라 다른 Provider 객체를 명시적으로 주입
notificationProvider.setSubscriptionProvider(subscriptionProvider);
// 별도의 비동기 함수로 알림 관련 초기화 오류 처리
Future.delayed(Duration.zero, () {
try {
if (notificationProvider.isPaymentEnabled) {
// 백그라운드에서 비동기적으로 알림 설정 업데이트
NotificationService.reschedulAllNotifications(
subscriptionProvider.subscriptions);
}
} catch (e) {
debugPrint('알림 초기 설정 중 오류 발생: $e');
}
});
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => subscriptionProvider),
ChangeNotifierProvider(create: (_) => categoryProvider),
ChangeNotifierProvider(create: (_) => AppLockProvider(appLockBox)),
ChangeNotifierProvider(create: (_) => notificationProvider),
ChangeNotifierProvider(create: (_) => localeProvider),
],
child: const SubManagerApp(),
),
);
}
class SubManagerApp extends StatelessWidget {
const SubManagerApp({super.key});
@override
Widget build(BuildContext context) {
return Consumer<LocaleProvider>(
builder: (context, localeProvider, child) {
return MaterialApp(
title: 'SubManager',
debugShowCheckedModeBanner: false,
theme: AppTheme.lightTheme,
locale: localeProvider.locale,
localizationsDelegates: const [
AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('en'),
Locale('ko'),
],
navigatorKey: navigatorKey,
home: const SplashScreen(),
);
},
);
}
}