Тестовое приложение с реализацией многослойной архитектуры, флоу онбординга и эмуляцией подписки.
- Framework: Flutter
- State Management: Cubit (flutter_bloc)
- Local Storage: SharedPreferences (через паттерн DAO)
- Architecture: Clean Architecture (Domain, Data, Presentation)
Приложение разделено на три независимых слоя, что обеспечивает легкость тестирования и замены библиотек:
- Repositories: Определение интерфейсов для работы с настройками и подпиской.
- Слой полностью независим от внешних зависимостей и Flutter.
- DAO (Data Access Object): Контракт для низкоуровневых операций с хранилищем.
- Sources: Конкретная реализация DAO с использованием
SharedPreferences. - Repositories Implementation: Реализует интерфейсы домена, используя DAO.
- Cubit: Управляет состоянием авторизации и логикой переключения экранов.
- Screens:
OnboardingScreen— 2 страницы с индикаторами.PaywallScreen— экран выбора тарифа с интерактивными карточками.MainScreen— целевой контент для подписчика.
- Widgets: Вынесенные атомарные компоненты (
SubscriptionOption,PageIndicatorDot).
lib/
├── data/
│ ├── repositories/ # Реализация репозиториев
│ └── sources/ # DAO интерфейсы и реализации (SP)
├── domain/
│ └── repositories/ # Абстрактные контракты (бизнес-логика)
├── presentation/
│ ├── cubit/ # State management (AuthCubit)
│ ├── screens/ # Экранные виджеты
│ └── widgets/ # Переиспользуемые UI компоненты
└── main.dart # Сборка DI и точка входа
В рамках данного тестового задания реализована базовая структура Clean Architecture. При развитии проекта в сторону промышленного решения (production-ready) я бы внедрил следующие улучшения:
- Проблема: Сейчас навигация реализована через декларативный
Switch-caseвRootScreen. Это затрудняет масштабирование и не поддерживает системные кнопки "Назад". - Решение: Внедрение GoRouter.
- Это позволит использовать именованные роуты (например,
/paywall,/main). - Добавит поддержку Deep Links (например, переход по ссылке
myapp://paywall?promo=DEV2024сразу откроет экран оплаты с примененным купоном). - Централизованный
Redirectбудет автоматически проверять статус подписки в Cubit перед открытием защищенных экранов.
- Это позволит использовать именованные роуты (например,
- Проблема: Сейчас зависимости (DAO, Repository, Cubit) пробрасываются вручную в
main.dart, что при росте проекта приведет к "спагетти-инициализации". - Решение: Использование GetIt совместно с Injectable. Это позволит автоматически регистрировать зависимости и получать их в любой части приложения без жесткой привязки.
- Проблема: Ручное переопределение
==,hashCodeи создание методовcopyWithдля состояний Cubit занимает много времени и чревато ошибками. - Решение: Внедрение Freezed. Это обеспечит:
- Гарантированную неизменяемость (immutability) состояний.
- Удобный паттерн-матчинг (метод
.when()) для отрисовки UI в зависимости от состояния.
- Проблема: Цвета и стили текста сейчас прописаны "инлайново" в виджетах.
- Решение: Вынос всех констант (цвета, отступы, шрифты) в отдельный класс
AppThemeи использованиеTheme.of(context). Это позволит легко реализовать темную тему и поддерживать единый дизайн-код.
- Проблема: Логика переключения экранов и корректность работы DAO не подтверждены автоматическими тестами.
- Решение: * Использование mockito или mocktail для создания моков
SettingsDao.- Написание bloc_test для проверки всех переходов состояний в
AuthCubit(например, проверка, что послеpurchaseSubscriptionсостояние меняется наmain). - Unit-тесты
- Написание bloc_test для проверки всех переходов состояний в