# План разработки mnemo_cards_web_v2 ## 📋 Описание проекта Flutter web приложение для изучения языков с использованием **yx_scope** и **yx_state** для управления зависимостями и состоянием. ### Основные функции: - 📚 Изучение языков через карточки и темы - 🎮 Мини-игры для запоминания - 👤 Профиль пользователя со статистикой - 🔐 Авторизация через Google и Telegram - 👻 Гостевой режим (без авторизации) --- ## 🏗️ Архитектура (yx_scope) ### Иерархия скоупов: ``` AppScope (корневой, всегда существует) ├── AuthModule (модуль авторизации) ├── RouterModule (модуль навигации) ├── AnalyticsModule (модуль аналитики) └── UserScope (дочерний скоуп, создается при входе) ├── PacksModule (модуль тем/карточек) ├── GamesModule (модуль игр) └── ProfileModule (модуль профиля) ``` ### Детальное описание скоупов: #### **AppScope** *Жизненный цикл: весь запуск приложения* **Зависимости:** - `Dio` - HTTP клиент - `GoRouter` - роутинг приложения - `FirebaseApp` - Firebase инстанс - `FirebaseAnalytics` - аналитика - `SharedPreferences` - локальное хранилище - `UserScopeHolder` - холдер для UserScope - `AuthService` - сервис авторизации (работает с Firebase Auth, Google Sign-In, Telegram) - `RemoteConfigService` - Remote Config - `ThemeStateManager` - управление темой (yx_state) **Интерфейс:** ```dart abstract class AppScope implements Scope { GoRouter get router; FirebaseAnalytics get analytics; AuthService get authService; UserScopeHolder get userScopeHolder; ThemeStateManager get themeManager; SharedPreferences get sharedPreferences; } ``` **Модули:** - `AuthModule` - Google/Telegram авторизация - `RouterModule` - настройка роутинга - `AnalyticsModule` - Firebase Analytics, Crashlytics - `StorageModule` - SharedPreferences, SecureStorage --- #### **UserScope** *Жизненный цикл: от входа пользователя до выхода (или с начала для гостя)* **Зависимости:** - `UserStateManager` - состояние пользователя (yx_state) - `HttpRepositoryV2` - API запросы с токеном пользователя - `PackManager` - управление темами/карточками - `GamesManager` - управление играми - `FavoriteCardsManager` - избранные карточки - `TestStateManager` - состояние тестов - `StatisticsService` - статистика пользователя **Интерфейс:** ```dart abstract class UserScope implements Scope { UserStateManager get userStateManager; PackManager get packManager; GamesManager get gamesManager; StatisticsService get statisticsService; } // Интерфейс для родителя (AppScope должен его реализовать) abstract class UserScopeParent implements Scope { GoRouter get router; FirebaseAnalytics get analytics; AuthService get authService; SharedPreferences get sharedPreferences; } ``` **Модули:** - `PacksModule` - работа с темами и карточками - `GamesModule` - загрузка и запуск игр - `ProfileModule` - статистика, настройки профиля **Типы пользователей:** - **Гость** - `UserScope` создается без авторизации, `UserDto` = null - **Авторизованный** - `UserScope` с `UserDto` после логина --- ## 🎨 UI Структура (3 вкладки) ### 1. **Темы (HomePage)** - Список доступных тем (`CardPackDto`) - Карточки тем с превью - Переход к просмотру карточек темы - Фильтры и поиск ### 2. **Игры (GamesPage)** - Список доступных игр (`GameDto`) - Кнопки запуска игр - Интеграция с WebView играми - Прогресс по играм ### 3. **Профиль (ProfilePage)** - Статистика изучения - Кнопка входа/выхода - Настройки (тема, звук, etc) - Промокоды и подписка --- ## 📦 State Management (yx_state) ### State Managers: #### 1. **ThemeStateManager** (в AppScope) ```dart class ThemeState { final ThemeMode mode; const ThemeState(this.mode); } class ThemeStateManager extends StateManager { ThemeStateManager(SharedPreferences prefs) : super(ThemeState(_loadFromPrefs(prefs))); void toggleTheme() => handle((emit) async { final newMode = state.mode == ThemeMode.light ? ThemeMode.dark : ThemeMode.light; emit(ThemeState(newMode)); await _saveToPrefs(newMode); }); } ``` #### 2. **UserStateManager** (в UserScope) ```dart @freezed class UserState with _$UserState { const factory UserState.guest() = _Guest; const factory UserState.authenticated({ required UserDto user, }) = _Authenticated; const factory UserState.loading() = _Loading; } class UserStateManager extends StateManager { UserStateManager() : super(const UserState.guest()); void setUser(UserDto user) => handle((emit) async { emit(UserState.authenticated(user: user)); }); void logout() => handle((emit) async { emit(const UserState.guest()); }); } ``` #### 3. **PacksStateManager** (в UserScope) ```dart @freezed class PacksState with _$PacksState { const factory PacksState.loading() = _Loading; const factory PacksState.loaded(List packs) = _Loaded; const factory PacksState.error(String message) = _Error; } class PacksStateManager extends StateManager { final HttpRepositoryV2 _repository; PacksStateManager(this._repository) : super(const PacksState.loading()); Future loadPacks() => handle((emit) async { emit(const PacksState.loading()); try { final packs = await _repository.getPacks(); emit(PacksState.loaded(packs)); } catch (e) { emit(PacksState.error(e.toString())); } }); } ``` #### 4. **GamesStateManager** (в UserScope) ```dart @freezed class GamesState with _$GamesState { const factory GamesState.loading() = _Loading; const factory GamesState.loaded(List games) = _Loaded; const factory GamesState.error(String message) = _Error; } ``` --- ## 🔐 Авторизация ### Процесс авторизации: #### **Гостевой режим:** ```dart // При запуске приложения void main() async { final appScopeHolder = AppScopeHolder(); await appScopeHolder.create(); // Создаем UserScope для гостя сразу final appScope = appScopeHolder.scope!; await appScope.userScopeHolder.create(); runApp(App(appScopeHolder: appScopeHolder)); } ``` #### **Google авторизация:** ```dart class AuthService { final GoogleSignIn _googleSignIn; final HttpRepositoryV2 _repository; Future loginWithGoogle() async { final account = await _googleSignIn.signIn(); final auth = await account.authentication; // Отправляем токен на backend final (user, token) = await _repository.createOrGetUser( auth.idToken!, ExternalIdType.google, account.email, account.displayName, ); return user; } } ``` #### **Telegram авторизация:** ```dart class AuthService { Future loginWithTelegram(TelegramWebAppData data) async { final (user, token) = await _repository.createOrGetUser( data.user.id.toString(), ExternalIdType.telegram, 'no-email-tg', data.user.username, ); return user; } } ``` ### Переключение между гостем и авторизованным: ```dart // В AuthPage после успешной авторизации final user = await authService.loginWithGoogle(); userScopeHolder.scope!.userStateManager.setUser(user); // При выходе await userStateManager.logout(); // UserScope НЕ удаляется, просто переходит в guest режим ``` --- ## 🚦 Навигация (go_router) ### Структура роутов: ```dart final router = GoRouter( initialLocation: '/home', routes: [ ShellRoute( builder: (context, state, child) => MainShell(child: child), routes: [ GoRoute( path: '/home', builder: (context, state) => const HomePage(), ), GoRoute( path: '/games', builder: (context, state) => const GamesPage(), ), GoRoute( path: '/profile', builder: (context, state) => const ProfilePage(), ), ], ), GoRoute( path: '/auth', builder: (context, state) => const AuthPage(), ), GoRoute( path: '/pack/:id', builder: (context, state) => PackDetailsPage( packId: state.pathParameters['id']!, ), ), GoRoute( path: '/test/:packId', builder: (context, state) => TestPage( packId: state.pathParameters['packId']!, ), ), ], ); ``` ### MainShell - Bottom Navigation: ```dart class MainShell extends StatelessWidget { final Widget child; @override Widget build(BuildContext context) { return Scaffold( body: child, bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Темы'), BottomNavigationBarItem(icon: Icon(Icons.games), label: 'Игры'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Профиль'), ], onTap: (index) { switch (index) { case 0: context.go('/home'); case 1: context.go('/games'); case 2: context.go('/profile'); } }, ), ); } } ``` --- ## 📚 Зависимости (pubspec.yaml) ### Обновленный pubspec.yaml: ```yaml dependencies: flutter: sdk: flutter # YX Framework yx_scope: ^1.1.2 yx_scope_flutter: ^1.1.2 yx_state: ^1.0.0 yx_state_flutter: ^1.0.0 # Общие пакеты проекта mnemo_cards_common: path: ../mnemo_cards_common mnemo_cards_frontend_common: path: ../mnemo_cards_frontend_common # Роутинг go_router: ^14.2.0 # HTTP dio: ^5.3.3 # State Management helpers rxdart: ^0.28.0 # Firebase firebase_core: ^3.3.0 firebase_auth: ^5.3.1 firebase_analytics: ^11.2.1 firebase_crashlytics: ^4.0.4 firebase_remote_config: ^5.4.7 # Авторизация google_sign_in: ^6.2.1 # telegram_web_app: ^0.3.1 (если нужно) # Code Generation freezed_annotation: ^2.4.1 json_annotation: ^4.7.0 # Storage shared_preferences: ^2.2.3 flutter_secure_storage: ^9.2.2 # UI flutter_screenutil: ^5.9.0 shimmer: ^3.0.0 auto_size_text: ^3.0.0 fl_chart: ^0.68.0 # Utils universal_image: ^1.0.10 url_launcher: ^6.2.6 package_info_plus: ^8.0.0 dev_dependencies: flutter_test: sdk: flutter # Code Generation build_runner: ^2.4.13 freezed: ^2.4.5 json_serializable: ^6.8.0 # Linting flutter_lints: ^6.0.0 yx_scope_linter: ^1.1.0 custom_lint: ^0.5.3 ``` --- ## 📁 Структура проекта ``` lib/ ├── main.dart # Точка входа ├── app.dart # Главный виджет приложения │ ├── di/ # Dependency Injection (yx_scope) │ ├── app_scope/ │ │ ├── app_scope_container.dart # Контейнер AppScope │ │ ├── app_scope_holder.dart # Холдер AppScope │ │ ├── app_scope.dart # Интерфейс AppScope │ │ └── modules/ │ │ ├── auth_module.dart # Модуль авторизации │ │ ├── router_module.dart # Модуль роутинга │ │ ├── analytics_module.dart # Модуль аналитики │ │ └── storage_module.dart # Модуль хранилища │ │ │ └── user_scope/ │ ├── user_scope_container.dart # Контейнер UserScope │ ├── user_scope_holder.dart # Холдер UserScope │ ├── user_scope.dart # Интерфейс UserScope │ └── modules/ │ ├── packs_module.dart # Модуль тем/карточек │ ├── games_module.dart # Модуль игр │ └── profile_module.dart # Модуль профиля │ ├── domain/ # Бизнес-логика │ ├── models/ # Модели (из mnemo_cards_common) │ ├── services/ │ │ ├── auth_service.dart # Сервис авторизации │ │ ├── http_repository_v2.dart # HTTP клиент (Bearer OAuth2) │ │ ├── pack_manager.dart # Менеджер тем │ │ ├── games_manager.dart # Менеджер игр │ │ └── statistics_service.dart # Сервис статистики │ │ │ └── state/ # State Managers (yx_state) │ ├── theme_state_manager.dart │ ├── user_state_manager.dart │ ├── packs_state_manager.dart │ └── games_state_manager.dart │ ├── presentation/ # UI слой │ ├── router/ │ │ └── app_router.dart # Конфигурация go_router │ │ │ ├── pages/ │ │ ├── home/ │ │ │ ├── home_page.dart # Страница "Темы" │ │ │ └── widgets/ │ │ │ │ │ ├── games/ │ │ │ ├── games_page.dart # Страница "Игры" │ │ │ └── widgets/ │ │ │ │ │ ├── profile/ │ │ │ ├── profile_page.dart # Страница "Профиль" │ │ │ └── widgets/ │ │ │ │ │ ├── auth/ │ │ │ └── auth_page.dart # Страница авторизации │ │ │ │ │ ├── pack_details/ │ │ │ └── pack_details_page.dart # Детали темы │ │ │ │ │ └── test/ │ │ └── test_page.dart # Страница теста │ │ │ ├── widgets/ # Общие виджеты │ │ ├── app_bar.dart │ │ ├── bottom_nav_bar.dart │ │ ├── pack_card.dart │ │ ├── game_card.dart │ │ └── statistics_chart.dart │ │ │ └── theme/ │ └── app_theme.dart # Темы приложения │ └── utils/ # Утилиты ├── logger.dart ├── extensions.dart └── constants.dart ``` --- ## 🔄 Жизненный цикл приложения ### 1. Запуск приложения: ```dart void main() async { WidgetsFlutterBinding.ensureInitialized(); // Инициализация Firebase await Firebase.initializeApp(); // Создание AppScope final appScopeHolder = AppScopeHolder(); await appScopeHolder.create(); // Создание UserScope для гостя final appScope = appScopeHolder.scope!; await appScope.userScopeHolder.create(); runApp(App(appScopeHolder: appScopeHolder)); } ``` ### 2. Структура App виджета: ```dart class App extends StatelessWidget { final AppScopeHolder appScopeHolder; const App({required this.appScopeHolder, super.key}); @override Widget build(BuildContext context) { return ScopeProvider( holder: appScopeHolder, child: ScopeBuilder.withPlaceholder( builder: (context, appScope) { // Вложенный ScopeProvider для UserScope return ScopeProvider( holder: appScope.userScopeHolder, child: ScopeBuilder.withPlaceholder( builder: (context, userScope) { return StateManagerBuilder( stateManager: appScope.themeManager, builder: (context, themeState) { return MaterialApp.router( routerConfig: appScope.router, theme: AppTheme.light, darkTheme: AppTheme.dark, themeMode: themeState.mode, ); }, ); }, placeholder: const Center( child: CircularProgressIndicator(), ), ), ); }, placeholder: const Center( child: CircularProgressIndicator(), ), ), ); } } ``` ### 3. Авторизация: ```dart // В AuthPage class AuthPage extends StatelessWidget { @override Widget build(BuildContext context) { return ScopeBuilder( builder: (context, appScope) { return ScopeBuilder( builder: (context, userScope) { return Column( children: [ ElevatedButton( onPressed: () async { // Логин через Google final user = await appScope.authService .loginWithGoogle(); // Обновляем состояние пользователя userScope.userStateManager.setUser(user); // Роутер автоматически перенаправит на home context.go('/home'); }, child: Text('Войти через Google'), ), ElevatedButton( onPressed: () async { // Логин через Telegram final user = await appScope.authService .loginWithTelegram(); userScope.userStateManager.setUser(user); context.go('/home'); }, child: Text('Войти через Telegram'), ), TextButton( onPressed: () { // Войти как гость (UserScope уже создан) context.go('/home'); }, child: Text('Продолжить как гость'), ), ], ); }, ); }, ); } } ``` ### 4. Использование в страницах: ```dart // HomePage class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return ScopeBuilder( builder: (context, userScope) { return StateManagerBuilder( stateManager: userScope.packsStateManager, builder: (context, state) { return state.when( loading: () => CircularProgressIndicator(), loaded: (packs) => ListView.builder( itemCount: packs.length, itemBuilder: (context, index) { return PackCard(pack: packs[index]); }, ), error: (message) => Text('Error: $message'), ); }, ); }, ); } } ``` --- ## 🎯 Этапы разработки ### **Этап 1: Основа (1-2 дня)** - [x] Создать структуру проекта - [ ] Настроить pubspec.yaml с зависимостями - [ ] Создать AppScope (контейнер, холдер, интерфейс) - [ ] Создать UserScope (контейнер, холдер, интерфейс) - [ ] Настроить Firebase - [ ] Реализовать ThemeStateManager - [ ] Настроить go_router с базовыми роутами - [ ] Создать главный App виджет с ScopeProvider'ами ### **Этап 2: Авторизация (1-2 дня)** - [ ] Реализовать AuthService (Google, Telegram) - [ ] Создать UserStateManager - [ ] Реализовать HttpRepositoryV2 с токенами - [ ] Создать AuthPage - [ ] Реализовать гостевой режим - [ ] Настроить роутинг для auth/guest ### **Этап 3: Темы (2-3 дня)** - [ ] Создать PacksModule в UserScope - [ ] Реализовать PacksStateManager - [ ] Создать PackManager - [ ] Реализовать HomePage с списком тем - [ ] Создать PackDetailsPage - [ ] Реализовать TestPage - [ ] Добавить избранное ### **Этап 4: Игры (1-2 дня)** - [ ] Создать GamesModule в UserScope - [ ] Реализовать GamesStateManager - [ ] Создать GamesManager - [ ] Реализовать GamesPage - [ ] Интегрировать WebView для игр ### **Этап 5: Профиль (1-2 дня)** - [ ] Создать ProfileModule в UserScope - [ ] Реализовать StatisticsService - [ ] Создать ProfilePage - [ ] Добавить графики статистики (fl_chart) - [ ] Реализовать настройки - [ ] Добавить промокоды и подписку ### **Этап 6: Полировка (1-2 дня)** - [ ] Добавить анимации и переходы - [ ] Оптимизировать производительность - [ ] Добавить обработку ошибок - [ ] Добавить loading states - [ ] Протестировать все flow'ы - [ ] Адаптивная верстка для разных экранов ### **Этап 7: Тестирование и деплой (1 день)** - [ ] Тестирование авторизации - [ ] Тестирование всех страниц - [ ] Проверка работы с backend - [ ] Build для production - [ ] Деплой на хостинг --- ## 📝 Примечания ### Преимущества yx_scope: - ✅ Compile-safe доступ к зависимостям - ✅ Четкий жизненный цикл скоупов - ✅ Отсутствие Service Locator паттерна - ✅ Простая иерархия и изоляция - ✅ Flutter-friendly интеграция ### Преимущества yx_state: - ✅ Простой и понятный API - ✅ Встроенная обработка ошибок - ✅ Интеграция с Flutter виджетами - ✅ Поддержка rxdart transformers ### Важные моменты: - UserScope создается сразу при запуске (для гостя) - UserScope НЕ удаляется при logout, только меняется состояние - AuthService находится в AppScope (доступен всегда) - HttpRepositoryV2 в UserScope получает токен из AuthService - Все State Managers используют freezed для типобезопасности --- ## 🔗 Ссылки на документацию - [yx_scope](../packages/yx/city-services-pub/yx_scope/packages/yx_scope/README.md) - [yx_scope_flutter](../packages/yx/city-services-pub/yx_scope/packages/yx_scope_flutter/README.md) - [yx_state](../packages/yx/city-services-pub/yx_state/packages/yx_state/README.md) - [go_router](https://pub.dev/packages/go_router) - [freezed](https://pub.dev/packages/freezed) --- **Общая оценка времени разработки: 8-14 дней** Готов к началу разработки! 🚀