diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 0000000..6ca7e36 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,64 @@ +# Правила для Cursor AI + +## Соглашение о коммитах + +При генерации сообщений коммитов ВСЕГДА используй следующий формат: +`<тип>(<контекст1>,<контекст2>,...): <короткое описание>` + +Для Pull Request формат: +`<тип>(<контекст1>,<контекст2>,...): <короткое описание>` + +Где: +- `<тип>` - тип коммита (см. ниже) +- `<контекст>` - модули/компоненты, которые изменяются (можно указать несколько через запятую) +- `<короткое описание>` - краткое описание изменений на русском языке + +### Типы коммитов согласно convention: + +- **feat** - новая функция +- **fix** - исправление ошибок +- **refactor** - изменение кода, которое не исправляет ошибку и не добавляет функции (рефакторинг кода) +- **build** - изменения, влияющие на систему сборки или внешние зависимости (примеры областей: android, ios, linux и так далее) +- **docs** - изменения только в документации +- **chore** - добавление/обновление/настройка инструментов и библиотек (пример: pubspec.yaml) +- **test** - добавление недостающих тестов или исправление существующих тестов +- **ci** - изменения в файлах конфигурации и скриптах CI (примеры областей: папка CI) + +### Контекст (scope): + +Указывай модуль или компонент, который изменяется. Можно указать несколько через запятую: +- `app` - основное приложение +- `di` - dependency injection +- `auth` - аутентификация +- `api` - API endpoints +- `db` - база данных +- `config` - конфигурация +- `i18n` - интернационализация +- `scripts` - скрипты +- `pubspec` - зависимости проекта +- `android`, `ios`, `linux` - платформы +- другие модули проекта + +### Примеры правильных коммитов: + +- `feat(app,di,auth): Добавить локальный репозиторий` +- `fix(api): Исправить валидацию запросов` +- `docs(i18n): Обновить руководство по генерации` +- `refactor(handler): Оптимизировать обработку запросов` +- `test(security): Добавить тесты для rate limiter` +- `chore(pubspec): Обновить зависимости` + +## Язык + +- Всегда отвечай на русском языке +- Коммиты пиши на русском языке +- Документацию веди на русском языке +- Язык описания PR - Русский + +## Стиль кода + +- Следуй Dart/Flutter conventions +- Используй осмысленные имена переменных и функций +- Добавляй комментарии к публичным функциям +- Группируй импорты (стандартные, внешние, внутренние) + diff --git a/README.md b/README.md index 64f7d4b..3fa1b83 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # 🚀 Friflex Flutter Starter - Корпоративный шаблон -![Flutter](https://img.shields.io/badge/Flutter-3.35.5+-02569B?style=for-the-badge&logo=flutter&logoColor=white) -![Dart](https://img.shields.io/badge/Dart-3.9.2+-0175C2?style=for-the-badge&logo=dart&logoColor=white) +![Flutter](https://img.shields.io/badge/Flutter-3.38.1+-02569B?style=for-the-badge&logo=flutter&logoColor=white) +![Dart](https://img.shields.io/badge/Dart-3.10.0+-0175C2?style=for-the-badge&logo=dart&logoColor=white) ![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge) Корпоративный стартовый шаблон для разработки масштабируемых Flutter-приложений @@ -24,6 +24,7 @@ - 🌍 Поддержка интернационализации - 🎨 UI Kit и система токенов дизайна - 🔍 Инструменты отладки и мониторинга +- ⚡ Современный Dart 3.10+ с dot shorthands ## 🎯 Для чего нужен стартер @@ -90,20 +91,27 @@ features/ | Категория | Библиотека | Версия | Описание | |-----------|------------|--------|----------| -| 🧭 **Навигация** | [go_router](https://pub.dev/packages/go_router) | `16.2.4` | Декларативный роутинг | +| 🧭 **Навигация** | [go_router](https://pub.dev/packages/go_router) | `17.0.0` | Декларативный роутинг | | 🔄 **State Management** | [flutter_bloc](https://pub.dev/packages/flutter_bloc) | `9.1.1` | Управление состоянием | | 💉 **DI** | Custom InheritedWidget | - | Внедрение зависимостей | | 🎨 **Resources** | [flutter_gen](https://pub.dev/packages/flutter_gen) | `5.12.0` | Генерация ресурсов | | 🌐 **HTTP** | [dio](https://pub.dev/packages/dio) | `5.9.0` | HTTP клиент | +| 🎨 **SVG** | [flutter_svg](https://pub.dev/packages/flutter_svg) | `2.2.2` | Поддержка SVG | +| 🎬 **Animation** | [lottie](https://pub.dev/packages/lottie) | `3.3.2` | Анимации Lottie | | 🔒 **Secure Storage** | [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) | - | Защищенное хранилище | -| 📊 **Logging** | [talker](https://pub.dev/packages/talker_flutter) | `5.0.1` | Логирование и отладка | +| 📊 **Logging** | [talker](https://pub.dev/packages/talker_flutter) | `5.0.2` | Логирование и отладка | +| 🎨 **Theme** | [theme_tailor](https://pub.dev/packages/theme_tailor) | `3.1.1` | Генерация тем | +| ⚙️ **Environment** | [envied](https://pub.dev/packages/envied) | `1.3.1` | Управление переменными окружения | ### 🔧 Инструменты разработки -- **📝 Линтинг**: корпоративные правила кода -- **🏗️ Code Generation**: `build_runner` для генерации кода -- **🌍 Локализация**: `flutter_localizations` + `intl` -- **⚙️ Окружения**: `envied` для управления переменными +| Инструмент | Версия | Описание | +|-----------|--------|----------| +| **📝 Линтинг** | `flutter_lints: 6.0.0` | Корпоративные правила кода | +| **🏗️ Code Generation** | `build_runner: 2.10.3` | Генерация кода | +| **🌍 Локализация** | `intl: 0.20.2` | Интернационализация | +| **⚙️ Environment** | `envied: 1.3.1` + `envied_generator: 1.3.1` | Управление переменными окружения | +| **🎨 Theme Generator** | `theme_tailor: 3.1.1` | Генерация тем | ## 🗂️ Структура проекта diff --git a/lib/app/app.dart b/lib/app/app.dart index 289f1e3..3bb931f 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -10,7 +10,6 @@ import 'package:friflex_starter/features/error/error_screen.dart'; import 'package:friflex_starter/features/splash/splash_screen.dart'; import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart'; import 'package:friflex_starter/features/update/update_routes.dart'; -import 'package:friflex_starter/features/update/update_type.dart'; import 'package:friflex_starter/l10n/gen/app_localizations.dart'; import 'package:friflex_starter/l10n/localization_notifier.dart'; import 'package:go_router/go_router.dart'; @@ -124,7 +123,7 @@ class _App extends StatelessWidget { child: BlocConsumer( listener: (context, state) { if (state is UpdateSuccessState && - state.updateInfo.updateType == UpdateType.hard && + state.updateInfo.updateType == .hard && context.mounted) { router.goNamed(UpdateRoutes.hardUpdateScreenName); } diff --git a/lib/app/app_config/app_config.dart b/lib/app/app_config/app_config.dart index 63549bf..f26056e 100644 --- a/lib/app/app_config/app_config.dart +++ b/lib/app/app_config/app_config.dart @@ -41,7 +41,7 @@ class AppConfigDev implements IAppConfig { AppConfigDev(); @override - AppEnv get env => AppEnv.dev; + AppEnv get env => .dev; @override String get name => 'AppConfigDev'; @@ -67,7 +67,7 @@ class AppConfigProd implements IAppConfig { AppConfigProd(); @override - AppEnv get env => AppEnv.prod; + AppEnv get env => .prod; @override String get name => 'AppConfigProd'; @@ -93,7 +93,7 @@ class AppConfigStage implements IAppConfig { AppConfigStage(); @override - AppEnv get env => AppEnv.stage; + AppEnv get env => .stage; @override String get name => 'AppConfigStage'; diff --git a/lib/app/theme/app_colors_scheme.dart b/lib/app/theme/app_colors_scheme.dart index 5b563f7..4b3c653 100644 --- a/lib/app/theme/app_colors_scheme.dart +++ b/lib/app/theme/app_colors_scheme.dart @@ -50,7 +50,7 @@ class AppColors extends ThemeExtension with _$AppColorsTailorMixin { testColor: Colors.red, errorSnackbarBackground: Color(0xFFD24720), successSnackbarBackground: Color(0xFF6FB62C), - infoSnackbarBackground: Color.fromARGB(255, 220, 108, 77), + infoSnackbarBackground: .fromARGB(255, 220, 108, 77), itemTextColor: Color(0xFFFAF3EB), ); @@ -59,7 +59,7 @@ class AppColors extends ThemeExtension with _$AppColorsTailorMixin { testColor: Colors.green, errorSnackbarBackground: Color(0xFF638B8B), successSnackbarBackground: Color(0xFF93C499), - infoSnackbarBackground: Color.fromARGB(255, 35, 147, 178), + infoSnackbarBackground: .fromARGB(255, 35, 147, 178), itemTextColor: Colors.white, ); } diff --git a/lib/app/ui_kit/app_snackbar.dart b/lib/app/ui_kit/app_snackbar.dart index 2fbf425..adfa2f7 100644 --- a/lib/app/ui_kit/app_snackbar.dart +++ b/lib/app/ui_kit/app_snackbar.dart @@ -63,7 +63,7 @@ class AppSnackBar extends StatefulWidget { _show( context: context, message: message, - type: TypeSnackBar.error, + type: .error, displayDuration: displayDuration, ); } @@ -81,7 +81,7 @@ class AppSnackBar extends StatefulWidget { _show( context: context, message: message, - type: TypeSnackBar.info, + type: .info, displayDuration: displayDuration, ); } @@ -99,7 +99,7 @@ class AppSnackBar extends StatefulWidget { _show( context: context, message: message, - type: TypeSnackBar.success, + type: .success, displayDuration: displayDuration, ); } @@ -266,9 +266,9 @@ class _AppSnackBarState extends State /// [TypeSnackBar.error] - цвет ошибки Color _getBackgroundColor(TypeSnackBar type) { return switch (type) { - TypeSnackBar.success => context.appColors.successSnackbarBackground, - TypeSnackBar.error => context.appColors.errorSnackbarBackground, - TypeSnackBar.info => context.appColors.infoSnackbarBackground, + .success => context.appColors.successSnackbarBackground, + .error => context.appColors.errorSnackbarBackground, + .info => context.appColors.infoSnackbarBackground, }; } } @@ -290,17 +290,17 @@ class _Icon extends StatelessWidget { @override Widget build(BuildContext context) { return switch (type) { - TypeSnackBar.success => const Icon( + .success => const Icon( Icons.check_circle, color: Colors.white, size: 32, ), - TypeSnackBar.error => const Icon( + .error => const Icon( Icons.error, color: Colors.white, size: 32, ), - TypeSnackBar.info => const Icon( + .info => const Icon( Icons.info, color: Colors.white, size: 32, diff --git a/lib/di/di_container.dart b/lib/di/di_container.dart index 08c49de..f4fcdb0 100644 --- a/lib/di/di_container.dart +++ b/lib/di/di_container.dart @@ -41,9 +41,9 @@ final class DiContainer { }) async { // Инициализация конфигурации приложения appConfig = switch (env) { - AppEnv.dev => AppConfigDev(), - AppEnv.prod => AppConfigProd(), - AppEnv.stage => AppConfigStage(), + .dev => AppConfigDev(), + .prod => AppConfigProd(), + .stage => AppConfigStage(), }; // Инициализация HTTP клиента diff --git a/lib/di/di_repositories.dart b/lib/di/di_repositories.dart index bed3e52..dcbc2b9 100644 --- a/lib/di/di_repositories.dart +++ b/lib/di/di_repositories.dart @@ -155,9 +155,9 @@ final class DiRepositories { }) { try { final repo = switch (environment) { - AppEnv.dev => mockFactory(), - AppEnv.prod => mainFactory(), - AppEnv.stage => + .dev => mockFactory(), + .prod => mainFactory(), + .stage => _mockReposToSwitch.contains(T) ? mockFactory() : mainFactory(), }; diff --git a/lib/features/debug/screens/components_screen.dart b/lib/features/debug/screens/components_screen.dart index 3659d99..a792e9c 100644 --- a/lib/features/debug/screens/components_screen.dart +++ b/lib/features/debug/screens/components_screen.dart @@ -6,7 +6,6 @@ import 'package:friflex_starter/app/ui_kit/app_snackbar.dart'; import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart'; import 'package:friflex_starter/features/update/presentation/components/soft_modal_sheet.dart'; import 'package:friflex_starter/features/update/update_routes.dart'; -import 'package:friflex_starter/features/update/update_type.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; @@ -79,7 +78,7 @@ class _ComponentsScreenState extends State { onPressed: () { final updateCubitState = context.read().state; if (updateCubitState is UpdateSuccessState && - updateCubitState.updateInfo.updateType == UpdateType.soft) { + updateCubitState.updateInfo.updateType == .soft) { unawaited( SoftUpdateModal.show( context, diff --git a/lib/features/root/root_screen.dart b/lib/features/root/root_screen.dart index 6b2e729..48e8f9d 100644 --- a/lib/features/root/root_screen.dart +++ b/lib/features/root/root_screen.dart @@ -3,11 +3,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:friflex_starter/app/app_context_ext.dart'; -import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/features/debug/debug_routes.dart'; import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart'; import 'package:friflex_starter/features/update/presentation/components/soft_modal_sheet.dart'; -import 'package:friflex_starter/features/update/update_type.dart'; import 'package:go_router/go_router.dart'; /// {@template root_screen} @@ -47,7 +45,7 @@ class _RootScreenState extends State { // Проверяем только состояние успеха с доступной информацией об обновлении if (updateState is UpdateSuccessState && - updateState.updateInfo.updateType == UpdateType.soft) { + updateState.updateInfo.updateType == .soft) { unawaited( SoftUpdateModal.show( context, @@ -64,7 +62,7 @@ class _RootScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( - floatingActionButton: context.di.env != AppEnv.prod + floatingActionButton: context.di.env != .prod ? FloatingActionButton( child: const Icon(Icons.bug_report), onPressed: () { diff --git a/lib/features/update/data/repository/update_mock_repository.dart b/lib/features/update/data/repository/update_mock_repository.dart index 5a279de..8251876 100644 --- a/lib/features/update/data/repository/update_mock_repository.dart +++ b/lib/features/update/data/repository/update_mock_repository.dart @@ -1,12 +1,11 @@ import 'package:friflex_starter/features/update/domain/entity/update_entity.dart'; import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart'; -import 'package:friflex_starter/features/update/update_type.dart'; /// Мок обновления обязательное, можно использовать для тестирования const mockHardUpdateEntity = UpdateEntity( availableVersion: '2.0.0', updateUrl: 'https://example.com/update', - updateType: UpdateType.hard, + updateType: .hard, whatIsNew: 'Добавлены новые функции и исправлены ошибки.', ); @@ -14,7 +13,7 @@ const mockHardUpdateEntity = UpdateEntity( const mockSoftUpdateEntity = UpdateEntity( availableVersion: '2.0.0', updateUrl: 'https://example.com/update', - updateType: UpdateType.soft, + updateType: .soft, whatIsNew: 'Добавлены новые функции и исправлены ошибки.', ); diff --git a/lib/main.dart b/lib/main.dart index 6e22de4..07f2cff 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/runner/app_runner.dart'; -void main() => AppRunner(AppEnv.prod).run(); +void main() => AppRunner(.prod).run(); diff --git a/lib/targets/dev.dart b/lib/targets/dev.dart index 95804b7..708320e 100644 --- a/lib/targets/dev.dart +++ b/lib/targets/dev.dart @@ -1,4 +1,3 @@ -import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/runner/app_runner.dart'; -void main() => AppRunner(AppEnv.dev).run(); +void main() => AppRunner(.dev).run(); diff --git a/lib/targets/prod.dart b/lib/targets/prod.dart index 6e22de4..07f2cff 100644 --- a/lib/targets/prod.dart +++ b/lib/targets/prod.dart @@ -1,4 +1,3 @@ -import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/runner/app_runner.dart'; -void main() => AppRunner(AppEnv.prod).run(); +void main() => AppRunner(.prod).run(); diff --git a/lib/targets/stage.dart b/lib/targets/stage.dart index c943134..72eddca 100644 --- a/lib/targets/stage.dart +++ b/lib/targets/stage.dart @@ -1,4 +1,3 @@ -import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/runner/app_runner.dart'; -void main() => AppRunner(AppEnv.stage).run(); +void main() => AppRunner(.stage).run(); diff --git a/pubspec.lock b/pubspec.lock index 21ced00..23d06b9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1273,5 +1273,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.2 <4.0.0" - flutter: ">=3.35.5" + dart: ">=3.10.0 <4.0.0" + flutter: ">=3.38.1" diff --git a/pubspec.yaml b/pubspec.yaml index a6139b0..63d1d01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,8 +6,8 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 0.0.1+1 environment: - sdk: ^3.9.2 - flutter: ">=3.35.5" + sdk: ">=3.10.0 <4.0.0" + flutter: ">=3.38.1" dependencies: flutter: