import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tma_app/app.dart'; import 'package:tma_app/core/api/dio_provider.dart'; import 'package:tma_app/core/api/session_store.dart'; import 'package:tma_app/core/config/app_config.dart'; import 'package:tma_app/core/storage/preferences_provider.dart'; import 'package:tma_app/shared/widgets/rup_toast.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); // dotenv 로드 — .env 파일이 없으면 기본값으로 동작 try { await dotenv.load(fileName: '.env'); } catch (_) { // .env 파일이 없는 경우 무시 (기본값 사용) } // 출시 빌드가 개발 서버·평문 http 를 가리키면 바로 멈춘다 — 사용자 기기에서 조용히 모든 요청이 실패하는 것보다 낫다 if (kReleaseMode && readDataMode() == DataMode.api) { final problem = releaseApiUrlProblem(readApiBaseUrl()); if (problem != null) throw StateError(problem); } // 시안은 세로 화면 전용이다 await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); // 진입 화면 결정에 필요한 저장값을 첫 프레임 전에 읽어 둔다 final prefs = await SharedPreferences.getInstance(); // 로그인 세션(토큰) — 서버 모드에서만 secure storage 를 쓴다 final session = readDataMode() == DataMode.api ? await SessionStore.load(const SecureTokenVault()) : SessionStore(MemoryTokenVault()); runApp( ProviderScope( overrides: [ sharedPreferencesProvider.overrideWithValue(prefs), sessionStoreProvider.overrideWithValue(session), apiErrorReporterProvider.overrideWith(apiErrorToastReporter), ], child: const RupApp(), ), ); }