28 Commits

Author SHA1 Message Date
Artem Luzin m
4260c7cc65 fix(app): remove unused parameter from _AppInternal constructor 2025-11-24 17:40:31 +07:00
Artem Luzin m
2595692107 Merge branches 'feat/перенести-роутер-ближе-к-месту-инициализации' and 'feat/перенести-роутер-ближе-к-месту-инициализации' of https://github.com/smmarty/friflex_starter into feat/перенести-роутер-ближе-к-месту-инициализации 2025-11-24 16:38:15 +07:00
Artem Luzin m
c86b4cc0bc feat(app): add mockRouter to _AppInternal for improved testing 2025-11-24 16:38:11 +07:00
Artem Luzin
d46c829959 Update lib/app/app.dart
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 16:23:42 +07:00
Artem Luzin m
84e5f5e869 feat/перенести роутер в место инициализации 2025-11-24 16:18:36 +07:00
Yuri Petrov
fcf5048038 chore(pubspec,app_services): обновить зависимости и настройки линтинга (#39)
Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-11-17 13:21:38 +03:00
Yuri Petrov
454f3b7929 chore(app): обновление flutter и пакетов (#38)
* chore(pubspec,di): Обновить версии SDK и исправить использование AppEnv

* chore(readme): Обновить версии Flutter и Dart, добавить новые библиотеки

---------

Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-11-17 12:49:07 +03:00
Yuri Petrov
d9c45eb57e fix(linter): улучшение правил анализа и линтинга (#37)
* fix(linter): улучшение правил анализа и линтинга, добавление исключений и ошибок

* fix(tasks): исправления по ревью

---------

Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-11-17 11:51:42 +03:00
Yuri Petrov
55de1ad8d1 github(copilot): create copilot-instructions.md 2025-10-24 10:39:56 +03:00
Yuri Petrov
42b7c34d1a fix(di): улучшить обработку ошибок при инициализации репозиториев (#34)
* fix(di): улучшить обработку ошибок при инициализации репозиториев
* fix(di): улучшить обработку ошибок при инициализации репозиториев и сервисов

---------

Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-10-23 12:00:09 +03:00
sitkat
5d93fb1713 docs(tools): Добавлена гибкость для документирования классов в RFC-documentation (#35) 2025-10-22 13:28:08 +03:00
Yuri Petrov
0351c768c4 chore(pubspec): FLUTTER-151 обновление flutter и пакетов (#33)
* chore(deps): update dependencies and SDK versions in pubspec.yaml and related files
* chore(readme): update Readme

---------

Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-10-08 15:14:02 +03:00
zl0y4951
dfe30a1762 fix(di): FLUTTER-11: Во FlutterStarter исправить инициализацию репозиториев (#32)
* fix(di): поправил множественный вызов конструктора репозитория

* docs(di): поправил документацию по подмене репозитория
2025-10-06 10:19:27 +03:00
Yuri Petrov
8710792c4b feat(update): добавить модуль управления Hard & Soft обновлений (#30)
1. Реализован интерфейс и репозитории для проверки обновлений.
2. Добавлены состояния и кубит для управления процессом обновления.
3. Созданы UI-компоненты для отображения информации об обновлениях.
4. Обновлен README.md с описанием нового модуля и его интеграции
2025-09-26 08:21:42 +03:00
Yuri Petrov
e1fb99c86f fix(assets): удалить неиспользуемые assets (#29)
Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-09-20 16:10:46 +03:00
Yuri Petrov
c295412f4d docs(readme): обновить структуру и содержание README.md, улучшить оформление (#27)
Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-09-17 12:46:45 +03:00
Yuri Petrov
03e189e46b fix(license): исправить название компании в лицензии (#28)
Co-authored-by: petrovyuri <petrovyuri@example.com>
2025-09-17 12:46:07 +03:00
Yuri Petrov
d491a2f07f fix: FLUTTERSTARTER-3: обновить версии зависимостей и исправить типы в интерфейсе IDebugService (#26)
1. Обновил типы в IDebugService
2. Добавил отправку ошибок в блоке в Observer
2025-08-25 15:04:36 +03:00
Yuri Petrov
ac26aa4a89 feat(linter): FLUTTERSTARTER-2 добавить правило для использования только видимых параметров в документации (#24) 2025-08-08 10:59:17 +03:00
Artem Luzin
d8110c23b3 feat(app): FLUTTERSTARTER-1 Заблокировать изменение шрифта (#23)
* fix(app): добавить глобальный MediaQuery для предсказуемости изменения шрифта
2025-08-08 10:57:17 +03:00
Yuri Petrov
35bceccbf7 feat(app): добавить файл CODEOWNERS для управления правами доступа (#22)
Co-authored-by: PetrovY <y.petrov@friflex.com>
2025-07-18 13:09:39 +03:00
Yuri Petrov
d5a0602c8a docs(readme): Update README.md 2025-06-25 10:53:46 +03:00
PetrovY
63cd544184 docs(readme): Update README.md 2025-06-25 10:41:26 +03:00
Yuri Petrov
8baddfb9f9 feat(rfc): добавить рекомендации по управлению сгенерированными файлами и файлом pubspec.lock (#18)
Co-authored-by: PetrovY <y.petrov@friflex.com>
2025-06-25 10:35:03 +03:00
Yuri Petrov
80a8bfb905 feat(profile): добавить обработку события выхода из профиля и rethrow (#17)
Co-authored-by: PetrovY <y.petrov@friflex.com>
2025-06-25 10:31:37 +03:00
Artem Barkalov
c21fa95b7a chore: Добавить конфигурации запуска для IntelliJ (#15) 2025-06-23 11:01:37 +03:00
PetrovY
b54445be70 feat(app): Добавить сервис геолокации и обновить зависимости 2025-06-23 10:30:24 +03:00
PetrovY
150a85ab24 refactor(app): Обновить зависимости и улучшить документацию для сервисов 2025-06-23 10:20:29 +03:00
93 changed files with 2537 additions and 991 deletions

64
.cursorrules Normal file
View File

@@ -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
- Используй осмысленные имена переменных и функций
- Добавляй комментарии к публичным функциям
- Группируй импорты (стандартные, внешние, внутренние)

63
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,63 @@
Инструкция для AI-агента (ревью Flutter-кода)
Вы — строгий ревьюер кода Flutter-приложения по принципам **Clean Architecture**.
Ваша задача — проводить ревью кода, выявлять ошибки и давать корректные рекомендации.
Bloc и Cubit должны находиться в слое domain.
Контекст (справочно, не включать в ответ):
- Архитектура: три слоя (presentation, domain, data), каждый в своей папке.
- State management: flutter_bloc.
- Навигация: go_router.
- HTTP: dio.
- Анализатор: flutter_lint_rules.
- Feature-first структура: lib/features/<feature_name>/{data,domain,presentation}.
---
## Обзор запроса на вытягивание
1. В начале ответа укажите решение: **«Принять»** или **«Отклонить»**.
2. Укажите оценку: **Оценка: X/100** (X — фактический балл).
3. Кратко и строгим тоном перечислите ключевые проблемы:
- Архитектура (слои `presentation`, `domain`, `data`).
- Принципы **DRY, KISS, SOLID**.
- Разделение ответственности (BLoC, репозитории, DTO, UI).
- Безопасность (`dio`, SSL, валидация данных).
- Кодстайл и соглашения (`flutter_lint_rules`).
- Цикломатическая сложность: избегать чрезмерно сложных функций и классов (оптимально ≤ 10).
4. При необходимости предоставьте исправленный фрагмент кода или улучшенное решение.
5. Ответ должен быть в **Markdown** и полностью на **русском языке**.
---
## Критерии оценки
- **Чистая архитектура**: корректное разделение слоёв, отсутствие Flutter-зависимостей в `domain`.
- **KISS**: минимальная сложность и читаемость решений.
- **DRY**: отсутствие дублирования логики, использование утилит/виджетов.
- **SOLID**: правильная декомпозиция классов и интерфейсы вместо жёстких связей.
- **Безопасность**: корректная работа с API, валидация данных.
- **Кодстайл**: именование, структура файлов и папок.
- **Цикломатическая сложность**: методы и классы должны быть простыми, без избыточных ветвлений.
---
## Пример ответа
**Отклонить**
**Оценка: 58/100**
- Нарушен принцип DRY: HTTP-запрос продублирован в двух репозиториях.
- BLoC перегружен бизнес-логикой (трансформация DTO → Entity должна быть в `data`).
- Domain-слой содержит зависимость от Flutter — это недопустимо.
- Отсутствует обработка ошибок и валидация данных в `dio`.
**Исправленный фрагмент:**
```dart
// Вместо дублирования запроса используем общий DataSource
class UserRemoteDataSource {
final Dio dio;
UserRemoteDataSource(this.dio);
Future<UserEntity> fetchUser(String id) async {
final response = await dio.get('/users/$id');
return UserDto.fromJson(response.data).toEntity();
}
}
```

View File

@@ -1,3 +1,5 @@
<!-- markdownlint-disable MD033 -->
<!-- markdownlint-disable MD041 -->
## Ссылка на задачу или issue (обязательно)
<!--- https://tracker.yandex.ru/XXX -->
@@ -5,6 +7,7 @@
<!--- Напишите здесь какую проблему решают изменения в этом запросе. -->
## Чек лист (обязательно)
- [ ] Код соответствует рекомендациям и требованиям проекта.
- [ ] Проверил анализатор на предмет ошибок и предупреждений.
- [ ] Название ПР соответствует [требованиям проекта](../tools/rfc/RFC-documentation.md).
@@ -12,6 +15,7 @@
- [ ] Добавлены необходимые тесты, если требуется.
## Скриншоты (желательно)
<details>
<summary>Показать</summary>

3
.gitignore vendored
View File

@@ -17,6 +17,9 @@ migrate_working_dir/
*.ipr
*.iws
.idea/
# Хранить конфигурации запуска
!.idea/runConfigurations/
!.idea/runConfigurations/**
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line

6
.idea/runConfigurations/DEV.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="DEV" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/lib/targets/dev.dart" />
<method v="2" />
</configuration>
</component>

6
.idea/runConfigurations/PROD.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="PROD" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/lib/targets/prod.dart" />
<method v="2" />
</configuration>
</component>

6
.idea/runConfigurations/STAGE.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="STAGE" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/lib/targets/stage.dart" />
<method v="2" />
</configuration>
</component>

6
.idea/runConfigurations/main_dart.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
<method />
</configuration>
</component>

View File

@@ -14,5 +14,23 @@
"/// {@macro ${TM_FILENAME_BASE}}"
],
"description": "DartDoc короткая запись macro с именем файла"
}
},
"TODO": {
"prefix": "todo",
"body": [
"// TODO($1): $2"
],
"description": "Create todo"
},
"TRYCATCH": {
"prefix": "tryc",
"body": [
"try {",
"$1",
"} on Object catch (error,stackTrace) {",
"",
"}",
],
"description": "Create trycatch"
},
}

26
.vscode/tasks.json vendored
View File

@@ -1,24 +1,12 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "fluttergen",
"type": "shell",
"command": "/Users/yura/.pub-cache/bin/fluttergen",
"group": {
"kind": "build",
"isDefault": true
},
"presentation": {
"reveal": "always"
},
"problemMatcher": []
},
{
"label": "build_runner --delete-conflicting-outputs",
"type": "shell",
"command": "dart",
"command": "flutter",
"args": [
"pub",
"run",
"build_runner",
"build",
@@ -62,6 +50,16 @@
"type": "shell",
"command": "./tools/switch_service.sh aurora",
"problemMatcher": []
},
{
"label": "flutter gen-l10n",
"type": "shell",
"command": "flutter",
"args": [
"gen-l10n"
],
"group": "build",
"problemMatcher": [],
}
]
}

0
CHANGELOG.md Normal file
View File

View File

@@ -1 +1,29 @@
Friflex LCC
# Файл CODEOWNERS определяет владельцев кода для автоматического назначения ревьюеров
# GitHub Actions и CI/CD
.github/ @petrovyuri
actions/ @petrovyuri
# Конфигурация разработки и IDE
.vscode/ @petrovyuri
# Ресурсы и конфигурация приложения
assets/ @petrovyuri
env/ @petrovyuri
tools/ @petrovyuri
# Исходный код приложения
lib/ @petrovyuri
app_services/ @petrovyuri
# Конфигурационные файлы проекта
analysis_options.yaml @petrovyuri
build.yaml @petrovyuri
pubspec.yaml @petrovyuri
# Документация
CHANGELOG.md @petrovyuri
README.md @petrovyuri
# По умолчанию все остальные файлы
* @petrovyuri

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2025 Friflex
Copyright (c) 2025 Friflex LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,21 +1,14 @@
<div align="center">
# 🚀 Friflex Flutter Starter - Корпоративный шаблон
</div>
<div align="center">
![Flutter](https://img.shields.io/badge/Flutter-3.32.0+-02569B?style=for-the-badge&logo=flutter&logoColor=white)
![Dart](https://img.shields.io/badge/Dart-3.8.0+-0175C2?style=for-the-badge&logo=dart&logoColor=white)
![Version](https://img.shields.io/badge/Version-v0.0.1-green?style=for-the-badge)
![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-приложений**
Корпоративный стартовый шаблон для разработки масштабируемых Flutter-приложений
[📋 Документация](#-документация) • [🏗️ Архитектура](#-архитектура) • [🚀 Быстрый старт](#-быстрый-старт) • [🔧 Конфигурация](#-конфигурация)
</div>
---
## 📖 Описание проекта
@@ -31,6 +24,7 @@
- 🌍 Поддержка интернационализации
- 🎨 UI Kit и система токенов дизайна
- 🔍 Инструменты отладки и мониторинга
- ⚡ Современный Dart 3.10+ с dot shorthands
## 🎯 Для чего нужен стартер
@@ -97,20 +91,27 @@ features/
| Категория | Библиотека | Версия | Описание |
|-----------|------------|--------|----------|
| 🧭 **Навигация** | [go_router](https://pub.dev/packages/go_router) | `15.1.2` | Декларативный роутинг |
| 🧭 **Навигация** | [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.10.0` | Генерация ресурсов |
| 🌐 **HTTP** | [dio](https://pub.dev/packages/dio) | `5.8.0+1` | HTTP клиент |
| 🎨 **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) | `4.8.0` | Логирование и отладка |
| 📊 **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` | Генерация тем |
## 🗂️ Структура проекта
@@ -162,33 +163,33 @@ enum AppEnv {
### 🛠️ Установка
1. **Клонирование проекта**
#### Клонирование проекта
```bash
git clone https://github.com/smmarty/friflex_starter.git
cd friflex_starter
```
2. **Установка зависимостей**
#### Установка зависимостей
```bash
flutter pub get
```
3. **Генерация файлов**
#### Генерация файлов
```bash
dart run build_runner build --delete-conflicting-outputs
flutter packages pub run flutter_gen
```
4. **Запуск приложения**
#### Запуск приложения
```bash
flutter run
```
5. **Замените название пакета на ваш**
#### Замените название пакета на ваш
### 🎯 Запуск с разными окружениями
@@ -262,6 +263,8 @@ dart run build_runner build --delete-conflicting-outputs
| 🔄 [RFC-gitflow.md](./tools/rfc/RFC-gitflow.md) | Git workflow |
| 🏗️ [RFC-projects_structure.md](./tools/rfc/RFC-projects_structure.md) | Структура проекта |
| 📝 [RFC-documentation.md](./tools/rfc/RFC-documentation.md) | Правила документирования |
| 🔧 [RFC-managing_generated_files.md](./tools/rfc/RFC-managing_generated_files.md) | Рекомендации по управлению сгенерированными файлами |
| 🌐 [RFC-managing_pubspec_lock.md](./tools/rfc/RFC-managing_pubspec_lock.md) | Рекомендации по управлению pubspec.lock |
## 🎯 Особенности и нюансы
@@ -286,15 +289,10 @@ dart run build_runner build --delete-conflicting-outputs
## 📄 Лицензия
Этот проект распространяется под лицензией MIT License. Подробности смотрите в файле [LICENSE](./LICENCE).
Этот проект распространяется под лицензией MIT License. Подробности смотрите в файле [LICENSE](./LICENSE).
Copyright © 2025 Friflex LLC. Все права защищены.
---
<div align="right">
*Разработано с любовью командой Friflex ❤️*
</div>
### Разработано с любовью командой Friflex ❤️

View File

@@ -1,89 +1,135 @@
include: package:flutter_lints/flutter.yaml
# Включает правила из:
# - package:lints/core.yaml: основные правила критических проблем
# - package:lints/recommended.yaml: рекомендуемые правила для чистого кода
# - package:flutter_lints/flutter.yaml: специфичные правила для Flutter
# Персонализированные настройки анализатора и линтера.
# Базовый набор flutter_lints + дополнительные ужесточения.
analyzer:
exclude:
- "android/**"
- "assets/**"
- "build/**"
- "config/**"
- "core/**"
- "res/**"
- "ios/**"
- "**/*.g.dart"
- "**/*.config.dart"
- "**/*.gen.dart"
- "**/*.freezed.dart"
- "**/generated/*"
- "**/*.gr.dart"
- "**/*.yaml"
- "app_services/aurora/**"
- "/app_services/aurora/**"
- "**/app_services/aurora/**"
- "**/*.lock.dart"
errors:
# Переопределения уровней ошибок (error/warning/info)
avoid_dynamic_calls: error # Запрещает использование dynamic для вызовов методов
avoid_returning_null_for_future: error # Запрещает возврат null вместо Future
avoid_slow_async_io: warning # Предупреждает о медленных асинхронных операциях ввода/вывода
avoid_type_to_string: warning # Предупреждает о неправильном использовании toString() для типов
cancel_subscriptions: error # Требует отмены подписок, предотвращает утечки памяти
close_sinks: error # Требует закрытия sink-ов, предотвращает утечки ресурсов
comment_references: warning # Проверяет корректность ссылок в комментариях
always_declare_return_types: error # Требует явного указания возвращаемых типов методов
always_require_non_null_named_parameters: warning # Требует использования @required для ненулевых параметров
avoid_bool_literals_in_conditional_expressions: warning # Запрещает избыточные булевы литералы в условных выражениях
avoid_return_types_on_setters: warning # Запрещает возвращаемые типы для сеттеров
avoid_returning_null: error # Запрещает возврат null
avoid_setters_without_getters: error # Требует создания геттера при наличии сеттера
avoid_void_async: error # Запрещает использование void для асинхронных функций
constant_identifier_names: error # Проверяет правильность именования констант
unnecessary_new: warning # Запрещает избыточное использование ключевого слова new
use_decorated_box: warning # Рекомендует использовать DecoratedBox вместо Container
use_colored_box: warning # Рекомендует использовать ColoredBox вместо Container с цветом
- "build/**" # Исключаем артефакты сборки (генерируемый код, кэш, ресурсы)
- "android/**" # Платформенный код Android (Java/Kotlin, Gradle) не анализируем Dart линтером
- "ios/**" # Платформенный код iOS (Swift/ObjC) вне области Dart анализа
- "assets/**" # Статические ассеты (шрифты, изображения, json и др.)
- "**/*.g.dart" # Сгенерированные файлы build_runner / json_serializable
- "**/*.freezed.dart" # Сгенерированные модели пакетом freezed
- "**/*.gen.dart" # Общий паттерн для доп. генераторов
- "**/*.gr.dart" # Генерируемые маршруты (auto_route / другие)
- "**/*.config.dart" # Сгенерированные конфигурационные файлы
- "**/generated/**" # Папки с автогенерируемыми исходниками
- "**/*.lock" # Временные/lock файлы генераторов (если создаются)
- "**/app_services/aurora/**" # Внутренний сервисный код (предположительно внешняя интеграция) исключён
errors:
avoid_dynamic_calls: error # Запрет неявных dynamic вызовов (типобезопасность)
avoid_returning_null_for_future: error # Future не должен завершаться null без явной модели
avoid_slow_async_io: warning # Подсказка о потенциально медленных IO операциях
avoid_type_to_string: warning # Предупреждение против использования Type.toString для логики
cancel_subscriptions: error # Требование отменять StreamSubscription для предотвращения утечек
close_sinks: error # Обязательное закрытие Sink (ресурсное управление)
comment_references: warning # Проверка корректности ссылок в документационных комментариях
always_declare_return_types: error # Явный тип возвращаемого значения повышает читаемость
avoid_bool_literals_in_conditional_expressions: warning # Избегать выражений вида condition ? true : false
avoid_return_types_on_setters: warning # Сеттеры не должны объявлять тип возврата
avoid_returning_null: warning # Предпочтительнее nullable типы / Option объекты вместо raw null
avoid_setters_without_getters: error # Сеттер без геттера может скрывать состояние
avoid_void_async: error # async void нежелателен (трудно ловить ошибки)
constant_identifier_names: error # Единый стиль именования констант (UPPER_CASE)
unnecessary_new: warning # Снижение шума: new не нужен в современном Dart
use_decorated_box: warning # Оптимизация: DecoratedBox вместо контейнера без лишних виджетов
use_colored_box: warning # Оптимизация: ColoredBox для простого цвета фона
linter:
rules:
# Нестандартные правила или правила с измененными значениями
always_put_required_named_parameters_first: true # Требовать размещать обязательные именованные параметры первыми
avoid_catches_without_on_clauses: true # Избегать catch без указания типа исключения
avoid_catching_errors: true # Избегать перехвата ошибок типа Error
avoid_equals_and_hash_code_on_mutable_classes: true # Избегать equals и hashCode в изменяемых классах
avoid_escaping_inner_quotes: true # Избегать экранирования внутренних кавычек
avoid_field_initializers_in_const_classes: true # Избегать инициализаторов полей в константных классах
avoid_implementing_value_types: true # Избегать реализации интерфейсов значимых типов
avoid_multiple_declarations_per_line: false # Разрешать несколько объявлений в одной строке
avoid_positional_boolean_parameters: true # Избегать позиционных булевых параметров
avoid_private_typedef_functions: true # Избегать приватных typedef-функций
avoid_redundant_argument_values: true # Избегать избыточных значений аргументов
avoid_returning_this: true # Избегать возврата this
cascade_invocations: true # Использовать каскадные вызовы
deprecated_consistency: true # Поддерживать согласованность устаревших элементов
do_not_use_environment: false # Разрешить использование Environment
leading_newlines_in_multiline_strings: true # Начинать многострочные строки с новой строки
no_runtimeType_toString: true # Не использовать runtimeType.toString()
one_member_abstracts: false # Разрешать абстрактные классы с одним методом
only_throw_errors: true # Выбрасывать только объекты Error
parameter_assignments: true # Запрещать присваивание значений параметрам
prefer_asserts_with_message: true # Использовать сообщения с assert
prefer_constructors_over_static_methods: true # Предпочитать конструкторы статическим методам
prefer_final_in_for_each: true # Использовать final в for-each циклах
prefer_final_locals: true # Использовать final для локальных переменных
public_member_api_docs: false # Не требовать документацию для всех публичных членов
require_trailing_commas: true # Требовать запятые в конце для улучшения форматирования
sort_constructors_first: true # Требовать размещать конструкторы первыми
sort_pub_dependencies: false # Не требовать сортировки зависимостей в pubspec
sort_unnamed_constructors_first: false # Не требовать размещать безымянные конструкторы первыми
use_is_even_rather_than_modulo: true # Использовать isEven вместо % 2 == 0
use_late_for_private_fields_and_variables: false # Не требовать late для приватных полей
use_setters_to_change_properties: true # Использовать сеттеры для изменения свойств
use_string_buffers: true # Использовать StringBuffer для сложной конкатенации
use_to_and_as_if_applicable: true # Использовать методы to и as при применимости
no_literal_bool_comparisons: true # Запрещать сравнения с литералами true/false
use_key_in_widget_constructors: true # Обязательное указание ключа для stateful/stateless widgets
always_use_package_imports: true # Всегда использовать package: импорты
unawaited_futures: true # Требовать использование unawaited для неожидаемых Future
# === Именование и стиль ===
- always_put_required_named_parameters_first # Обязательные именованные параметры первыми повышают читаемость
- always_use_package_imports # Единый стиль импортов через package: упрощает refactoring
- curly_braces_in_flow_control_structures # Обязательные фигурные скобки в if/for/while для безопасности
- directives_ordering # Упорядочивание импортов (dart → package → relative)
- eol_at_end_of_file # Пустая строка в конце файла (POSIX standard)
- prefer_single_quotes # Единый стиль кавычек (одинарные быстрее печатать)
- slash_for_doc_comments # Использовать /// вместо /** */ для документации
- sort_constructors_first # Конструкторы в начале класса — быстрый обзор API
# === Обработка ошибок и типобезопасность ===
- avoid_catching_errors # Не перехватывать Error (только Exception) — ошибки системные
- await_only_futures # await только для Future (ловит ошибки типизации)
- control_flow_in_finally # Запрет return/break/continue в finally блоках
- empty_catches # Предупреждение о пустых catch (скрытые баги)
- hash_and_equals # Требует переопределять hashCode и == вместе
- only_throw_errors # Бросать Error/Exception типы, а не произвольные объекты
- test_types_in_equals # Проверка типа в equals для безопасности
- unrelated_type_equality_checks # Запрет сравнения несовместимых типов
- use_build_context_synchronously # КРИТИЧНО: запрет BuildContext после async gap
- use_rethrow_when_possible # Использовать rethrow вместо throw e (сохраняет stacktrace)
- discarded_futures # Выявляет неожиданные Future без await
- unawaited_futures # Явно помечать намеренно не ожидаемые Future
# === Иммутабельность и const ===
- avoid_equals_and_hash_code_on_mutable_classes # hashCode/== на изменяемых классах приводит к багам в коллекциях
- prefer_const_constructors # Константные конструкторы для производительности
- prefer_const_constructors_in_immutables # Обязательные const в неизменяемых классах
- prefer_const_literals_to_create_immutables # Константные литералы коллекций
- prefer_final_fields # final для полей где возможно
- prefer_final_in_for_each # final в forEach предотвращает случайные изменения
- prefer_final_locals # final локальные переменные — стремимся к иммутабельности
- parameter_assignments # Не переназначать параметры — вводит путаницу
# === Flutter оптимизации ===
- avoid_unnecessary_containers # Убирает лишние Container виджеты
- sized_box_for_whitespace # SizedBox вместо Container для отступов
- sized_box_shrink_expand # Специализированные конструкторы SizedBox.shrink/expand
- sort_child_properties_last # child/children последними в виджетах
- use_colored_box # ColoredBox для простого цвета фона
- use_decorated_box # DecoratedBox вместо контейнера без лишних виджетов
- use_key_in_widget_constructors # Ключ позволяет корректно применить дифф к дереву виджетов
# === Современный Dart (2.17+, 3.x) ===
- use_super_parameters # super параметры для краткости
- combinators_ordering # Сортировка show/hide в импортах
- implicit_call_tearoffs # Разрешить неявные tearoffs
- matching_super_parameters # Автоматическое соответствие параметрам супер-конструктора
- use_enums # Предпочитать enums вместо статических констант
# === Читаемость кода ===
- avoid_escaping_inner_quotes # Предпочтительно менять тип кавычек вместо экранирования
- avoid_field_initializers_in_const_classes # Инициализация в константных классах тяжеловесна/избыточна
- avoid_implementing_value_types # Не реализовывать value-type интерфейсы вручную (риск несовпадений)
- avoid_positional_boolean_parameters # Булевые позиционные параметры плохо читаются (использовать именованные)
- avoid_private_typedef_functions # Приватные typedef затрудняют повторное использование / читаемость
- avoid_redundant_argument_values # Удалять аргументы совпадающие с значениями по умолчанию
- avoid_returning_this # Возврат this усложняет fluent API и может скрывать ошибки
- cascade_invocations # Использовать каскады для последовательности операций над объектом
- deprecated_consistency # Единый стиль пометок @deprecated
- leading_newlines_in_multiline_strings # Многострочные строки начинают с новой строки — чище diff
- no_literal_bool_comparisons # Исключить сравнения вида flag == true
- no_runtimeType_toString # runtimeType.toString нестабилен для логики (только отладка)
- prefer_asserts_with_message # Сообщение в assert облегчает диагностику
- prefer_constructors_over_static_methods # Конструкторы лучше выражают создание экземпляра
- comment_references # Корректные ссылки в комментариях (небитые, видимые)
# === Сахар и идиомы ===
- avoid_init_to_null # Не писать = null явно (по умолчанию)
- prefer_if_null_operators # ?? оператор вместо тернарника с null
- prefer_interpolation_to_compose_strings # Интерполяция вместо конкатенации
- prefer_is_empty # .isEmpty вместо .length == 0
- prefer_is_not_empty # .isNotEmpty вместо .length > 0
- use_is_even_rather_than_modulo # isEven более явно и может быть эффективнее
- use_setters_to_change_properties # Изменение полей через сеттеры (инкапсуляция)
- use_string_buffers # StringBuffer эффективнее при конкатенации в циклах
- use_to_and_as_if_applicable # Использовать to*/as* если семантика преобразования доступна
# === Удаление избыточности ===
- avoid_return_types_on_setters # Сеттеры не должны объявлять тип возврата
- unnecessary_const # Убирает лишние const
- unnecessary_new # Убирает лишние new
- unnecessary_this # Убирает лишние this
- unnecessary_parenthesis # Лишние скобки
# === Продакшн / отладка ===
- avoid_print # Запрет print в продакшене (использовать logger)
bloc:
rules:
- avoid_flutter_imports # Логика блока должна быть платформенно независимой
- avoid_public_bloc_methods # Публичные методы могут нарушать чистоту паттерна (использовать события)
- avoid_public_fields # Публичные поля обходят инкапсуляцию; использовать состояние/события
- prefer_file_naming_conventions # Единообразие именования файлов блоков повышает навигацию

View File

@@ -1 +1 @@
# Базовые сервисы для приложения
# Реализация сервисов для Аврора OC

View File

@@ -1,89 +1,135 @@
include: package:flutter_lints/flutter.yaml
# Включает правила из:
# - package:lints/core.yaml: основные правила критических проблем
# - package:lints/recommended.yaml: рекомендуемые правила для чистого кода
# - package:flutter_lints/flutter.yaml: специфичные правила для Flutter
# Персонализированные настройки анализатора и линтера.
# Базовый набор flutter_lints + дополнительные ужесточения.
analyzer:
exclude:
- "android/**"
- "assets/**"
- "build/**"
- "config/**"
- "core/**"
- "res/**"
- "ios/**"
- "**/*.g.dart"
- "**/*.config.dart"
- "**/*.gen.dart"
- "**/*.freezed.dart"
- "**/generated/*"
- "**/*.gr.dart"
- "**/*.yaml"
- "app_services/aurora/**"
- "/app_services/aurora/**"
- "**/app_services/aurora/**"
- "**/*.lock.dart"
errors:
# Переопределения уровней ошибок (error/warning/info)
avoid_dynamic_calls: error # Запрещает использование dynamic для вызовов методов
avoid_returning_null_for_future: error # Запрещает возврат null вместо Future
avoid_slow_async_io: warning # Предупреждает о медленных асинхронных операциях ввода/вывода
avoid_type_to_string: warning # Предупреждает о неправильном использовании toString() для типов
cancel_subscriptions: error # Требует отмены подписок, предотвращает утечки памяти
close_sinks: error # Требует закрытия sink-ов, предотвращает утечки ресурсов
comment_references: warning # Проверяет корректность ссылок в комментариях
always_declare_return_types: error # Требует явного указания возвращаемых типов методов
always_require_non_null_named_parameters: warning # Требует использования @required для ненулевых параметров
avoid_bool_literals_in_conditional_expressions: warning # Запрещает избыточные булевы литералы в условных выражениях
avoid_return_types_on_setters: warning # Запрещает возвращаемые типы для сеттеров
avoid_returning_null: error # Запрещает возврат null
avoid_setters_without_getters: error # Требует создания геттера при наличии сеттера
avoid_void_async: error # Запрещает использование void для асинхронных функций
constant_identifier_names: error # Проверяет правильность именования констант
unnecessary_new: warning # Запрещает избыточное использование ключевого слова new
use_decorated_box: warning # Рекомендует использовать DecoratedBox вместо Container
use_colored_box: warning # Рекомендует использовать ColoredBox вместо Container с цветом
- "build/**" # Исключаем артефакты сборки (генерируемый код, кэш, ресурсы)
- "android/**" # Платформенный код Android (Java/Kotlin, Gradle) не анализируем Dart линтером
- "ios/**" # Платформенный код iOS (Swift/ObjC) вне области Dart анализа
- "assets/**" # Статические ассеты (шрифты, изображения, json и др.)
- "**/*.g.dart" # Сгенерированные файлы build_runner / json_serializable
- "**/*.freezed.dart" # Сгенерированные модели пакетом freezed
- "**/*.gen.dart" # Общий паттерн для доп. генераторов
- "**/*.gr.dart" # Генерируемые маршруты (auto_route / другие)
- "**/*.config.dart" # Сгенерированные конфигурационные файлы
- "**/generated/**" # Папки с автогенерируемыми исходниками
- "**/*.lock" # Временные/lock файлы генераторов (если создаются)
- "**/app_services/aurora/**" # Внутренний сервисный код (предположительно внешняя интеграция) исключён
errors:
avoid_dynamic_calls: error # Запрет неявных dynamic вызовов (типобезопасность)
avoid_returning_null_for_future: error # Future не должен завершаться null без явной модели
avoid_slow_async_io: warning # Подсказка о потенциально медленных IO операциях
avoid_type_to_string: warning # Предупреждение против использования Type.toString для логики
cancel_subscriptions: error # Требование отменять StreamSubscription для предотвращения утечек
close_sinks: error # Обязательное закрытие Sink (ресурсное управление)
comment_references: warning # Проверка корректности ссылок в документационных комментариях
always_declare_return_types: error # Явный тип возвращаемого значения повышает читаемость
avoid_bool_literals_in_conditional_expressions: warning # Избегать выражений вида condition ? true : false
avoid_return_types_on_setters: warning # Сеттеры не должны объявлять тип возврата
avoid_returning_null: warning # Предпочтительнее nullable типы / Option объекты вместо raw null
avoid_setters_without_getters: error # Сеттер без геттера может скрывать состояние
avoid_void_async: error # async void нежелателен (трудно ловить ошибки)
constant_identifier_names: error # Единый стиль именования констант (UPPER_CASE)
unnecessary_new: warning # Снижение шума: new не нужен в современном Dart
use_decorated_box: warning # Оптимизация: DecoratedBox вместо контейнера без лишних виджетов
use_colored_box: warning # Оптимизация: ColoredBox для простого цвета фона
linter:
rules:
# Нестандартные правила или правила с измененными значениями
always_put_required_named_parameters_first: true # Требовать размещать обязательные именованные параметры первыми
avoid_catches_without_on_clauses: true # Избегать catch без указания типа исключения
avoid_catching_errors: true # Избегать перехвата ошибок типа Error
avoid_equals_and_hash_code_on_mutable_classes: true # Избегать equals и hashCode в изменяемых классах
avoid_escaping_inner_quotes: true # Избегать экранирования внутренних кавычек
avoid_field_initializers_in_const_classes: true # Избегать инициализаторов полей в константных классах
avoid_implementing_value_types: true # Избегать реализации интерфейсов значимых типов
avoid_multiple_declarations_per_line: false # Разрешать несколько объявлений в одной строке
avoid_positional_boolean_parameters: true # Избегать позиционных булевых параметров
avoid_private_typedef_functions: true # Избегать приватных typedef-функций
avoid_redundant_argument_values: true # Избегать избыточных значений аргументов
avoid_returning_this: true # Избегать возврата this
cascade_invocations: true # Использовать каскадные вызовы
deprecated_consistency: true # Поддерживать согласованность устаревших элементов
do_not_use_environment: false # Разрешить использование Environment
leading_newlines_in_multiline_strings: true # Начинать многострочные строки с новой строки
no_runtimeType_toString: true # Не использовать runtimeType.toString()
one_member_abstracts: false # Разрешать абстрактные классы с одним методом
only_throw_errors: true # Выбрасывать только объекты Error
parameter_assignments: true # Запрещать присваивание значений параметрам
prefer_asserts_with_message: true # Использовать сообщения с assert
prefer_constructors_over_static_methods: true # Предпочитать конструкторы статическим методам
prefer_final_in_for_each: true # Использовать final в for-each циклах
prefer_final_locals: true # Использовать final для локальных переменных
public_member_api_docs: false # Не требовать документацию для всех публичных членов
require_trailing_commas: true # Требовать запятые в конце для улучшения форматирования
sort_constructors_first: true # Требовать размещать конструкторы первыми
sort_pub_dependencies: false # Не требовать сортировки зависимостей в pubspec
sort_unnamed_constructors_first: false # Не требовать размещать безымянные конструкторы первыми
use_is_even_rather_than_modulo: true # Использовать isEven вместо % 2 == 0
use_late_for_private_fields_and_variables: false # Не требовать late для приватных полей
use_setters_to_change_properties: true # Использовать сеттеры для изменения свойств
use_string_buffers: true # Использовать StringBuffer для сложной конкатенации
use_to_and_as_if_applicable: true # Использовать методы to и as при применимости
no_literal_bool_comparisons: true # Запрещать сравнения с литералами true/false
use_key_in_widget_constructors: true # Обязательное указание ключа для stateful/stateless widgets
always_use_package_imports: true # Всегда использовать package: импорты
# === Именование и стиль ===
- always_put_required_named_parameters_first # Обязательные именованные параметры первыми повышают читаемость
- always_use_package_imports # Единый стиль импортов через package: упрощает refactoring
- curly_braces_in_flow_control_structures # Обязательные фигурные скобки в if/for/while для безопасности
- directives_ordering # Упорядочивание импортов (dart → package → relative)
- eol_at_end_of_file # Пустая строка в конце файла (POSIX standard)
- prefer_single_quotes # Единый стиль кавычек (одинарные быстрее печатать)
- slash_for_doc_comments # Использовать /// вместо /** */ для документации
- sort_constructors_first # Конструкторы в начале класса — быстрый обзор API
# === Обработка ошибок и типобезопасность ===
- avoid_catching_errors # Не перехватывать Error (только Exception) — ошибки системные
- await_only_futures # await только для Future (ловит ошибки типизации)
- control_flow_in_finally # Запрет return/break/continue в finally блоках
- empty_catches # Предупреждение о пустых catch (скрытые баги)
- hash_and_equals # Требует переопределять hashCode и == вместе
- only_throw_errors # Бросать Error/Exception типы, а не произвольные объекты
- test_types_in_equals # Проверка типа в equals для безопасности
- unrelated_type_equality_checks # Запрет сравнения несовместимых типов
- use_build_context_synchronously # КРИТИЧНО: запрет BuildContext после async gap
- use_rethrow_when_possible # Использовать rethrow вместо throw e (сохраняет stacktrace)
- discarded_futures # Выявляет неожиданные Future без await
- unawaited_futures # Явно помечать намеренно не ожидаемые Future
# === Иммутабельность и const ===
- avoid_equals_and_hash_code_on_mutable_classes # hashCode/== на изменяемых классах приводит к багам в коллекциях
- prefer_const_constructors # Константные конструкторы для производительности
- prefer_const_constructors_in_immutables # Обязательные const в неизменяемых классах
- prefer_const_literals_to_create_immutables # Константные литералы коллекций
- prefer_final_fields # final для полей где возможно
- prefer_final_in_for_each # final в forEach предотвращает случайные изменения
- prefer_final_locals # final локальные переменные — стремимся к иммутабельности
- parameter_assignments # Не переназначать параметры — вводит путаницу
# === Flutter оптимизации ===
- avoid_unnecessary_containers # Убирает лишние Container виджеты
- sized_box_for_whitespace # SizedBox вместо Container для отступов
- sized_box_shrink_expand # Специализированные конструкторы SizedBox.shrink/expand
- sort_child_properties_last # child/children последними в виджетах
- use_colored_box # ColoredBox для простого цвета фона
- use_decorated_box # DecoratedBox вместо контейнера без лишних виджетов
- use_key_in_widget_constructors # Ключ позволяет корректно применить дифф к дереву виджетов
# === Современный Dart (2.17+, 3.x) ===
- use_super_parameters # super параметры для краткости
- combinators_ordering # Сортировка show/hide в импортах
- implicit_call_tearoffs # Разрешить неявные tearoffs
- matching_super_parameters # Автоматическое соответствие параметрам супер-конструктора
- use_enums # Предпочитать enums вместо статических констант
# === Читаемость кода ===
- avoid_escaping_inner_quotes # Предпочтительно менять тип кавычек вместо экранирования
- avoid_field_initializers_in_const_classes # Инициализация в константных классах тяжеловесна/избыточна
- avoid_implementing_value_types # Не реализовывать value-type интерфейсы вручную (риск несовпадений)
- avoid_positional_boolean_parameters # Булевые позиционные параметры плохо читаются (использовать именованные)
- avoid_private_typedef_functions # Приватные typedef затрудняют повторное использование / читаемость
- avoid_redundant_argument_values # Удалять аргументы совпадающие с значениями по умолчанию
- avoid_returning_this # Возврат this усложняет fluent API и может скрывать ошибки
- cascade_invocations # Использовать каскады для последовательности операций над объектом
- deprecated_consistency # Единый стиль пометок @deprecated
- leading_newlines_in_multiline_strings # Многострочные строки начинают с новой строки — чище diff
- no_literal_bool_comparisons # Исключить сравнения вида flag == true
- no_runtimeType_toString # runtimeType.toString нестабилен для логики (только отладка)
- prefer_asserts_with_message # Сообщение в assert облегчает диагностику
- prefer_constructors_over_static_methods # Конструкторы лучше выражают создание экземпляра
- comment_references # Корректные ссылки в комментариях (небитые, видимые)
# === Сахар и идиомы ===
- avoid_init_to_null # Не писать = null явно (по умолчанию)
- prefer_if_null_operators # ?? оператор вместо тернарника с null
- prefer_interpolation_to_compose_strings # Интерполяция вместо конкатенации
- prefer_is_empty # .isEmpty вместо .length == 0
- prefer_is_not_empty # .isNotEmpty вместо .length > 0
- use_is_even_rather_than_modulo # isEven более явно и может быть эффективнее
- use_setters_to_change_properties # Изменение полей через сеттеры (инкапсуляция)
- use_string_buffers # StringBuffer эффективнее при конкатенации в циклах
- use_to_and_as_if_applicable # Использовать to*/as* если семантика преобразования доступна
# === Удаление избыточности ===
- avoid_return_types_on_setters # Сеттеры не должны объявлять тип возврата
- unnecessary_const # Убирает лишние const
- unnecessary_new # Убирает лишние new
- unnecessary_this # Убирает лишние this
- unnecessary_parenthesis # Лишние скобки
# === Продакшн / отладка ===
- avoid_print # Запрет print в продакшене (использовать logger)
bloc:
rules:
- avoid_flutter_imports # Логика блока должна быть платформенно независимой
- avoid_public_bloc_methods # Публичные методы могут нарушать чистоту паттерна (использовать события)
- avoid_public_fields # Публичные поля обходят инкапсуляцию; использовать состояние/события
- prefer_file_naming_conventions # Единообразие именования файлов блоков повышает навигацию

View File

@@ -1,4 +1,5 @@
library;
export 'src/app_location_service.dart';
export 'src/app_path_provider.dart';
export 'src/app_secure_storage.dart';

View File

@@ -0,0 +1,18 @@
import 'package:i_app_services/i_app_services.dart';
/// {@template app_location_service}
/// Реализация сервиса для работы с гео на платформе Aurora.
/// {@endtemplate}
class AppLocationService implements ILocationService {
/// {@macro app_location_service}
const AppLocationService();
/// Наименование сервиса
static const name = 'AuroraAppLocationService';
@override
Future<Object?> getCurrentPosition() {
// TODO: Реализовать получение текущей позиции в AuroraOS
throw UnimplementedError();
}
}

View File

@@ -1,5 +1,4 @@
import 'package:i_app_services/i_app_services.dart';
import 'package:path_provider/path_provider.dart';
/// {@template app_path_provider}
/// Класс для Аврора реализации сервиса работы с путями
@@ -13,6 +12,7 @@ class AppPathProvider implements IPathProvider {
@override
Future<String> getAppDocumentsDirectoryPath() async {
return (await getApplicationDocumentsDirectory()).path;
// TODO: Реализовать для AuroraOS
throw UnimplementedError();
}
}

View File

@@ -1,5 +1,3 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_secure_storage_aurora/flutter_secure_storage_aurora.dart';
import 'package:i_app_services/i_app_services.dart';
/// {@template app_secure_storage}
@@ -12,7 +10,8 @@ final class AppSecureStorage implements ISecureStorage {
/// Принимает:
/// - [secretKey] - ключ шифрования данных
AppSecureStorage({required this.secretKey}) {
FlutterSecureStorageAurora.setSecret(secretKey);
// Инициализация Aurora Secure Storage с ключом шифрования
// FlutterSecureStorageAurora.setSecret(secretKey);
}
@override
@@ -20,34 +19,36 @@ final class AppSecureStorage implements ISecureStorage {
static const name = 'AuroraAppSecureStorage';
/// Экземпляр хранилища данных
final _box = const FlutterSecureStorage();
@override
Future<void> clear() async {
await _box.deleteAll();
}
@override
Future<void> delete(String key) async {
await _box.delete(key: key);
}
@override
Future<bool> exists(String key) {
return _box.containsKey(key: key);
// TODO: Реализовать удаление ключа из Aurora Secure Storage
throw UnimplementedError();
}
@override
Future<String?> read(String key) async {
return _box.read(key: key);
// TODO: Реализовать чтение значения по ключу из Aurora Secure Storage
throw UnimplementedError();
}
@override
Future<void> write(String key, String value) async {
await _box.write(key: key, value: value);
// TODO: Реализовать запись значения по ключу в Aurora Secure Storage
throw UnimplementedError();
}
@override
String get nameImpl => AppSecureStorage.name;
@override
Future<bool> containsKey(String key) {
// TODO: Реализовать проверку наличия ключа в Aurora Secure Storage
throw UnimplementedError();
}
@override
Future<void> deleteAll() {
// TODO: Реализовать удаление всех ключей из Aurora Secure Storage
throw UnimplementedError();
}
}

View File

@@ -4,22 +4,12 @@ version: 0.0.1
publish_to: none
environment:
sdk: '>=3.16.2 <4.0.0'
sdk: ">=3.0.0 <4.0.0"
dependencies:
flutter:
sdk: flutter
# Зависимости для сервиса защищенного хранилища
flutter_secure_storage: 8.0.0
flutter_secure_storage_aurora:
git:
url: https://gitlab.com/omprussia/flutter/flutter-community-plugins/flutter_secure_storage_aurora.git
ref: aurora-0.5.3
# для работы с путями (плагин встроен в sdk flutter 3.27.1)
path_provider: 2.1.5
# Обязательные интерфейсы
i_app_services:
path: ../../i_app_services

View File

@@ -1,89 +1,135 @@
include: package:flutter_lints/flutter.yaml
# Включает правила из:
# - package:lints/core.yaml: основные правила критических проблем
# - package:lints/recommended.yaml: рекомендуемые правила для чистого кода
# - package:flutter_lints/flutter.yaml: специфичные правила для Flutter
# Персонализированные настройки анализатора и линтера.
# Базовый набор flutter_lints + дополнительные ужесточения.
analyzer:
exclude:
- "android/**"
- "assets/**"
- "build/**"
- "config/**"
- "core/**"
- "res/**"
- "ios/**"
- "**/*.g.dart"
- "**/*.config.dart"
- "**/*.gen.dart"
- "**/*.freezed.dart"
- "**/generated/*"
- "**/*.gr.dart"
- "**/*.yaml"
- "app_services/aurora/**"
- "/app_services/aurora/**"
- "**/app_services/aurora/**"
- "**/*.lock.dart"
errors:
# Переопределения уровней ошибок (error/warning/info)
avoid_dynamic_calls: error # Запрещает использование dynamic для вызовов методов
avoid_returning_null_for_future: error # Запрещает возврат null вместо Future
avoid_slow_async_io: warning # Предупреждает о медленных асинхронных операциях ввода/вывода
avoid_type_to_string: warning # Предупреждает о неправильном использовании toString() для типов
cancel_subscriptions: error # Требует отмены подписок, предотвращает утечки памяти
close_sinks: error # Требует закрытия sink-ов, предотвращает утечки ресурсов
comment_references: warning # Проверяет корректность ссылок в комментариях
always_declare_return_types: error # Требует явного указания возвращаемых типов методов
always_require_non_null_named_parameters: warning # Требует использования @required для ненулевых параметров
avoid_bool_literals_in_conditional_expressions: warning # Запрещает избыточные булевы литералы в условных выражениях
avoid_return_types_on_setters: warning # Запрещает возвращаемые типы для сеттеров
avoid_returning_null: error # Запрещает возврат null
avoid_setters_without_getters: error # Требует создания геттера при наличии сеттера
avoid_void_async: error # Запрещает использование void для асинхронных функций
constant_identifier_names: error # Проверяет правильность именования констант
unnecessary_new: warning # Запрещает избыточное использование ключевого слова new
use_decorated_box: warning # Рекомендует использовать DecoratedBox вместо Container
use_colored_box: warning # Рекомендует использовать ColoredBox вместо Container с цветом
- "build/**" # Исключаем артефакты сборки (генерируемый код, кэш, ресурсы)
- "android/**" # Платформенный код Android (Java/Kotlin, Gradle) не анализируем Dart линтером
- "ios/**" # Платформенный код iOS (Swift/ObjC) вне области Dart анализа
- "assets/**" # Статические ассеты (шрифты, изображения, json и др.)
- "**/*.g.dart" # Сгенерированные файлы build_runner / json_serializable
- "**/*.freezed.dart" # Сгенерированные модели пакетом freezed
- "**/*.gen.dart" # Общий паттерн для доп. генераторов
- "**/*.gr.dart" # Генерируемые маршруты (auto_route / другие)
- "**/*.config.dart" # Сгенерированные конфигурационные файлы
- "**/generated/**" # Папки с автогенерируемыми исходниками
- "**/*.lock" # Временные/lock файлы генераторов (если создаются)
- "**/app_services/aurora/**" # Внутренний сервисный код (предположительно внешняя интеграция) исключён
errors:
avoid_dynamic_calls: error # Запрет неявных dynamic вызовов (типобезопасность)
avoid_returning_null_for_future: error # Future не должен завершаться null без явной модели
avoid_slow_async_io: warning # Подсказка о потенциально медленных IO операциях
avoid_type_to_string: warning # Предупреждение против использования Type.toString для логики
cancel_subscriptions: error # Требование отменять StreamSubscription для предотвращения утечек
close_sinks: error # Обязательное закрытие Sink (ресурсное управление)
comment_references: warning # Проверка корректности ссылок в документационных комментариях
always_declare_return_types: error # Явный тип возвращаемого значения повышает читаемость
avoid_bool_literals_in_conditional_expressions: warning # Избегать выражений вида condition ? true : false
avoid_return_types_on_setters: warning # Сеттеры не должны объявлять тип возврата
avoid_returning_null: warning # Предпочтительнее nullable типы / Option объекты вместо raw null
avoid_setters_without_getters: error # Сеттер без геттера может скрывать состояние
avoid_void_async: error # async void нежелателен (трудно ловить ошибки)
constant_identifier_names: error # Единый стиль именования констант (UPPER_CASE)
unnecessary_new: warning # Снижение шума: new не нужен в современном Dart
use_decorated_box: warning # Оптимизация: DecoratedBox вместо контейнера без лишних виджетов
use_colored_box: warning # Оптимизация: ColoredBox для простого цвета фона
linter:
rules:
# Нестандартные правила или правила с измененными значениями
always_put_required_named_parameters_first: true # Требовать размещать обязательные именованные параметры первыми
avoid_catches_without_on_clauses: true # Избегать catch без указания типа исключения
avoid_catching_errors: true # Избегать перехвата ошибок типа Error
avoid_equals_and_hash_code_on_mutable_classes: true # Избегать equals и hashCode в изменяемых классах
avoid_escaping_inner_quotes: true # Избегать экранирования внутренних кавычек
avoid_field_initializers_in_const_classes: true # Избегать инициализаторов полей в константных классах
avoid_implementing_value_types: true # Избегать реализации интерфейсов значимых типов
avoid_multiple_declarations_per_line: false # Разрешать несколько объявлений в одной строке
avoid_positional_boolean_parameters: true # Избегать позиционных булевых параметров
avoid_private_typedef_functions: true # Избегать приватных typedef-функций
avoid_redundant_argument_values: true # Избегать избыточных значений аргументов
avoid_returning_this: true # Избегать возврата this
cascade_invocations: true # Использовать каскадные вызовы
deprecated_consistency: true # Поддерживать согласованность устаревших элементов
do_not_use_environment: false # Разрешить использование Environment
leading_newlines_in_multiline_strings: true # Начинать многострочные строки с новой строки
no_runtimeType_toString: true # Не использовать runtimeType.toString()
one_member_abstracts: false # Разрешать абстрактные классы с одним методом
only_throw_errors: true # Выбрасывать только объекты Error
parameter_assignments: true # Запрещать присваивание значений параметрам
prefer_asserts_with_message: true # Использовать сообщения с assert
prefer_constructors_over_static_methods: true # Предпочитать конструкторы статическим методам
prefer_final_in_for_each: true # Использовать final в for-each циклах
prefer_final_locals: true # Использовать final для локальных переменных
public_member_api_docs: false # Не требовать документацию для всех публичных членов
require_trailing_commas: true # Требовать запятые в конце для улучшения форматирования
sort_constructors_first: true # Требовать размещать конструкторы первыми
sort_pub_dependencies: false # Не требовать сортировки зависимостей в pubspec
sort_unnamed_constructors_first: false # Не требовать размещать безымянные конструкторы первыми
use_is_even_rather_than_modulo: true # Использовать isEven вместо % 2 == 0
use_late_for_private_fields_and_variables: false # Не требовать late для приватных полей
use_setters_to_change_properties: true # Использовать сеттеры для изменения свойств
use_string_buffers: true # Использовать StringBuffer для сложной конкатенации
use_to_and_as_if_applicable: true # Использовать методы to и as при применимости
no_literal_bool_comparisons: true # Запрещать сравнения с литералами true/false
use_key_in_widget_constructors: true # Обязательное указание ключа для stateful/stateless widgets
always_use_package_imports: true # Всегда использовать package: импорты
# === Именование и стиль ===
- always_put_required_named_parameters_first # Обязательные именованные параметры первыми повышают читаемость
- always_use_package_imports # Единый стиль импортов через package: упрощает refactoring
- curly_braces_in_flow_control_structures # Обязательные фигурные скобки в if/for/while для безопасности
- directives_ordering # Упорядочивание импортов (dart → package → relative)
- eol_at_end_of_file # Пустая строка в конце файла (POSIX standard)
- prefer_single_quotes # Единый стиль кавычек (одинарные быстрее печатать)
- slash_for_doc_comments # Использовать /// вместо /** */ для документации
- sort_constructors_first # Конструкторы в начале класса — быстрый обзор API
# === Обработка ошибок и типобезопасность ===
- avoid_catching_errors # Не перехватывать Error (только Exception) — ошибки системные
- await_only_futures # await только для Future (ловит ошибки типизации)
- control_flow_in_finally # Запрет return/break/continue в finally блоках
- empty_catches # Предупреждение о пустых catch (скрытые баги)
- hash_and_equals # Требует переопределять hashCode и == вместе
- only_throw_errors # Бросать Error/Exception типы, а не произвольные объекты
- test_types_in_equals # Проверка типа в equals для безопасности
- unrelated_type_equality_checks # Запрет сравнения несовместимых типов
- use_build_context_synchronously # КРИТИЧНО: запрет BuildContext после async gap
- use_rethrow_when_possible # Использовать rethrow вместо throw e (сохраняет stacktrace)
- discarded_futures # Выявляет неожиданные Future без await
- unawaited_futures # Явно помечать намеренно не ожидаемые Future
# === Иммутабельность и const ===
- avoid_equals_and_hash_code_on_mutable_classes # hashCode/== на изменяемых классах приводит к багам в коллекциях
- prefer_const_constructors # Константные конструкторы для производительности
- prefer_const_constructors_in_immutables # Обязательные const в неизменяемых классах
- prefer_const_literals_to_create_immutables # Константные литералы коллекций
- prefer_final_fields # final для полей где возможно
- prefer_final_in_for_each # final в forEach предотвращает случайные изменения
- prefer_final_locals # final локальные переменные — стремимся к иммутабельности
- parameter_assignments # Не переназначать параметры — вводит путаницу
# === Flutter оптимизации ===
- avoid_unnecessary_containers # Убирает лишние Container виджеты
- sized_box_for_whitespace # SizedBox вместо Container для отступов
- sized_box_shrink_expand # Специализированные конструкторы SizedBox.shrink/expand
- sort_child_properties_last # child/children последними в виджетах
- use_colored_box # ColoredBox для простого цвета фона
- use_decorated_box # DecoratedBox вместо контейнера без лишних виджетов
- use_key_in_widget_constructors # Ключ позволяет корректно применить дифф к дереву виджетов
# === Современный Dart (2.17+, 3.x) ===
- use_super_parameters # super параметры для краткости
- combinators_ordering # Сортировка show/hide в импортах
- implicit_call_tearoffs # Разрешить неявные tearoffs
- matching_super_parameters # Автоматическое соответствие параметрам супер-конструктора
- use_enums # Предпочитать enums вместо статических констант
# === Читаемость кода ===
- avoid_escaping_inner_quotes # Предпочтительно менять тип кавычек вместо экранирования
- avoid_field_initializers_in_const_classes # Инициализация в константных классах тяжеловесна/избыточна
- avoid_implementing_value_types # Не реализовывать value-type интерфейсы вручную (риск несовпадений)
- avoid_positional_boolean_parameters # Булевые позиционные параметры плохо читаются (использовать именованные)
- avoid_private_typedef_functions # Приватные typedef затрудняют повторное использование / читаемость
- avoid_redundant_argument_values # Удалять аргументы совпадающие с значениями по умолчанию
- avoid_returning_this # Возврат this усложняет fluent API и может скрывать ошибки
- cascade_invocations # Использовать каскады для последовательности операций над объектом
- deprecated_consistency # Единый стиль пометок @deprecated
- leading_newlines_in_multiline_strings # Многострочные строки начинают с новой строки — чище diff
- no_literal_bool_comparisons # Исключить сравнения вида flag == true
- no_runtimeType_toString # runtimeType.toString нестабилен для логики (только отладка)
- prefer_asserts_with_message # Сообщение в assert облегчает диагностику
- prefer_constructors_over_static_methods # Конструкторы лучше выражают создание экземпляра
- comment_references # Корректные ссылки в комментариях (небитые, видимые)
# === Сахар и идиомы ===
- avoid_init_to_null # Не писать = null явно (по умолчанию)
- prefer_if_null_operators # ?? оператор вместо тернарника с null
- prefer_interpolation_to_compose_strings # Интерполяция вместо конкатенации
- prefer_is_empty # .isEmpty вместо .length == 0
- prefer_is_not_empty # .isNotEmpty вместо .length > 0
- use_is_even_rather_than_modulo # isEven более явно и может быть эффективнее
- use_setters_to_change_properties # Изменение полей через сеттеры (инкапсуляция)
- use_string_buffers # StringBuffer эффективнее при конкатенации в циклах
- use_to_and_as_if_applicable # Использовать to*/as* если семантика преобразования доступна
# === Удаление избыточности ===
- avoid_return_types_on_setters # Сеттеры не должны объявлять тип возврата
- unnecessary_const # Убирает лишние const
- unnecessary_new # Убирает лишние new
- unnecessary_this # Убирает лишние this
- unnecessary_parenthesis # Лишние скобки
# === Продакшн / отладка ===
- avoid_print # Запрет print в продакшене (использовать logger)
bloc:
rules:
- avoid_flutter_imports # Логика блока должна быть платформенно независимой
- avoid_public_bloc_methods # Публичные методы могут нарушать чистоту паттерна (использовать события)
- avoid_public_fields # Публичные поля обходят инкапсуляцию; использовать состояние/события
- prefer_file_naming_conventions # Единообразие именования файлов блоков повышает навигацию

View File

@@ -1,4 +1,5 @@
library;
export 'src/app_location_service.dart';
export 'src/app_path_provider.dart';
export 'src/app_secure_storage.dart';

View File

@@ -0,0 +1,50 @@
import 'package:geolocator/geolocator.dart';
import 'package:i_app_services/i_app_services.dart';
/// {@template app_location_service}
/// Реализация сервиса для работы с гео в базовой реализацией Android/OS.
/// {@endtemplate}
class AppLocationService implements ILocationService {
/// {@macro app_location_service}
const AppLocationService();
/// Наименование сервиса
static const name = 'BaseAppLocationService';
@override
Future<Position> getCurrentPosition() async {
bool serviceEnabled;
LocationPermission permission;
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Службы геолокации не включены, не продолжаем
// обращаться к позиции и запрашиваем у пользователей
// приложения включить службы геолокации.
return Future.error('Location services are disabled.');
}
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Разрешения отклонены, в следующий раз можно попробовать
// запросить разрешения снова (здесь также возвращается
// shouldShowRequestPermissionRationale Android.
// Согласно рекомендациям Android ваше приложение
// должно показать пояснительный интерфейс сейчас.
return Future.error('Location permissions are denied');
}
}
if (permission == LocationPermission.deniedForever) {
// Разрешения отклонены навсегда, обрабатываем соответствующим образом.
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.',
);
}
// Когда мы доходим сюда, разрешения предоставлены и мы можем
// продолжить обращение к позиции устройства.
return await Geolocator.getCurrentPosition();
}
}

View File

@@ -4,7 +4,7 @@ version: 0.0.1
publish_to: none
environment:
sdk: ^3.8.0
sdk: ">=3.0.0 <4.0.0"
dependencies:
flutter:
@@ -14,11 +14,14 @@ dependencies:
flutter_secure_storage: 9.2.4
# Зависимости для сервиса незащищенного хранилища
shared_preferences: 2.3.5
shared_preferences: 2.5.3
# для работы с путями в хранилища
path_provider: 2.1.5
# Работа с геолокацией
geolocator: 14.0.2
# Обязательные интерфейсы
i_app_services:
path: ../../i_app_services

View File

@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "7482962148e8d758338d8a28f589f317e1e42ba4"
channel: "stable"
project_type: package

View File

@@ -0,0 +1 @@
# Реализация сервисов для HarmonyOS

View File

@@ -0,0 +1,135 @@
include: package:flutter_lints/flutter.yaml
# Персонализированные настройки анализатора и линтера.
# Базовый набор flutter_lints + дополнительные ужесточения.
analyzer:
exclude:
- "build/**" # Исключаем артефакты сборки (генерируемый код, кэш, ресурсы)
- "android/**" # Платформенный код Android (Java/Kotlin, Gradle) не анализируем Dart линтером
- "ios/**" # Платформенный код iOS (Swift/ObjC) вне области Dart анализа
- "assets/**" # Статические ассеты (шрифты, изображения, json и др.)
- "**/*.g.dart" # Сгенерированные файлы build_runner / json_serializable
- "**/*.freezed.dart" # Сгенерированные модели пакетом freezed
- "**/*.gen.dart" # Общий паттерн для доп. генераторов
- "**/*.gr.dart" # Генерируемые маршруты (auto_route / другие)
- "**/*.config.dart" # Сгенерированные конфигурационные файлы
- "**/generated/**" # Папки с автогенерируемыми исходниками
- "**/*.lock" # Временные/lock файлы генераторов (если создаются)
- "**/app_services/aurora/**" # Внутренний сервисный код (предположительно внешняя интеграция) исключён
errors:
avoid_dynamic_calls: error # Запрет неявных dynamic вызовов (типобезопасность)
avoid_returning_null_for_future: error # Future не должен завершаться null без явной модели
avoid_slow_async_io: warning # Подсказка о потенциально медленных IO операциях
avoid_type_to_string: warning # Предупреждение против использования Type.toString для логики
cancel_subscriptions: error # Требование отменять StreamSubscription для предотвращения утечек
close_sinks: error # Обязательное закрытие Sink (ресурсное управление)
comment_references: warning # Проверка корректности ссылок в документационных комментариях
always_declare_return_types: error # Явный тип возвращаемого значения повышает читаемость
avoid_bool_literals_in_conditional_expressions: warning # Избегать выражений вида condition ? true : false
avoid_return_types_on_setters: warning # Сеттеры не должны объявлять тип возврата
avoid_returning_null: warning # Предпочтительнее nullable типы / Option объекты вместо raw null
avoid_setters_without_getters: error # Сеттер без геттера может скрывать состояние
avoid_void_async: error # async void нежелателен (трудно ловить ошибки)
constant_identifier_names: error # Единый стиль именования констант (UPPER_CASE)
unnecessary_new: warning # Снижение шума: new не нужен в современном Dart
use_decorated_box: warning # Оптимизация: DecoratedBox вместо контейнера без лишних виджетов
use_colored_box: warning # Оптимизация: ColoredBox для простого цвета фона
linter:
rules:
# === Именование и стиль ===
- always_put_required_named_parameters_first # Обязательные именованные параметры первыми повышают читаемость
- always_use_package_imports # Единый стиль импортов через package: упрощает refactoring
- curly_braces_in_flow_control_structures # Обязательные фигурные скобки в if/for/while для безопасности
- directives_ordering # Упорядочивание импортов (dart → package → relative)
- eol_at_end_of_file # Пустая строка в конце файла (POSIX standard)
- prefer_single_quotes # Единый стиль кавычек (одинарные быстрее печатать)
- slash_for_doc_comments # Использовать /// вместо /** */ для документации
- sort_constructors_first # Конструкторы в начале класса — быстрый обзор API
# === Обработка ошибок и типобезопасность ===
- avoid_catching_errors # Не перехватывать Error (только Exception) — ошибки системные
- await_only_futures # await только для Future (ловит ошибки типизации)
- control_flow_in_finally # Запрет return/break/continue в finally блоках
- empty_catches # Предупреждение о пустых catch (скрытые баги)
- hash_and_equals # Требует переопределять hashCode и == вместе
- only_throw_errors # Бросать Error/Exception типы, а не произвольные объекты
- test_types_in_equals # Проверка типа в equals для безопасности
- unrelated_type_equality_checks # Запрет сравнения несовместимых типов
- use_build_context_synchronously # КРИТИЧНО: запрет BuildContext после async gap
- use_rethrow_when_possible # Использовать rethrow вместо throw e (сохраняет stacktrace)
- discarded_futures # Выявляет неожиданные Future без await
- unawaited_futures # Явно помечать намеренно не ожидаемые Future
# === Иммутабельность и const ===
- avoid_equals_and_hash_code_on_mutable_classes # hashCode/== на изменяемых классах приводит к багам в коллекциях
- prefer_const_constructors # Константные конструкторы для производительности
- prefer_const_constructors_in_immutables # Обязательные const в неизменяемых классах
- prefer_const_literals_to_create_immutables # Константные литералы коллекций
- prefer_final_fields # final для полей где возможно
- prefer_final_in_for_each # final в forEach предотвращает случайные изменения
- prefer_final_locals # final локальные переменные — стремимся к иммутабельности
- parameter_assignments # Не переназначать параметры — вводит путаницу
# === Flutter оптимизации ===
- avoid_unnecessary_containers # Убирает лишние Container виджеты
- sized_box_for_whitespace # SizedBox вместо Container для отступов
- sized_box_shrink_expand # Специализированные конструкторы SizedBox.shrink/expand
- sort_child_properties_last # child/children последними в виджетах
- use_colored_box # ColoredBox для простого цвета фона
- use_decorated_box # DecoratedBox вместо контейнера без лишних виджетов
- use_key_in_widget_constructors # Ключ позволяет корректно применить дифф к дереву виджетов
# === Современный Dart (2.17+, 3.x) ===
- use_super_parameters # super параметры для краткости
- combinators_ordering # Сортировка show/hide в импортах
- implicit_call_tearoffs # Разрешить неявные tearoffs
- matching_super_parameters # Автоматическое соответствие параметрам супер-конструктора
- use_enums # Предпочитать enums вместо статических констант
# === Читаемость кода ===
- avoid_escaping_inner_quotes # Предпочтительно менять тип кавычек вместо экранирования
- avoid_field_initializers_in_const_classes # Инициализация в константных классах тяжеловесна/избыточна
- avoid_implementing_value_types # Не реализовывать value-type интерфейсы вручную (риск несовпадений)
- avoid_positional_boolean_parameters # Булевые позиционные параметры плохо читаются (использовать именованные)
- avoid_private_typedef_functions # Приватные typedef затрудняют повторное использование / читаемость
- avoid_redundant_argument_values # Удалять аргументы совпадающие с значениями по умолчанию
- avoid_returning_this # Возврат this усложняет fluent API и может скрывать ошибки
- cascade_invocations # Использовать каскады для последовательности операций над объектом
- deprecated_consistency # Единый стиль пометок @deprecated
- leading_newlines_in_multiline_strings # Многострочные строки начинают с новой строки — чище diff
- no_literal_bool_comparisons # Исключить сравнения вида flag == true
- no_runtimeType_toString # runtimeType.toString нестабилен для логики (только отладка)
- prefer_asserts_with_message # Сообщение в assert облегчает диагностику
- prefer_constructors_over_static_methods # Конструкторы лучше выражают создание экземпляра
- comment_references # Корректные ссылки в комментариях (небитые, видимые)
# === Сахар и идиомы ===
- avoid_init_to_null # Не писать = null явно (по умолчанию)
- prefer_if_null_operators # ?? оператор вместо тернарника с null
- prefer_interpolation_to_compose_strings # Интерполяция вместо конкатенации
- prefer_is_empty # .isEmpty вместо .length == 0
- prefer_is_not_empty # .isNotEmpty вместо .length > 0
- use_is_even_rather_than_modulo # isEven более явно и может быть эффективнее
- use_setters_to_change_properties # Изменение полей через сеттеры (инкапсуляция)
- use_string_buffers # StringBuffer эффективнее при конкатенации в циклах
- use_to_and_as_if_applicable # Использовать to*/as* если семантика преобразования доступна
# === Удаление избыточности ===
- avoid_return_types_on_setters # Сеттеры не должны объявлять тип возврата
- unnecessary_const # Убирает лишние const
- unnecessary_new # Убирает лишние new
- unnecessary_this # Убирает лишние this
- unnecessary_parenthesis # Лишние скобки
# === Продакшн / отладка ===
- avoid_print # Запрет print в продакшене (использовать logger)
bloc:
rules:
- avoid_flutter_imports # Логика блока должна быть платформенно независимой
- avoid_public_bloc_methods # Публичные методы могут нарушать чистоту паттерна (использовать события)
- avoid_public_fields # Публичные поля обходят инкапсуляцию; использовать состояние/события
- prefer_file_naming_conventions # Единообразие именования файлов блоков повышает навигацию

View File

@@ -0,0 +1,5 @@
library;
export 'src/app_location_service.dart';
export 'src/app_path_provider.dart';
export 'src/app_secure_storage.dart';

View File

@@ -0,0 +1,18 @@
import 'package:i_app_services/i_app_services.dart';
/// {@template app_location_service}
/// Реализация сервиса для работы с гео на HarmonyOS.
/// {@endtemplate}
class AppLocationService implements ILocationService {
/// {@macro app_location_service}
const AppLocationService();
/// Наименование сервиса
static const name = 'HarmonyAppLocationService';
@override
Future<Object?> getCurrentPosition() {
// TODO: Реализовать получение текущей позиции в HarmonyOS
throw UnimplementedError();
}
}

View File

@@ -0,0 +1,18 @@
import 'package:i_app_services/i_app_services.dart';
/// {@template app_path_provider}
/// Класс для HarmonyOS реализации сервиса работы с путями
/// {@endtemplate}
class AppPathProvider implements IPathProvider {
/// {@macro app_path_provider}
const AppPathProvider();
/// Наименование сервиса
static const name = 'HarmonyAppPathProvider';
@override
Future<String> getAppDocumentsDirectoryPath() async {
// TODO: Реализовать для HarmonyOS
throw UnimplementedError();
}
}

View File

@@ -0,0 +1,50 @@
import 'package:i_app_services/i_app_services.dart';
/// {@template app_secure_storage}
/// Класс для HarmonyOS реализации сервиса по работе с защищенным хранилищем
/// {@endtemplate}
final class AppSecureStorage implements ISecureStorage {
/// Создает сервис для работы с защищенным хранилищем
///
/// Принимает:
/// - [secretKey] - ключ шифрования данных, если требуется
AppSecureStorage({required this.secretKey});
@override
final String secretKey;
static const name = 'HarmonyAppSecureStorage';
@override
Future<void> delete(String key) async {
// TODO: Реализовать удаление ключа из HarmonyOS Secure Storage
throw UnimplementedError();
}
@override
Future<String?> read(String key) async {
// TODO: Реализовать чтение значения по ключу из HarmonyOS Secure Storage
throw UnimplementedError();
}
@override
Future<void> write(String key, String value) async {
// TODO: Реализовать запись значения по ключу в HarmonyOS Secure Storage
throw UnimplementedError();
}
@override
String get nameImpl => AppSecureStorage.name;
@override
Future<bool> containsKey(String key) {
// TODO: Реализовать проверку наличия ключа в HarmonyOS Secure Storage
throw UnimplementedError();
}
@override
Future<void> deleteAll() {
// TODO: Реализовать удаление всех ключей из HarmonyOS Secure Storage
throw UnimplementedError();
}
}

View File

@@ -0,0 +1,18 @@
name: app_services
description: "Huawei сервисы для приложения"
version: 0.0.1
publish_to: none
environment:
sdk: ">=3.0.0 <4.0.0"
dependencies:
flutter:
sdk: flutter
# Обязательные интерфейсы
i_app_services:
path: ../../i_app_services
dev_dependencies:
flutter_lints: 6.0.0

View File

@@ -1,89 +1,135 @@
include: package:flutter_lints/flutter.yaml
# Включает правила из:
# - package:lints/core.yaml: основные правила критических проблем
# - package:lints/recommended.yaml: рекомендуемые правила для чистого кода
# - package:flutter_lints/flutter.yaml: специфичные правила для Flutter
# Персонализированные настройки анализатора и линтера.
# Базовый набор flutter_lints + дополнительные ужесточения.
analyzer:
exclude:
- "android/**"
- "assets/**"
- "build/**"
- "config/**"
- "core/**"
- "res/**"
- "ios/**"
- "**/*.g.dart"
- "**/*.config.dart"
- "**/*.gen.dart"
- "**/*.freezed.dart"
- "**/generated/*"
- "**/*.gr.dart"
- "**/*.yaml"
- "app_services/aurora/**"
- "/app_services/aurora/**"
- "**/app_services/aurora/**"
- "**/*.lock.dart"
errors:
# Переопределения уровней ошибок (error/warning/info)
avoid_dynamic_calls: error # Запрещает использование dynamic для вызовов методов
avoid_returning_null_for_future: error # Запрещает возврат null вместо Future
avoid_slow_async_io: warning # Предупреждает о медленных асинхронных операциях ввода/вывода
avoid_type_to_string: warning # Предупреждает о неправильном использовании toString() для типов
cancel_subscriptions: error # Требует отмены подписок, предотвращает утечки памяти
close_sinks: error # Требует закрытия sink-ов, предотвращает утечки ресурсов
comment_references: warning # Проверяет корректность ссылок в комментариях
always_declare_return_types: error # Требует явного указания возвращаемых типов методов
always_require_non_null_named_parameters: warning # Требует использования @required для ненулевых параметров
avoid_bool_literals_in_conditional_expressions: warning # Запрещает избыточные булевы литералы в условных выражениях
avoid_return_types_on_setters: warning # Запрещает возвращаемые типы для сеттеров
avoid_returning_null: error # Запрещает возврат null
avoid_setters_without_getters: error # Требует создания геттера при наличии сеттера
avoid_void_async: error # Запрещает использование void для асинхронных функций
constant_identifier_names: error # Проверяет правильность именования констант
unnecessary_new: warning # Запрещает избыточное использование ключевого слова new
use_decorated_box: warning # Рекомендует использовать DecoratedBox вместо Container
use_colored_box: warning # Рекомендует использовать ColoredBox вместо Container с цветом
- "build/**" # Исключаем артефакты сборки (генерируемый код, кэш, ресурсы)
- "android/**" # Платформенный код Android (Java/Kotlin, Gradle) не анализируем Dart линтером
- "ios/**" # Платформенный код iOS (Swift/ObjC) вне области Dart анализа
- "assets/**" # Статические ассеты (шрифты, изображения, json и др.)
- "**/*.g.dart" # Сгенерированные файлы build_runner / json_serializable
- "**/*.freezed.dart" # Сгенерированные модели пакетом freezed
- "**/*.gen.dart" # Общий паттерн для доп. генераторов
- "**/*.gr.dart" # Генерируемые маршруты (auto_route / другие)
- "**/*.config.dart" # Сгенерированные конфигурационные файлы
- "**/generated/**" # Папки с автогенерируемыми исходниками
- "**/*.lock" # Временные/lock файлы генераторов (если создаются)
- "**/app_services/aurora/**" # Внутренний сервисный код (предположительно внешняя интеграция) исключён
errors:
avoid_dynamic_calls: error # Запрет неявных dynamic вызовов (типобезопасность)
avoid_returning_null_for_future: error # Future не должен завершаться null без явной модели
avoid_slow_async_io: warning # Подсказка о потенциально медленных IO операциях
avoid_type_to_string: warning # Предупреждение против использования Type.toString для логики
cancel_subscriptions: error # Требование отменять StreamSubscription для предотвращения утечек
close_sinks: error # Обязательное закрытие Sink (ресурсное управление)
comment_references: warning # Проверка корректности ссылок в документационных комментариях
always_declare_return_types: error # Явный тип возвращаемого значения повышает читаемость
avoid_bool_literals_in_conditional_expressions: warning # Избегать выражений вида condition ? true : false
avoid_return_types_on_setters: warning # Сеттеры не должны объявлять тип возврата
avoid_returning_null: warning # Предпочтительнее nullable типы / Option объекты вместо raw null
avoid_setters_without_getters: error # Сеттер без геттера может скрывать состояние
avoid_void_async: error # async void нежелателен (трудно ловить ошибки)
constant_identifier_names: error # Единый стиль именования констант (UPPER_CASE)
unnecessary_new: warning # Снижение шума: new не нужен в современном Dart
use_decorated_box: warning # Оптимизация: DecoratedBox вместо контейнера без лишних виджетов
use_colored_box: warning # Оптимизация: ColoredBox для простого цвета фона
linter:
rules:
# Нестандартные правила или правила с измененными значениями
always_put_required_named_parameters_first: true # Требовать размещать обязательные именованные параметры первыми
avoid_catches_without_on_clauses: true # Избегать catch без указания типа исключения
avoid_catching_errors: true # Избегать перехвата ошибок типа Error
avoid_equals_and_hash_code_on_mutable_classes: true # Избегать equals и hashCode в изменяемых классах
avoid_escaping_inner_quotes: true # Избегать экранирования внутренних кавычек
avoid_field_initializers_in_const_classes: true # Избегать инициализаторов полей в константных классах
avoid_implementing_value_types: true # Избегать реализации интерфейсов значимых типов
avoid_multiple_declarations_per_line: false # Разрешать несколько объявлений в одной строке
avoid_positional_boolean_parameters: true # Избегать позиционных булевых параметров
avoid_private_typedef_functions: true # Избегать приватных typedef-функций
avoid_redundant_argument_values: true # Избегать избыточных значений аргументов
avoid_returning_this: true # Избегать возврата this
cascade_invocations: true # Использовать каскадные вызовы
deprecated_consistency: true # Поддерживать согласованность устаревших элементов
do_not_use_environment: false # Разрешить использование Environment
leading_newlines_in_multiline_strings: true # Начинать многострочные строки с новой строки
no_runtimeType_toString: true # Не использовать runtimeType.toString()
one_member_abstracts: false # Разрешать абстрактные классы с одним методом
only_throw_errors: true # Выбрасывать только объекты Error
parameter_assignments: true # Запрещать присваивание значений параметрам
prefer_asserts_with_message: true # Использовать сообщения с assert
prefer_constructors_over_static_methods: true # Предпочитать конструкторы статическим методам
prefer_final_in_for_each: true # Использовать final в for-each циклах
prefer_final_locals: true # Использовать final для локальных переменных
public_member_api_docs: false # Не требовать документацию для всех публичных членов
require_trailing_commas: true # Требовать запятые в конце для улучшения форматирования
sort_constructors_first: true # Требовать размещать конструкторы первыми
sort_pub_dependencies: false # Не требовать сортировки зависимостей в pubspec
sort_unnamed_constructors_first: false # Не требовать размещать безымянные конструкторы первыми
use_is_even_rather_than_modulo: true # Использовать isEven вместо % 2 == 0
use_late_for_private_fields_and_variables: false # Не требовать late для приватных полей
use_setters_to_change_properties: true # Использовать сеттеры для изменения свойств
use_string_buffers: true # Использовать StringBuffer для сложной конкатенации
use_to_and_as_if_applicable: true # Использовать методы to и as при применимости
no_literal_bool_comparisons: true # Запрещать сравнения с литералами true/false
use_key_in_widget_constructors: true # Обязательное указание ключа для stateful/stateless widgets
always_use_package_imports: true # Всегда использовать package: импорты
# === Именование и стиль ===
- always_put_required_named_parameters_first # Обязательные именованные параметры первыми повышают читаемость
- always_use_package_imports # Единый стиль импортов через package: упрощает refactoring
- curly_braces_in_flow_control_structures # Обязательные фигурные скобки в if/for/while для безопасности
- directives_ordering # Упорядочивание импортов (dart → package → relative)
- eol_at_end_of_file # Пустая строка в конце файла (POSIX standard)
- prefer_single_quotes # Единый стиль кавычек (одинарные быстрее печатать)
- slash_for_doc_comments # Использовать /// вместо /** */ для документации
- sort_constructors_first # Конструкторы в начале класса — быстрый обзор API
# === Обработка ошибок и типобезопасность ===
- avoid_catching_errors # Не перехватывать Error (только Exception) — ошибки системные
- await_only_futures # await только для Future (ловит ошибки типизации)
- control_flow_in_finally # Запрет return/break/continue в finally блоках
- empty_catches # Предупреждение о пустых catch (скрытые баги)
- hash_and_equals # Требует переопределять hashCode и == вместе
- only_throw_errors # Бросать Error/Exception типы, а не произвольные объекты
- test_types_in_equals # Проверка типа в equals для безопасности
- unrelated_type_equality_checks # Запрет сравнения несовместимых типов
- use_build_context_synchronously # КРИТИЧНО: запрет BuildContext после async gap
- use_rethrow_when_possible # Использовать rethrow вместо throw e (сохраняет stacktrace)
- discarded_futures # Выявляет неожиданные Future без await
- unawaited_futures # Явно помечать намеренно не ожидаемые Future
# === Иммутабельность и const ===
- avoid_equals_and_hash_code_on_mutable_classes # hashCode/== на изменяемых классах приводит к багам в коллекциях
- prefer_const_constructors # Константные конструкторы для производительности
- prefer_const_constructors_in_immutables # Обязательные const в неизменяемых классах
- prefer_const_literals_to_create_immutables # Константные литералы коллекций
- prefer_final_fields # final для полей где возможно
- prefer_final_in_for_each # final в forEach предотвращает случайные изменения
- prefer_final_locals # final локальные переменные — стремимся к иммутабельности
- parameter_assignments # Не переназначать параметры — вводит путаницу
# === Flutter оптимизации ===
- avoid_unnecessary_containers # Убирает лишние Container виджеты
- sized_box_for_whitespace # SizedBox вместо Container для отступов
- sized_box_shrink_expand # Специализированные конструкторы SizedBox.shrink/expand
- sort_child_properties_last # child/children последними в виджетах
- use_colored_box # ColoredBox для простого цвета фона
- use_decorated_box # DecoratedBox вместо контейнера без лишних виджетов
- use_key_in_widget_constructors # Ключ позволяет корректно применить дифф к дереву виджетов
# === Современный Dart (2.17+, 3.x) ===
- use_super_parameters # super параметры для краткости
- combinators_ordering # Сортировка show/hide в импортах
- implicit_call_tearoffs # Разрешить неявные tearoffs
- matching_super_parameters # Автоматическое соответствие параметрам супер-конструктора
- use_enums # Предпочитать enums вместо статических констант
# === Читаемость кода ===
- avoid_escaping_inner_quotes # Предпочтительно менять тип кавычек вместо экранирования
- avoid_field_initializers_in_const_classes # Инициализация в константных классах тяжеловесна/избыточна
- avoid_implementing_value_types # Не реализовывать value-type интерфейсы вручную (риск несовпадений)
- avoid_positional_boolean_parameters # Булевые позиционные параметры плохо читаются (использовать именованные)
- avoid_private_typedef_functions # Приватные typedef затрудняют повторное использование / читаемость
- avoid_redundant_argument_values # Удалять аргументы совпадающие с значениями по умолчанию
- avoid_returning_this # Возврат this усложняет fluent API и может скрывать ошибки
- cascade_invocations # Использовать каскады для последовательности операций над объектом
- deprecated_consistency # Единый стиль пометок @deprecated
- leading_newlines_in_multiline_strings # Многострочные строки начинают с новой строки — чище diff
- no_literal_bool_comparisons # Исключить сравнения вида flag == true
- no_runtimeType_toString # runtimeType.toString нестабилен для логики (только отладка)
- prefer_asserts_with_message # Сообщение в assert облегчает диагностику
- prefer_constructors_over_static_methods # Конструкторы лучше выражают создание экземпляра
- comment_references # Корректные ссылки в комментариях (небитые, видимые)
# === Сахар и идиомы ===
- avoid_init_to_null # Не писать = null явно (по умолчанию)
- prefer_if_null_operators # ?? оператор вместо тернарника с null
- prefer_interpolation_to_compose_strings # Интерполяция вместо конкатенации
- prefer_is_empty # .isEmpty вместо .length == 0
- prefer_is_not_empty # .isNotEmpty вместо .length > 0
- use_is_even_rather_than_modulo # isEven более явно и может быть эффективнее
- use_setters_to_change_properties # Изменение полей через сеттеры (инкапсуляция)
- use_string_buffers # StringBuffer эффективнее при конкатенации в циклах
- use_to_and_as_if_applicable # Использовать to*/as* если семантика преобразования доступна
# === Удаление избыточности ===
- avoid_return_types_on_setters # Сеттеры не должны объявлять тип возврата
- unnecessary_const # Убирает лишние const
- unnecessary_new # Убирает лишние new
- unnecessary_this # Убирает лишние this
- unnecessary_parenthesis # Лишние скобки
# === Продакшн / отладка ===
- avoid_print # Запрет print в продакшене (использовать logger)
bloc:
rules:
- avoid_flutter_imports # Логика блока должна быть платформенно независимой
- avoid_public_bloc_methods # Публичные методы могут нарушать чистоту паттерна (использовать события)
- avoid_public_fields # Публичные поля обходят инкапсуляцию; использовать состояние/события
- prefer_file_naming_conventions # Единообразие именования файлов блоков повышает навигацию

View File

@@ -1,4 +1,5 @@
library;
export 'src/i_location_service.dart';
export 'src/i_path_provider.dart';
export 'src/i_secure_storage.dart';

View File

@@ -0,0 +1,9 @@
/// {@template i_location_service}
/// Интерфейс для работы с геопозицией пользователя
/// {@endtemplate}
abstract interface class ILocationService {
static const name = 'ILocationService';
/// Метод для получения координат пользователя
Future<dynamic> getCurrentPosition();
}

View File

@@ -4,6 +4,6 @@ abstract interface class IPathProvider {
/// Наименования интерфейса
static const name = 'IPathProvider';
/// Получение path на внутренне хранилище приложения
/// Получение path на внутреннее хранилище приложения
Future<String?> getAppDocumentsDirectoryPath();
}

View File

@@ -1,10 +1,10 @@
name: i_app_services
description: "Хранит в себе все интерфейсы для реализации общих сервисов"
description: "Хранит в себе все интерфейсы для реализации сервисов"
version: 0.0.1
publish_to: "none"
environment:
sdk: ^3.8.0
sdk: ">=3.0.0 <4.0.0"
dependencies:
flutter:

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2,4 +2,3 @@ arb-dir: lib/l10n
template-arb-file: app_en.arb
output-dir: lib/l10n/gen
output-localization-file: app_localizations.dart
synthetic-package: false

View File

@@ -1,15 +1,18 @@
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_providers.dart';
import 'package:friflex_starter/app/depends_providers.dart';
import 'package:friflex_starter/app/theme/app_theme.dart';
import 'package:friflex_starter/app/theme/theme_notifier.dart';
import 'package:friflex_starter/di/di_container.dart';
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/l10n/gen/app_localizations.dart';
import 'package:friflex_starter/l10n/localization_notifier.dart';
import 'package:friflex_starter/router/app_router.dart';
import 'package:go_router/go_router.dart';
/// {@template app}
@@ -24,10 +27,7 @@ import 'package:go_router/go_router.dart';
/// {@endtemplate}
class App extends StatefulWidget {
/// {@macro app}
const App({required this.router, required this.initDependencies, super.key});
/// Роутер приложения для навигации между экранами
final GoRouter router;
const App({required this.initDependencies, super.key});
/// Функция для инициализации зависимостей приложения
/// Возвращает Future с контейнером зависимостей
@@ -67,37 +67,22 @@ class _AppState extends State<App> {
builder: () => FutureBuilder<DiContainer>(
future: _initFuture,
builder: (_, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
// Пока инициализация показываем Splash
return const SplashScreen();
case ConnectionState.done:
if (snapshot.hasError) {
return ErrorScreen(
return switch (snapshot.connectionState) {
// Если состояние не определено, ожидается или активно, то отображаем экран загрузки
ConnectionState.none ||
ConnectionState.waiting ||
ConnectionState.active => const SplashScreen(),
ConnectionState.done =>
// Если данные получены и не равны null, то отображаем внутренний виджет приложения
// Иначе отображаем экран ошибки
(snapshot.hasData && snapshot.data != null)
? _AppInternal(diContainer: snapshot.data!)
: ErrorScreen(
error: snapshot.error,
stackTrace: snapshot.stackTrace,
onRetry: _retryInit,
);
}
final diContainer = snapshot.data;
if (diContainer == null) {
return ErrorScreen(
error:
'Ошибка инициализации зависимостей, diContainer = null',
stackTrace: null,
onRetry: _retryInit,
);
}
return DependsProviders(
diContainer: diContainer,
child: ThemeConsumer(
builder: () => _App(router: widget.router),
),
);
}
};
},
),
),
@@ -119,16 +104,65 @@ class _AppState extends State<App> {
///
/// Настраивает MaterialApp с роутером, темами и локализацией.
/// {@endtemplate}
class _App extends StatelessWidget {
class _AppInternal extends StatefulWidget {
/// {@macro app_internal}
const _App({required this.router});
const _AppInternal({
required this.diContainer,
@visibleForTesting this.mockRouter, // ignore: unused_element_parameter
});
/// Контейнер зависимостей
final DiContainer diContainer;
/// Роутер приложения для навигации для тестирования
final GoRouter? mockRouter;
@override
State<_AppInternal> createState() => _AppInternalState();
}
class _AppInternalState extends State<_AppInternal> {
/// Роутер приложения для навигации
final GoRouter router;
late final GoRouter router;
@override
void initState() {
super.initState();
router =
widget.mockRouter ??
AppRouter.createRouter(widget.diContainer.debugService);
}
@override
void dispose() {
router.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp.router(
return DependsProviders(
diContainer: widget.diContainer,
child: BlocConsumer<UpdateCubit, UpdateState>(
listener: (context, state) {
if (state is UpdateSuccessState &&
state.updateInfo.updateType == .hard &&
context.mounted) {
router.goNamed(UpdateRoutes.hardUpdateScreenName);
}
},
builder: (context, state) {
// Если состояние загрузки, то отображаем экран загрузки
if (state is UpdateLoadingState) {
return const SplashScreen();
}
return ThemeConsumer(
builder: () => MediaQuery(
key: const ValueKey('prevent_rebuild'),
data: MediaQuery.of(
context,
).copyWith(textScaler: TextScaler.noScaling, boldText: false),
child: MaterialApp.router(
routerConfig: router,
darkTheme: AppTheme.dark,
theme: AppTheme.light,
@@ -136,6 +170,11 @@ class _App extends StatelessWidget {
locale: context.localization.locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
),
);
},
),
);
}
}

View File

@@ -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';

View File

@@ -13,15 +13,15 @@ final class _Dev {
static const String baseUrl = 'https://dev';
static const List<int> _enviedkeysecretKey = <int>[
1144186709,
921404830,
4081271781,
1250675737,
1700106436,
3701613773,
];
static const List<int> _envieddatasecretKey = <int>[
1144186673,
921404923,
4081271699,
1250675837,
1700106401,
3701613755,
];
static final String secretKey = String.fromCharCodes(
@@ -38,33 +38,33 @@ final class _Dev {
// generated_from: env/prod.env
final class _Prod {
static const List<int> _enviedkeybaseUrl = <int>[
3830062036,
1024953349,
2723997296,
2959773775,
4255295633,
114701674,
1920572043,
3423730200,
3647804248,
2815792265,
3038381766,
655048609,
2313418965,
1278864221,
2117667009,
1514310825,
996679495,
1388625655,
2368060187,
1047428716,
502950869,
2499923893,
963407594,
1679400929,
];
static const List<int> _envieddatabaseUrl = <int>[
3830062012,
1024953457,
2723997188,
2959773759,
4255295714,
114701648,
1920572068,
3423730231,
3647804200,
2815792379,
3038381737,
655048645,
2313418941,
1278864169,
2117666997,
1514310873,
996679476,
1388625613,
2368060212,
1047428675,
502950821,
2499923911,
963407493,
1679400837,
];
static final String baseUrl = String.fromCharCodes(
@@ -76,17 +76,17 @@ final class _Prod {
);
static const List<int> _enviedkeysecretKey = <int>[
359753139,
3208048313,
1722903860,
3044498179,
1650398676,
1481444979,
21401329,
1103991377,
];
static const List<int> _envieddatasecretKey = <int>[
359753155,
3208048331,
1722903899,
3044498279,
1650398628,
1481444865,
21401246,
1103991349,
];
static final String secretKey = String.fromCharCodes(
@@ -103,35 +103,35 @@ final class _Prod {
// generated_from: env/stage.env
final class _Stage {
static const List<int> _enviedkeybaseUrl = <int>[
2791397647,
1739207173,
306419752,
1371918084,
4062400488,
3004897854,
2820011348,
1751321626,
3103957517,
2168627914,
1003673382,
1168070657,
568662299,
1623636460,
1720327728,
1318998227,
1880230818,
1752001284,
726589281,
1688681936,
2751223200,
1403987498,
1289212622,
1492662645,
2947900480,
809806156,
];
static const List<int> _envieddatabaseUrl = <int>[
2791397735,
1739207281,
306419804,
1371918196,
4062400411,
3004897796,
2820011387,
1751321653,
3103957630,
2168627902,
1003673415,
1168070758,
568662398,
1623636356,
1720327748,
1318998183,
1880230866,
1752001399,
726589275,
1688681983,
2751223183,
1403987545,
1289212602,
1492662548,
2947900455,
809806121,
];
static final String baseUrl = String.fromCharCodes(
@@ -143,19 +143,19 @@ final class _Stage {
);
static const List<int> _enviedkeysecretKey = <int>[
2132342089,
2579069434,
3904165526,
3391356107,
1192880530,
5890476,
3047051242,
2874959210,
758075519,
3738110555,
];
static const List<int> _envieddatasecretKey = <int>[
2132342074,
2579069326,
3904165623,
3391356076,
1192880631,
5890527,
3047051166,
2874959115,
758075416,
3738110526,
];
static final String secretKey = String.fromCharCodes(

View File

@@ -1,5 +1,9 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart';
import 'package:provider/provider.dart';
/// Класс для внедрения глобальных зависимостей
@@ -19,6 +23,21 @@ final class DependsProviders extends StatelessWidget {
providers: [
// Сюда добавляем глобальные блоки, inherited и т.д.
Provider.value(value: diContainer), // Передаем контейнер зависимостей
BlocProvider(
create: (_) {
final updateCubit = UpdateCubit(
diContainer.repositories.updatesRepository,
);
unawaited(
updateCubit.checkForUpdates(
versionCode:
'1.0.0', // TODO(yura): заменить на получение из diContainer
platform: 'android',
),
);
return updateCubit;
},
),
],
child: child,
);

View File

@@ -46,20 +46,20 @@ class AppColors extends ThemeExtension<AppColors> with _$AppColorsTailorMixin {
final Color infoSnackbarBackground;
/// Цвета светлой темы
static final AppColors light = AppColors(
static const AppColors light = AppColors(
testColor: Colors.red,
errorSnackbarBackground: const Color(0xFFD24720),
successSnackbarBackground: const Color(0xFF6FB62C),
infoSnackbarBackground: const Color.fromARGB(255, 220, 108, 77),
itemTextColor: const Color(0xFFFAF3EB),
errorSnackbarBackground: Color(0xFFD24720),
successSnackbarBackground: Color(0xFF6FB62C),
infoSnackbarBackground: .fromARGB(255, 220, 108, 77),
itemTextColor: Color(0xFFFAF3EB),
);
/// Цвета тёмной темы
static final AppColors dark = AppColors(
static const AppColors dark = AppColors(
testColor: Colors.green,
errorSnackbarBackground: const Color(0xFF638B8B),
successSnackbarBackground: const Color(0xFF93C499),
infoSnackbarBackground: const Color.fromARGB(255, 35, 147, 178),
errorSnackbarBackground: Color(0xFF638B8B),
successSnackbarBackground: Color(0xFF93C499),
infoSnackbarBackground: .fromARGB(255, 35, 147, 178),
itemTextColor: Colors.white,
);
}

View File

@@ -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,
);
}
@@ -180,11 +180,11 @@ class _AppSnackBarState extends State<AppSnackBar>
CurvedAnimation(parent: _animationController, curve: Curves.easeOut),
);
_animationController.forward();
unawaited(_animationController.forward());
}
/// Запуск таймера для автоматического закрытия снекбара
/// Таймер срабатывает по истечении [widget.displayDuration]
/// Таймер срабатывает по истечении widget.displayDuration
/// и вызывает метод [_dismiss] для закрытия снекбара
void _startDismissTimer() {
_dismissTimer = Timer(widget.displayDuration, _dismiss);
@@ -192,17 +192,19 @@ class _AppSnackBarState extends State<AppSnackBar>
/// Закрытие снекбара
/// Отменяет таймер, если он существует, и запускает обратную анимацию
/// После завершения анимации вызывает функцию [widget.onDismiss], если она задана
/// После завершения анимации вызывает функцию widget.onDismiss, если она задана
/// Если виджет не смонтирован, ничего не делает
void _dismiss() {
if (!mounted) return;
_dismissTimer?.cancel();
unawaited(
_animationController.reverse().then((_) {
if (mounted) {
widget.onDismiss?.call();
}
});
}),
);
}
@override
@@ -243,7 +245,7 @@ class _AppSnackBarState extends State<AppSnackBar>
Flexible(
child: Text(
widget.message,
style: TextStyle(color: Colors.white),
style: const TextStyle(color: Colors.white),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
@@ -264,9 +266,9 @@ class _AppSnackBarState extends State<AppSnackBar>
/// [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,
};
}
}
@@ -288,13 +290,21 @@ class _Icon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return switch (type) {
TypeSnackBar.success => Icon(
.success => const Icon(
Icons.check_circle,
color: Colors.white,
size: 32,
),
TypeSnackBar.error => Icon(Icons.error, color: Colors.white, size: 32),
TypeSnackBar.info => Icon(Icons.info, color: Colors.white, size: 32),
.error => const Icon(
Icons.error,
color: Colors.white,
size: 32,
),
.info => const Icon(
Icons.info,
color: Colors.white,
size: 32,
),
};
}
}

View File

@@ -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 клиента

View File

@@ -11,19 +11,21 @@ import 'package:friflex_starter/features/main/domain/repository/i_main_repositor
import 'package:friflex_starter/features/profile/data/repository/profile_mock_repository.dart';
import 'package:friflex_starter/features/profile/data/repository/profile_repository.dart';
import 'package:friflex_starter/features/profile/domain/repository/i_profile_repository.dart';
import 'package:friflex_starter/features/update/data/repository/update_mock_repository.dart';
import 'package:friflex_starter/features/update/data/repository/update_repository.dart';
import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart';
/// Список названий моковых репозиториев, которые должны быть подменены
/// для использования в сборке stage окружения.
///
/// Для того, чтобы репозиторий был автоматически подменен на моковый в stage
/// сборке, необходимо в этом списке указать название мокового репозитория,
/// обращаясь к соответствующему полю name.
/// сборке, необходимо в этом списке указать тип интерфейса репозитория
///
/// Пример:
/// ```
/// [ AuthCheckRepositoryMock().name, ]
/// <Type>{ IUpdateRepository }
/// ```
final List<String> _mockReposToSwitch = [];
const _mockReposToSwitch = <Type>{IUpdateRepository};
/// {@template di_repositories}
/// Класс для инициализации и управления репозиториями приложения.
@@ -52,6 +54,9 @@ final class DiRepositories {
/// Интерфейс для работы с репозиторием профиля
late final IProfileRepository profileRepository;
/// Интерфейс для работы с репозиторием обновлений
late final IUpdateRepository updatesRepository;
/// Метод для инициализации репозиториев в приложении.
///
/// Принимает:
@@ -68,7 +73,17 @@ final class DiRepositories {
required OnError onError,
required DiContainer diContainer,
}) {
try {
onProgress('Начинаем инициализацию репозиториев...');
// Инициализация репозитория обновлений
updatesRepository = _lazyInitRepo<IUpdateRepository>(
mockFactory: UpdateMockRepository.new,
mainFactory: UpdateRepository.new,
onProgress: onProgress,
onError: onError,
environment: diContainer.env,
);
// Инициализация репозитория авторизации
authRepository = _lazyInitRepo<IAuthRepository>(
mockFactory: AuthMockRepository.new,
@@ -79,18 +94,10 @@ final class DiRepositories {
),
),
onProgress: onProgress,
onError: onError,
environment: diContainer.env,
);
onProgress(authRepository.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория IAuthRepository',
error,
stackTrace,
);
}
try {
// Инициализация репозитория сервиса управления токеном доступа
mainRepository = _lazyInitRepo<IMainRepository>(
mockFactory: MainMockRepository.new,
@@ -101,18 +108,10 @@ final class DiRepositories {
),
),
onProgress: onProgress,
onError: onError,
environment: diContainer.env,
);
onProgress(mainRepository.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория IMainRepository',
error,
stackTrace,
);
}
try {
// Инициализация репозитория профиля
profileRepository = _lazyInitRepo<IProfileRepository>(
mockFactory: ProfileMockRepository.new,
@@ -123,16 +122,9 @@ final class DiRepositories {
),
),
onProgress: onProgress,
onError: onError,
environment: diContainer.env,
);
onProgress(profileRepository.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория IProfileRepository',
error,
stackTrace,
);
}
onProgress(
'Инициализация репозиториев завершена! Было подменено репозиториев - ${_mockReposToSwitch.length} (${_mockReposToSwitch.join(', ')})',
@@ -146,27 +138,36 @@ final class DiRepositories {
/// - [mockFactory] - функция-фабрика для инициализации мокового репозитория
/// - [mainFactory] - функция-фабрика для инициализации основного репозитория
/// - [onProgress] - обратный вызов для уведомления о прогрессе
/// - [onError] - обратный вызов для обработки ошибок инициализации
/// - [environment] - окружение приложения для определения стратегии инициализации
///
/// Возвращает:
/// - Экземпляр репозитория в зависимости от окружения
///
/// Throws:
/// - Перебрасывает исключение, если инициализация репозитория завершилась с ошибкой
T _lazyInitRepo<T extends DiBaseRepo>({
required AppEnv environment,
required T Function() mainFactory,
required T Function() mockFactory,
required OnProgress onProgress,
required OnError onError,
}) {
final mockRepo = mockFactory();
final mainRepo = mainFactory();
try {
final repo = switch (environment) {
AppEnv.dev => mockRepo,
AppEnv.prod => mainRepo,
AppEnv.stage =>
_mockReposToSwitch.contains(mockRepo.name) ? mockRepo : mainRepo,
.dev => mockFactory(),
.prod => mainFactory(),
.stage =>
_mockReposToSwitch.contains(T) ? mockFactory() : mainFactory(),
};
// throw Exception('Тестовая - ошибка инициализации репозитория $T');
onProgress(repo.name);
return repo;
} on Object catch (error, stackTrace) {
onError('Ошибка инициализации репозитория $T', error, stackTrace);
// Перебрасываем исключение дальше, чтобы не скрыть ошибку инициализации
rethrow;
}
}
}

View File

@@ -22,6 +22,9 @@ final class DiServices {
/// Сервис для работы с защищенным локальным хранилищем
late final ISecureStorage secureStorage;
/// Сервис для работы с геолокацией
late final ILocationService locationService;
/// Метод для инициализации сервисов в приложении.
///
/// Принимает:
@@ -38,6 +41,7 @@ final class DiServices {
required DiContainer diContainer,
}) {
try {
// throw Exception('Тестовая - ошибка инициализации сервиса путей');
pathProvider = const AppPathProvider();
onProgress(AppPathProvider.name);
} on Object catch (error, stackTrace) {
@@ -52,6 +56,17 @@ final class DiServices {
onError('Ошибка инициализации ${ISecureStorage.name}', error, stackTrace);
}
try {
locationService = const AppLocationService();
onProgress(AppLocationService.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${ILocationService.name}',
error,
stackTrace,
);
}
onProgress('Инициализация сервисов завершена!');
}
}

View File

@@ -1,17 +1,19 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
/// Интерфейс для сервиса отладки
abstract interface class IDebugService {
static const name = 'IDebugService';
/// Наблюдение за dio
dynamic get dioLogger;
Interceptor get dioLogger;
/// Наблюдение за роутами
dynamic get routeObserver;
NavigatorObserver get routeObserver;
/// Наблюдение за BLoC
dynamic get blocObserver;
BlocObserver get blocObserver;
/// Метод для логирования сообщений
void log(Object message, {Object logLevel, Map<String, dynamic>? args});

View File

@@ -1,6 +1,13 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart';
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:go_router/go_router.dart';
import 'package:provider/provider.dart';
/// {@template components_screen}
/// Экран для демонстрации и тестирования компонентов приложения.
@@ -66,6 +73,35 @@ class _ComponentsScreenState extends State<ComponentsScreen> {
},
child: const Text('Показать снекбар с информацией'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
final updateCubitState = context.read<UpdateCubit>().state;
if (updateCubitState is UpdateSuccessState &&
updateCubitState.updateInfo.updateType == .soft) {
unawaited(
SoftUpdateModal.show(
context,
updateEntity: updateCubitState.updateInfo,
onUpdate: () {
AppSnackBar.showSuccess(
context: context,
message: 'Начато обновление приложения',
);
},
),
);
}
},
child: const Text('Показать модальное окно обновления'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
unawaited(context.pushNamed(UpdateRoutes.hardUpdateScreenName));
},
child: const Text('Переход на экран Hard Update обновления'),
),
],
),
),

View File

@@ -1,6 +1,8 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/features/debug/debug_routes.dart';
import 'package:go_router/go_router.dart';
@@ -35,42 +37,42 @@ class DebugScreen extends StatelessWidget {
const HBox(16),
ElevatedButton(
onPressed: () {
context.pushNamed(DebugRoutes.iconsScreenName);
unawaited(context.pushNamed(DebugRoutes.iconsScreenName));
},
child: const Text('Экран с иконками'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
context.pushNamed(DebugRoutes.themeScreenName);
unawaited(context.pushNamed(DebugRoutes.themeScreenName));
},
child: const Text('Экран настроек темы'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
context.pushNamed(DebugRoutes.tokensScreenName);
unawaited(context.pushNamed(DebugRoutes.tokensScreenName));
},
child: const Text('Экран с токенами'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
context.pushNamed(DebugRoutes.uiKitScreenName);
unawaited(context.pushNamed(DebugRoutes.uiKitScreenName));
},
child: const Text('Экран UI Kit'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
context.pushNamed(DebugRoutes.langScreenName);
unawaited(context.pushNamed(DebugRoutes.langScreenName));
},
child: const Text('Экран локализации'),
),
const HBox(16),
ElevatedButton(
onPressed: () {
context.pushNamed(DebugRoutes.componentsScreenName);
onPressed: () async {
await context.pushNamed(DebugRoutes.componentsScreenName);
},
child: const Text('Экран компонентов'),
),

View File

@@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/theme/app_colors_scheme.dart';
import 'package:friflex_starter/gen/assets.gen.dart';
import 'package:friflex_starter/gen/fonts.gen.dart';
/// {@template lang_screen}
/// Экран для отладки и тестирования локализации приложения.
@@ -42,18 +40,12 @@ class LangScreen extends StatelessWidget {
const SizedBox(height: 16),
Text(
'Тестовое слово bold: ${context.l10n.helloWorld}',
style: TextStyle(
color: context.appColors.testColor,
fontFamily: Assets.fonts.montserratBold,
),
style: TextStyle(color: context.appColors.testColor),
),
const SizedBox(height: 16),
Text(
'Тестовое слово medium: ${context.l10n.helloWorld}',
style: TextStyle(
color: context.appColors.testColor,
fontFamily: FontFamily.montserrat,
),
style: TextStyle(color: context.appColors.testColor),
),
const SizedBox(height: 16),
Text('Текущий язык: ${context.l10n.localeName}'),

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/features/main/presentation/main_routes.dart';
@@ -23,7 +25,7 @@ class MainScreen extends StatelessWidget {
ElevatedButton(
onPressed: () {
// Переход на экран с деталями
context.pushNamed(MainRoutes.mainDetailScreenName);
unawaited(context.pushNamed(MainRoutes.mainDetailScreenName));
},
child: const Text('Переход на экран с деталями'),
),

View File

@@ -16,9 +16,14 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
ProfileBloc(this._profileRepository) : super(ProfileInitialState()) {
// Регистрируем обработчики событий в конструкторе
on<ProfileEvent>((event, emit) async {
// Обрабатываем событие загрузки профиля
if (event is ProfileFetchProfileEvent) {
await _fetchProfile(event, emit);
}
// Обрабатываем событие выхода из профиля
else if (event is ProfileLogoutProfileEvent) {
await _logout(event, emit);
}
});
}
@@ -44,6 +49,7 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final data = await _profileRepository.fetchUserProfile(event.id);
emit(ProfileSuccessState(data: data));
} on Object catch (error, stackTrace) {
// Обработка ошибки при загрузке профиля
emit(
ProfileErrorState(
message: 'Ошибка при загрузке профиля',
@@ -51,6 +57,19 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
stackTrace: stackTrace,
),
);
// Пробрасываем исключение в BlocObserver, для логирования или обработки
addError(error, stackTrace);
}
}
/// Метод для выхода из профиля пользователя.
Future<void> _logout(
ProfileLogoutProfileEvent event,
Emitter<ProfileState> emit,
) async {
// Здесь можно добавить логику выхода из профиля
// Например, очистка токенов, данных пользователя и т.д.
// В данном примере просто эмитим начальное состояние
emit(ProfileInitialState());
}
}

View File

@@ -1,16 +1,37 @@
part of 'profile_bloc.dart';
/// {@template profile_event}
/// События для управления состоянием профиля пользователя.
/// {@endtemplate}
sealed class ProfileEvent extends Equatable {
/// {@macro profile_event}
const ProfileEvent();
@override
List<Object> get props => [];
}
/// {@template profile_event}
/// Событие для загрузки профиля пользователя.
/// {@endtemplate}
final class ProfileFetchProfileEvent extends ProfileEvent {
/// {@macro profile_event}
const ProfileFetchProfileEvent({required this.id});
/// ID пользователя для загрузки профиля
final String id;
@override
List<Object> get props => [id];
}
/// {@template profile_logout_event}
/// Событие для выхода из профиля пользователя.
/// {@endtemplate}
final class ProfileLogoutProfileEvent extends ProfileEvent {
/// {@macro profile_logout_event}
const ProfileLogoutProfileEvent();
@override
List<Object> get props => [];
}

View File

@@ -1,7 +1,11 @@
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:go_router/go_router.dart';
/// {@template root_screen}
@@ -13,7 +17,7 @@ import 'package:go_router/go_router.dart';
/// - Отображение кнопки отладки в не-продакшн окружениях
/// - Интеграцию с GoRouter для навигации
/// {@endtemplate}
class RootScreen extends StatelessWidget {
class RootScreen extends StatefulWidget {
/// {@macro root_screen}
const RootScreen({required this.navigationShell, super.key});
@@ -21,25 +25,59 @@ class RootScreen extends StatelessWidget {
/// Содержит информацию о текущем состоянии навигации
final StatefulNavigationShell navigationShell;
@override
State<RootScreen> createState() => _RootScreenState();
}
class _RootScreenState extends State<RootScreen> {
@override
void initState() {
super.initState();
// После построения виджета, проверяем состояние кубита обновлений
// и если есть обновление, то показываем модальное окно
_checkSoftUpdate();
}
/// Проверяет состояние кубита обновлений и показывает модальное окно при наличии мягкого обновления
void _checkSoftUpdate() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final updateState = context.read<UpdateCubit>().state;
// Проверяем только состояние успеха с доступной информацией об обновлении
if (updateState is UpdateSuccessState &&
updateState.updateInfo.updateType == .soft) {
unawaited(
SoftUpdateModal.show(
context,
updateEntity: updateState.updateInfo,
onUpdate: () {
// TODO(yura): реализовать логику обновления приложения
},
),
);
}
});
}
@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: () {
context.pushNamed(DebugRoutes.debugScreenName);
unawaited(context.pushNamed(DebugRoutes.debugScreenName));
},
)
: null,
body: navigationShell,
body: widget.navigationShell,
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Главная'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Профиль'),
],
currentIndex: navigationShell.currentIndex,
onTap: navigationShell.goBranch,
currentIndex: widget.navigationShell.currentIndex,
onTap: widget.navigationShell.goBranch,
),
);
}

View File

@@ -0,0 +1,88 @@
# Модуль Hard/Soft Updates
Модуль для управления обновлениями приложения. Поддерживает мягкие (soft) и обязательные (hard) обновления.
## Ключевые сущности и состояния
- **`UpdateEntity`**: доменная сущность с данными об обновлении
- `availableVersion`: доступная версия
- `updateUrl`: ссылка на обновление
- `updateType`: тип (`soft` | `hard`), см. `UpdateType`
- `whatIsNew`: описание изменений
- **`UpdateType`**: перечисление типов обновления
- `UpdateType.soft`
- `UpdateType.hard`
- `UpdateType.none`
- **`UpdateCubit`**: управление состоянием проверки обновлений
- Состояния: `UpdateInitialState`, `UpdateLoadingState`, `UpdateSuccessState(UpdateEntity?)`, `UpdateErrorState(message)`
- Метод: `checkForUpdates({required String versionCode, required String platform})`
## Репозитории
- **`IUpdateRepository`**: Интерфейс, описывающий методы для проверки обновлений.
- Возвращает `Future<UpdateEntity>` (не может быть `null`)
- **`UpdateRepository`**: заготовка для реальной интеграции (бэкенд/стор)
- Реализуйте логику в `checkForUpdates`
- **`UpdateMockRepository`**: мок-реализация для разработки/демо
- Возвращает фиктивное обновление (по умолчанию soft)
## UI
- **Soft update** — `SoftUpdateModal`
- BottomSheet с заголовком, списком изменений и кнопками: «Отложить» и «Обновить»
- Статический метод `show` безопасно не откроет модалку, если `updateEntity == null`
Пример показа модального окна:
```dart
await SoftUpdateModal.show(
context,
updateEntity: updateEntity, // экземпляр UpdateEntity
onUpdate: () {
// TODO: переход в стор/браузер по updateEntity.updateUrl
},
);
```
- **Hard update** — `HardUpdateScreen`
- Блокирующий экран, информирует и не даёт продолжить без обновления
## Роуты
- `UpdateRoutes.buildRoutes()` — регистрирует экран hard-обновления по пути `/update`
## Структура модуля
```
features/update/
├── data/
│ └── repository/
│ ├── update_repository.dart # реализация для интеграции
│ └── update_mock_repository.dart # мок-репозиторий
├── domain/
│ ├── entity/
│ │ └── update_entity.dart # доменная сущность
│ ├── repository/
│ │ └── i_update_repository.dart # контракт репозитория
│ └── state/
│ └── cubit/
│ ├── update_cubit.dart # кубит и логика
│ └── update_state.dart # состояния
├── presentation/
│ ├── components/
│ │ └── soft_modal_sheet.dart # модалка для soft-обновления
│ └── screens/
│ └── hard_update_screen.dart # экран для hard-обновления
├── update_type.dart # константы типов обновления
└── update_routes.dart # роут на hard-экран
```
## Заметки по реализации
- Для продакшена реализуйте переход в магазин или браузер по `updateUrl`
- Для soft-обновления не блокируйте UX; для hard — перенаправляйте на блокирующий экран
- Возвращайте `null` из репозитория, если обновлений нет

View File

@@ -0,0 +1,42 @@
import 'package:friflex_starter/features/update/domain/entity/update_entity.dart';
import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart';
/// Мок обновления обязательное, можно использовать для тестирования
const mockHardUpdateEntity = UpdateEntity(
availableVersion: '2.0.0',
updateUrl: 'https://example.com/update',
updateType: .hard,
whatIsNew: 'Добавлены новые функции и исправлены ошибки.',
);
/// Мок обновления мягкое, можно использовать для тестирования
const mockSoftUpdateEntity = UpdateEntity(
availableVersion: '2.0.0',
updateUrl: 'https://example.com/update',
updateType: .soft,
whatIsNew: 'Добавлены новые функции и исправлены ошибки.',
);
/// {@template UpdateMockRepository}
/// Репозиторий для моковой реализации проверки обновлений
/// {@endtemplate}
final class UpdateMockRepository implements IUpdateRepository {
/// {@macro UpdateMockRepository}
const UpdateMockRepository();
@override
Future<UpdateEntity> checkForUpdates({
required String versionCode,
required String platform,
}) async {
// Имитация задержки для асинхронной операции
await Future<void>.delayed(const Duration(seconds: 1));
// Возвращаем фиктивные данные об обновлении
// Можно возвращать [_mockHardUpdateEntity] или [_mockSoftUpdateEntity]
return mockSoftUpdateEntity;
}
@override
String get name => 'UpdateMockRepository';
}

View File

@@ -0,0 +1,23 @@
import 'package:friflex_starter/features/update/domain/entity/update_entity.dart';
import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart';
/// {@template UpdateRepository}
/// Репозиторий для реализации проверки обновлений
/// {@endtemplate}
final class UpdateRepository implements IUpdateRepository {
/// {@macro UpdateRepository}
const UpdateRepository();
@override
Future<UpdateEntity> checkForUpdates({
required String versionCode,
required String platform,
}) {
// TODO: Реализовать реальную логику проверки обновлений
// Если обновления нет, возвращаем null
throw UnimplementedError();
}
@override
String get name => 'UpdateRepository';
}

View File

@@ -0,0 +1,35 @@
import 'package:equatable/equatable.dart';
import 'package:friflex_starter/features/update/update_type.dart';
/// {@template UpdateEntity}
/// Сущность для представления информации об обновлении
/// {@endtemplate}
class UpdateEntity extends Equatable {
/// {@macro UpdateEntity}
const UpdateEntity({
required this.availableVersion,
required this.updateUrl,
required this.updateType,
required this.whatIsNew,
});
/// Доступная версия обновления
final String availableVersion;
/// URL для загрузки обновления
final String updateUrl;
/// Тип обновления (например, 'hard' или 'soft', или не требуется)
final UpdateType updateType;
/// Описание изменений в обновлении
final String whatIsNew;
@override
List<Object?> get props => [
availableVersion,
updateUrl,
updateType,
whatIsNew,
];
}

View File

@@ -0,0 +1,16 @@
import 'package:friflex_starter/di/di_base_repo.dart';
import 'package:friflex_starter/features/update/domain/entity/update_entity.dart';
/// {@template IUpdateRepository}
/// Интерфейс репозитория для Hard&Soft обновлений
/// {@endtemplate}
abstract interface class IUpdateRepository with DiBaseRepo {
/// Проверяет наличие обновлений
/// [versionCode] - текущий код версии приложения
/// [platform] - платформа (например, 'android' или 'ios')
/// Возвращает [UpdateEntity] с информацией об обновлении
Future<UpdateEntity> checkForUpdates({
required String versionCode,
required String platform,
});
}

View File

@@ -0,0 +1,38 @@
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:friflex_starter/features/update/domain/entity/update_entity.dart';
import 'package:friflex_starter/features/update/domain/repository/i_update_repository.dart';
part 'update_state.dart';
/// {@template UpdateCubit}
/// Кубит для управления состояниями обновления приложения
/// {@endtemplate}
class UpdateCubit extends Cubit<UpdateState> {
/// {@macro UpdateCubit}
UpdateCubit(this._updatesRepository) : super(const UpdateInitialState());
/// Репозиторий для проверки обновлений
final IUpdateRepository _updatesRepository;
/// Метод для проверки доступности обновлений
/// [versionCode] - текущий код версии приложения
/// [platform] - платформа (например, 'android' или 'ios')
Future<void> checkForUpdates({
required String versionCode,
required String platform,
}) async {
if (state is UpdateLoadingState) return;
emit(const UpdateLoadingState());
try {
final updateInfo = await _updatesRepository.checkForUpdates(
versionCode: versionCode,
platform: platform,
);
emit(UpdateSuccessState(updateInfo));
} on Object catch (e, st) {
emit(UpdateErrorState(e.toString()));
addError(e, st);
}
}
}

View File

@@ -0,0 +1,56 @@
part of 'update_cubit.dart';
/// {@template UpdateState}
/// Состояния для управления процессом обновления приложения
/// {@endtemplate}
sealed class UpdateState extends Equatable {
/// {@macro UpdateState}
const UpdateState();
@override
List<Object?> get props => [];
}
/// {@template UpdateInitialState}
/// Состояние начальной инициализации
/// {@endtemplate}
final class UpdateInitialState extends UpdateState {
/// {@macro UpdateInitialState}
const UpdateInitialState();
}
/// {@template UpdateLoadingState}
/// Состояние загрузки информации об обновлении
/// {@endtemplate}
final class UpdateLoadingState extends UpdateState {
/// {@macro UpdateLoadingState}
const UpdateLoadingState();
}
/// {@template UpdateSuccessState}
/// Состояние успешного получения информации об обновлении
/// {@endtemplate}
final class UpdateSuccessState extends UpdateState {
/// {@macro UpdateSuccessState}
const UpdateSuccessState(this.updateInfo);
/// Информация об обновлении
final UpdateEntity updateInfo;
@override
List<Object?> get props => [updateInfo];
}
/// {@template UpdateErrorState}
/// Состояние ошибки при получении информации об обновлении
/// {@endtemplate}
final class UpdateErrorState extends UpdateState {
/// {@macro UpdateErrorState}
const UpdateErrorState(this.message);
/// Сообщение об ошибке в UI
final String message;
@override
List<Object> get props => [message];
}

View File

@@ -0,0 +1,103 @@
import 'package:flutter/material.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/features/update/domain/entity/update_entity.dart';
import 'package:go_router/go_router.dart';
/// {@template soft_update_modal}
/// Модальное окно для уведомления о доступности новой версии приложения.
///
/// Отвечает за:
/// - Отображение информации о новой версии приложения
/// - Предоставление возможности обновления или отложения
/// - Показ описания изменений в новой версии
/// - Мягкое уведомление пользователя без принуждения к обновлению
/// {@endtemplate}
class SoftUpdateModal extends StatelessWidget {
/// {@macro soft_update_modal}
const SoftUpdateModal({required this.updateEntity, this.onUpdate, super.key});
/// Информация об обновлении
final UpdateEntity updateEntity;
/// Обратный вызов при нажатии "Обновить"
final VoidCallback? onUpdate;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Заголовок
Text(
'Доступна новая версия: ${updateEntity.availableVersion} ',
style: Theme.of(
context,
).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
),
const HBox(16),
// Описание изменений
Text(
'Что нового:',
style: Theme.of(
context,
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
),
const HBox(8),
Text(
updateEntity.whatIsNew,
style: Theme.of(context).textTheme.bodyMedium,
),
const HBox(24),
// Кнопки действий
Row(
children: [
Expanded(
child: OutlinedButton(
onPressed: () {
context.pop();
},
child: const Text('Отложить'),
),
),
const WBox(12),
Expanded(
child: ElevatedButton(
onPressed: () {
context.pop();
onUpdate?.call();
},
child: const Text('Обновить'),
),
),
],
),
],
),
);
}
/// Показать модальное окно обновления
///
/// [context] - контекст для отображения модального окна
/// [updateEntity] - информация об обновлении
/// [onUpdate] - функция при нажатии "Обновить"
static Future<void> show(
BuildContext context, {
required UpdateEntity updateEntity,
VoidCallback? onUpdate,
}) {
return showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
builder: (context) =>
SoftUpdateModal(updateEntity: updateEntity, onUpdate: onUpdate),
);
}
}

View File

@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/features/update/domain/state/cubit/update_cubit.dart';
/// Блокирующий экран для обязательного обновления приложения
class HardUpdateScreen extends StatelessWidget {
const HardUpdateScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Hard Обновление')),
body: Center(
child: BlocBuilder<UpdateCubit, UpdateState>(
builder: (context, updateCubitState) {
final updateEntity = updateCubitState is UpdateSuccessState
? updateCubitState.updateInfo
: null;
return Column(
children: [
const Text(
'Доступна новая версия приложения. Пожалуйста, обновите его.',
),
const HBox(16),
Text(
'Доступная версия: ${updateEntity?.availableVersion ?? ''}',
),
const HBox(8),
Text('Что нового: ${updateEntity?.whatIsNew ?? ''}'),
const HBox(8),
Text('Тип обновления: ${updateEntity?.updateType ?? ''}'),
const HBox(8),
Text('URL для обновления: ${updateEntity?.updateUrl ?? ''}'),
],
);
},
),
),
);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:friflex_starter/features/update/presentation/screens/hard_update_screen.dart';
import 'package:go_router/go_router.dart';
abstract final class UpdateRoutes {
/// Название роута главной страницы
static const String hardUpdateScreenName = 'update_screen';
/// Путь роута экрана обновления
static const String _hardUpdateScreenPath = '/update';
/// Метод для построения роутов по Update
///
/// Принимает:
/// - [routes] - вложенные роуты
static GoRoute buildRoutes({List<RouteBase> routes = const []}) => GoRoute(
path: _hardUpdateScreenPath,
name: hardUpdateScreenName,
builder: (context, state) => const HardUpdateScreen(),
);
}

View File

@@ -0,0 +1,13 @@
/// {@template UpdateType}
/// Тип обновления
/// {@endtemplate}
enum UpdateType {
/// Обязательное обновление
hard,
/// Мягкое обновление
soft,
/// Не требуется обновление
none,
}

View File

@@ -1,3 +1,5 @@
// dart format width=80
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
@@ -5,7 +7,7 @@
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
@@ -13,34 +15,6 @@ import 'package:flutter_svg/flutter_svg.dart' as _svg;
import 'package:lottie/lottie.dart' as _lottie;
import 'package:vector_graphics/vector_graphics.dart' as _vg;
class $AssetsFontsGen {
const $AssetsFontsGen();
/// File path: assets/fonts/Montserrat-Bold.ttf
String get montserratBold => 'assets/fonts/Montserrat-Bold.ttf';
/// File path: assets/fonts/Montserrat-ExtraBold.ttf
String get montserratExtraBold => 'assets/fonts/Montserrat-ExtraBold.ttf';
/// File path: assets/fonts/Montserrat-Medium.ttf
String get montserratMedium => 'assets/fonts/Montserrat-Medium.ttf';
/// File path: assets/fonts/Montserrat-Regular.ttf
String get montserratRegular => 'assets/fonts/Montserrat-Regular.ttf';
/// File path: assets/fonts/Montserrat-SemiBold.ttf
String get montserratSemiBold => 'assets/fonts/Montserrat-SemiBold.ttf';
/// List of all assets
List<String> get values => [
montserratBold,
montserratExtraBold,
montserratMedium,
montserratRegular,
montserratSemiBold,
];
}
class $AssetsIconsGen {
const $AssetsIconsGen();
@@ -63,9 +37,8 @@ class $AssetsLottieGen {
}
class Assets {
Assets._();
const Assets._();
static const $AssetsFontsGen fonts = $AssetsFontsGen();
static const $AssetsIconsGen icons = $AssetsIconsGen();
static const $AssetsLottieGen lottie = $AssetsLottieGen();
}
@@ -96,6 +69,7 @@ class SvgGenImage {
String? semanticsLabel,
bool excludeFromSemantics = false,
_svg.SvgTheme? theme,
_svg.ColorMapper? colorMapper,
ColorFilter? colorFilter,
Clip clipBehavior = Clip.hardEdge,
@deprecated Color? color,
@@ -115,6 +89,7 @@ class SvgGenImage {
assetBundle: bundle,
packageName: package,
theme: theme,
colorMapper: colorMapper,
);
}
return _svg.SvgPicture(
@@ -171,6 +146,9 @@ class LottieGenImage {
bool? addRepaintBoundary,
FilterQuality? filterQuality,
void Function(String)? onWarning,
_lottie.LottieDecoder? decoder,
_lottie.RenderCache? renderCache,
bool? backgroundLoading,
}) {
return _lottie.Lottie.asset(
_assetName,
@@ -195,6 +173,9 @@ class LottieGenImage {
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
decoder: decoder,
renderCache: renderCache,
backgroundLoading: backgroundLoading,
);
}

View File

@@ -1,15 +0,0 @@
/// GENERATED CODE - DO NOT MODIFY BY HAND
/// *****************************************************
/// FlutterGen
/// *****************************************************
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
class FontFamily {
FontFamily._();
/// Font family: Montserrat
static const String montserrat = 'Montserrat';
}

View File

@@ -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();

View File

@@ -5,6 +5,7 @@ import 'package:friflex_starter/features/main/presentation/main_routes.dart';
import 'package:friflex_starter/features/profile/presentation/profile_routes.dart';
import 'package:friflex_starter/features/root/root_screen.dart';
import 'package:friflex_starter/features/splash/splash_screen.dart';
import 'package:friflex_starter/features/update/update_routes.dart';
import 'package:go_router/go_router.dart';
/// {@template app_router}
@@ -42,6 +43,7 @@ class AppRouter {
path: '/splash',
builder: (context, state) => const SplashScreen(),
),
UpdateRoutes.buildRoutes(),
],
);
}

View File

@@ -65,14 +65,10 @@ class AppRunner {
// Инициализация метода обработки ошибок
_initErrorHandlers(_debugService);
// Инициализация роутера
router = AppRouter.createRouter(_debugService);
// throw Exception('Test error');
runApp(
App(
router: router,
initDependencies: () {
return _initDependencies(
debugService: _debugService,
@@ -125,9 +121,6 @@ class AppRunner {
required AppEnv env,
required TimerRunner timerRunner,
}) async {
// Имитация задержки инициализации
// TODO(yura): Удалить после проверки
await Future.delayed(const Duration(seconds: 3));
debugService.log(() => 'Тип сборки: ${env.name}');
final diContainer = DiContainer(env: env, dService: debugService);
await diContainer.init(
@@ -137,8 +130,11 @@ class AppRunner {
..logOnComplete(name)
..stop();
},
onError: (message, error, [stackTrace]) =>
debugService.logError(message, error: error, stackTrace: stackTrace),
onError: (message, error, [stackTrace]) {
timerRunner.stop();
_debugService.logError(message, error: error, stackTrace: stackTrace);
throw Exception('Ошибка инициализации зависимостей: $message');
},
);
//throw Exception('Test error');
return diContainer;

View File

@@ -25,8 +25,8 @@ void _initErrorHandlers(IDebugService debugService) {
/// Метод для показа экрана ошибки
void _showErrorScreen(Object error, StackTrace? stackTrace) {
WidgetsBinding.instance.addPostFrameCallback((_) {
AppRouter.rootNavigatorKey.currentState?.push(
WidgetsBinding.instance.addPostFrameCallback((_) async {
await AppRouter.rootNavigatorKey.currentState?.push(
MaterialPageRoute(
builder: (_) => ErrorScreen(error: error, stackTrace: stackTrace),
),

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -5,18 +5,18 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: e55636ed79578b9abca5fecf9437947798f5ef7456308b5cb85720b793eac92f
sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d
url: "https://pub.dev"
source: hosted
version: "82.0.0"
version: "91.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "904ae5bb474d32c38fb9482e2d925d5454cda04ddd0e55d2e6826bc72f6ba8c0"
sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08
url: "https://pub.dev"
source: hosted
version: "7.4.5"
version: "8.4.1"
ansicolor:
dependency: transitive
description:
@@ -76,18 +76,18 @@ packages:
dependency: transitive
description:
name: build
sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9
url: "https://pub.dev"
source: hosted
version: "2.4.2"
version: "4.0.2"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
version: "1.2.0"
build_daemon:
dependency: transitive
description:
@@ -96,30 +96,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.4"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
url: "https://pub.dev"
source: hosted
version: "2.4.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
sha256: "7b5b569f3df370590a85029148d6fc66c7d0201fc6f1847c07dd85d365ae9fcd"
url: "https://pub.dev"
source: hosted
version: "2.4.15"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
version: "2.10.3"
built_collection:
dependency: transitive
description:
@@ -132,10 +116,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d
url: "https://pub.dev"
source: hosted
version: "8.9.4"
version: "8.12.0"
characters:
dependency: transitive
description:
@@ -164,10 +148,10 @@ packages:
dependency: transitive
description:
name: code_builder
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243"
url: "https://pub.dev"
source: hosted
version: "4.10.1"
version: "4.11.0"
collection:
dependency: transitive
description:
@@ -220,10 +204,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af"
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.3"
dartx:
dependency: transitive
description:
@@ -232,14 +216,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
dio:
dependency: "direct main"
description:
name: dio
sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
url: "https://pub.dev"
source: hosted
version: "5.8.0+1"
version: "5.9.0"
dio_web_adapter:
dependency: transitive
description:
@@ -252,18 +244,18 @@ packages:
dependency: "direct main"
description:
name: envied
sha256: a4e2b1d0caa479b5d61332ae516518c175a6d09328a35a0bc0a53894cc5d7e4d
sha256: cd95ddf0982e53f0b6664e889d4a9ce678b3907a59a5047923404375ef6dcacc
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.3.1"
envied_generator:
dependency: "direct dev"
description:
name: envied_generator
sha256: "894f6c5eb624c60a1ce6f642b6fd7ec68bc3440aa6f1881837aa9acbbeade0c8"
sha256: "81ad332912f1b31afbd2b913aff9ec7b032e97f4ba7e419f52d02bb90637e77c"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.3.1"
equatable:
dependency: "direct main"
description:
@@ -321,26 +313,26 @@ packages:
dependency: "direct dev"
description:
name: flutter_gen
sha256: "4117a3ea6b26a910c715bd58abcc5a90447e70930a5b98249e94c41da9e849bb"
sha256: eac4863b65813aacbf16ecc07e7c271ab82fb2d95181825348f1fb7b03fd52da
url: "https://pub.dev"
source: hosted
version: "5.10.0"
version: "5.12.0"
flutter_gen_core:
dependency: transitive
description:
name: flutter_gen_core
sha256: "3eaa2d3d8be58267ac4cd5e215ac965dd23cae0410dc073de2e82e227be32bfc"
sha256: b6bafbbd981da2f964eb45bcb8b8a7676a281084f8922c0c75de4cfbaa849311
url: "https://pub.dev"
source: hosted
version: "5.10.0"
version: "5.12.0"
flutter_gen_runner:
dependency: "direct dev"
description:
name: flutter_gen_runner
sha256: e74b4ead01df3e8f02e73a26ca856759dbbe8cb3fd60941ba9f4005cd0cd19c9
sha256: c99b10af9d404e3f46fd1927e7d90099779e935e86022674c4c2a9e6c2a93b29
url: "https://pub.dev"
source: hosted
version: "5.10.0"
version: "5.12.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -406,10 +398,10 @@ packages:
dependency: "direct main"
description:
name: flutter_svg
sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1
sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.2.2"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -420,14 +412,70 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
geoclue:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
name: geoclue
sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "0.1.1"
geolocator:
dependency: transitive
description:
name: geolocator
sha256: "79939537046c9025be47ec645f35c8090ecadb6fe98eba146a0d25e8c1357516"
url: "https://pub.dev"
source: hosted
version: "14.0.2"
geolocator_android:
dependency: transitive
description:
name: geolocator_android
sha256: "114072db5d1dce0ec0b36af2697f55c133bc89a2c8dd513e137c0afe59696ed4"
url: "https://pub.dev"
source: hosted
version: "5.0.1+1"
geolocator_apple:
dependency: transitive
description:
name: geolocator_apple
sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22
url: "https://pub.dev"
source: hosted
version: "2.3.13"
geolocator_linux:
dependency: transitive
description:
name: geolocator_linux
sha256: c4e966f0a7a87e70049eac7a2617f9e16fd4c585a26e4330bdfc3a71e6a721f3
url: "https://pub.dev"
source: hosted
version: "0.2.3"
geolocator_platform_interface:
dependency: transitive
description:
name: geolocator_platform_interface
sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67"
url: "https://pub.dev"
source: hosted
version: "4.2.6"
geolocator_web:
dependency: transitive
description:
name: geolocator_web
sha256: b1ae9bdfd90f861fde8fd4f209c37b953d65e92823cb73c7dee1fa021b06f172
url: "https://pub.dev"
source: hosted
version: "4.1.3"
geolocator_windows:
dependency: transitive
description:
name: geolocator_windows
sha256: "175435404d20278ffd220de83c2ca293b73db95eafbdc8131fe8609be1421eb6"
url: "https://pub.dev"
source: hosted
version: "0.2.5"
glob:
dependency: transitive
description:
@@ -440,10 +488,10 @@ packages:
dependency: "direct main"
description:
name: go_router
sha256: "0b1e06223bee260dee31a171fb1153e306907563a0b0225e8c1733211911429a"
sha256: c92d18e1fe994cb06d48aa786c46b142a5633067e8297cff6b5a3ac742620104
url: "https://pub.dev"
source: hosted
version: "15.1.2"
version: "17.0.0"
graphs:
dependency: transitive
description:
@@ -460,6 +508,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.3.4"
gsettings:
dependency: transitive
description:
name: gsettings
sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c"
url: "https://pub.dev"
source: hosted
version: "0.2.8"
hashcodes:
dependency: transitive
description:
@@ -499,6 +555,14 @@ packages:
relative: true
source: path
version: "0.0.1"
image:
dependency: transitive
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
image_size_getter:
dependency: transitive
description:
@@ -543,26 +607,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0"
url: "https://pub.dev"
source: hosted
version: "10.0.9"
version: "11.0.1"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.9"
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
version: "3.0.2"
lints:
dependency: transitive
description:
@@ -583,10 +647,10 @@ packages:
dependency: "direct main"
description:
name: lottie
sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950
sha256: "8ae0be46dbd9e19641791dc12ee480d34e1fd3f84c749adc05f3ad9342b71b95"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
version: "3.3.2"
matcher:
dependency: transitive
description:
@@ -607,10 +671,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
url: "https://pub.dev"
source: hosted
version: "1.16.0"
version: "1.17.0"
mime:
dependency: transitive
description:
@@ -635,6 +699,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
url: "https://pub.dev"
source: hosted
version: "8.3.1"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
path:
dependency: transitive
description:
@@ -775,26 +855,26 @@ packages:
dependency: transitive
description:
name: share_plus
sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
sha256: "3424e9d5c22fd7f7590254ba09465febd6f8827c8b19a44350de4ac31d92d3a6"
url: "https://pub.dev"
source: hosted
version: "10.1.4"
version: "12.0.0"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a"
url: "https://pub.dev"
source: hosted
version: "5.0.2"
version: "6.1.0"
shared_preferences:
dependency: transitive
description:
name: shared_preferences
sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
version: "2.3.5"
version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
@@ -868,18 +948,18 @@ packages:
dependency: transitive
description:
name: source_gen
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "4.0.2"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
version: "1.3.8"
source_span:
dependency: transitive
description:
@@ -932,42 +1012,42 @@ packages:
dependency: transitive
description:
name: talker
sha256: a664f5eae5284e94aa8c535e0400ab71a78fa2480ad18a7344dec928b5c56805
sha256: "82de443cadfb6c41d457e7774c7890a91c73af3c2f17f3f7c01670bb58d5f5a1"
url: "https://pub.dev"
source: hosted
version: "4.8.0"
version: "5.0.2"
talker_bloc_logger:
dependency: "direct main"
description:
name: talker_bloc_logger
sha256: "1af998d6cbafba00f66bbf49d77132275d3b113c6ac53ce0dc9d9981206f9cea"
sha256: e631fcc9454cd86639888a9bb4654582bbc8c64c7dcc913401bfecb8892ec759
url: "https://pub.dev"
source: hosted
version: "4.8.0"
version: "5.0.2"
talker_dio_logger:
dependency: "direct main"
description:
name: talker_dio_logger
sha256: "296a20ce600ccca7801deb5a530c28d03500c520f25f1e40c3bae727f63dd5eb"
sha256: "5bbecc237f3d2c4af9348da5a0086321ed6dd6bf9857d723b1f54f61c810cff2"
url: "https://pub.dev"
source: hosted
version: "4.8.0"
version: "5.0.2"
talker_flutter:
dependency: "direct main"
description:
name: talker_flutter
sha256: "480c51bba7ac0dcab23be5e9545214a43179625bc5f787326248e94af8316aee"
sha256: "4f7a8d739237a3a3c8ba4dddcdbc1f9d9dec143811641dbafebd6b70f947f8ca"
url: "https://pub.dev"
source: hosted
version: "4.8.0"
version: "5.0.2"
talker_logger:
dependency: "direct main"
description:
name: talker_logger
sha256: b5d0bd04229eeccdf765cabf73e964ee28528c749104b56867c8d2eb764a45ee
sha256: "8218836d871ea5ab1ec616cffe3cdae84e8fb44022d5cc04c95d7b220572b8fb"
url: "https://pub.dev"
source: hosted
version: "4.8.0"
version: "5.0.2"
term_glyph:
dependency: transitive
description:
@@ -980,26 +1060,26 @@ packages:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev"
source: hosted
version: "0.7.4"
version: "0.7.7"
theme_tailor:
dependency: "direct dev"
description:
name: theme_tailor
sha256: ba98be1d04856deef932757a3ca8fa7a5e2a6f96c30466a59c48924eeb608b97
sha256: "1ed7eeb5362e61aac4719e3ca4cd701fd6a74a507a911424eb6caef9b28a7fef"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.1.1"
theme_tailor_annotation:
dependency: "direct main"
description:
name: theme_tailor_annotation
sha256: "0d5ecd13a6a52add2082aa60497179f6093acf482eb69e7fa3a9f37eb990ac34"
sha256: "867848486f33dc4dff7649e3c6525133ecee410b5d97c77e22f1cb146baa1158"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
version: "3.1.1"
time:
dependency: transitive
description:
@@ -1008,14 +1088,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.5"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
typed_data:
dependency: transitive
description:
@@ -1092,10 +1164,10 @@ packages:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.0"
vm_service:
dependency: transitive
description:
@@ -1169,5 +1241,5 @@ packages:
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.8.0 <4.0.0"
flutter: ">=3.32.0"
dart: ">=3.10.0 <4.0.0"
flutter: ">=3.38.1"

View File

@@ -6,33 +6,33 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1
environment:
sdk: ^3.8.0
flutter: ">=3.32.0"
sdk: ">=3.10.0 <4.0.0"
flutter: ">=3.38.1"
dependencies:
flutter:
sdk: flutter
cupertino_icons: 1.0.8
envied: 1.1.1
go_router: 15.1.2
envied: 1.3.1
go_router: 17.0.0
flutter_bloc: 9.1.1
provider: 6.1.5
dio: 5.8.0+1
dio: 5.9.0
intl: 0.20.2
flutter_svg: 2.1.0
flutter_svg: 2.2.2
flutter_localizations:
sdk: flutter
lottie: 3.3.1
lottie: 3.3.2
# Пакеты для отладки
talker_flutter: 4.8.0
talker_dio_logger: 4.8.0
talker_bloc_logger: 4.8.0
talker_logger: 4.8.0
talker_flutter: 5.0.2
talker_dio_logger: 5.0.2
talker_bloc_logger: 5.0.2
talker_logger: 5.0.2
equatable: 2.0.7
theme_tailor_annotation: 3.0.2
theme_tailor_annotation: 3.1.1
### основной сервис с интерфейсами
i_app_services:
@@ -42,40 +42,27 @@ dependencies:
### В зависимости от платформы ###
app_services:
path: app_services/base/app_services ### Базовая реализация ###
#path: app_services/aurora/app_services ### Аврора реализация ###
# path: app_services/aurora/app_services ### Аврора реализация ###
# path: app_services/hms/app_services ### HarmonyOS реализация ###
dev_dependencies:
flutter_test:
sdk: flutter
envied_generator: 1.1.1
build_runner: 2.4.15
flutter_gen_runner: 5.10.0
flutter_gen: 5.10.0
envied_generator: 1.3.1
build_runner: 2.10.3
flutter_gen_runner: 5.12.0
flutter_gen: 5.12.0
flutter_lints: 6.0.0
theme_tailor: 3.0.3
theme_tailor: 3.1.1
flutter:
uses-material-design: true
generate: true
assets:
- assets/icons/
- assets/fonts/
- assets/lottie/
fonts:
- family: Montserrat
fonts:
- asset: assets/fonts/Montserrat-ExtraBold.ttf
weight: 800
- asset: assets/fonts/Montserrat-Bold.ttf
weight: 700
- asset: assets/fonts/Montserrat-SemiBold.ttf
weight: 600
- asset: assets/fonts/Montserrat-Medium.ttf
weight: 500
- asset: assets/fonts/Montserrat-Regular.ttf
weight: 400
flutter_gen:
integrations:
flutter_svg: true

View File

@@ -631,12 +631,12 @@ void main() {
// Создаем приложение с кастомными отступами
await tester.pumpWidget(
MaterialApp(
const MaterialApp(
home: MediaQuery(
data: const MediaQueryData(
data: MediaQueryData(
padding: EdgeInsets.only(top: 50), // Симулируем статус бар
),
child: const Scaffold(body: Center(child: Text('Test'))),
child: Scaffold(body: Center(child: Text('Test'))),
),
),
);

View File

@@ -1,31 +1,37 @@
## Рекомендованный Readme для проектов
#### Приложение [ProjectName]
# Приложение [ProjectName]
## Структура проекта
- проект архитектурно делится на три слоя: data, domain и presentation;
- все [features] реализуются в отдельных папках, с внутренним делением на слои;
- проект архитектурно делится на три слоя: data, domain и presentation;
- все [features] реализуются в отдельных папках, с внутренним делением на слои;
## Основные пакеты и реализации (обновляется при добавлении или изменении)
- управление роутингом: [go_router](https://pub.dev/packages/go_router);
- основной state manager: [flutter_bloc](https://pub.dev/packages/flutter_bloc);
- di: ручная реализация через InheritedWidget;
- работа с ресурсами: [flutter_gen](https://pub.dev/packages/flutter_gen);
- анализатор: используем [friflex_lint_rules](https://pub.friflex.com/packages/friflex_lint_rules), с правилами написания кода от компании.;
- для хранения защищенных данных - [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage);
- для хранения данных - [shared_preferences](https://pub.dev/packages/shared_preferences);
- для работы с API - [dio](https://pub.dev/packages/dio);
- управление роутингом: [go_router](https://pub.dev/packages/go_router);
- основной state manager: [flutter_bloc](https://pub.dev/packages/flutter_bloc);
- di: ручная реализация через InheritedWidget;
- работа с ресурсами: [flutter_gen](https://pub.dev/packages/flutter_gen);
- анализатор: используем [friflex_lint_rules](https://pub.friflex.com/packages/friflex_lint_rules), с правилами написания кода от компании.;
- для хранения защищенных данных - [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage);
- для хранения данных - [shared_preferences](https://pub.dev/packages/shared_preferences);
- для работы с API - [dio](https://pub.dev/packages/dio);
## Инструкция по запуску проекта
- [Инструкция по запуску проекта](./tools/rfc/RFC-build.md)
- [Инструкция по запуску проекта](./tools/rfc/RFC-build.md)
## Стиль написания кода
- [Стиль написания кода](./*ools*/rfc/RFC-codestyle.md)
- [Стиль написания кода](./*ools*/rfc/RFC-codestyle.md)
## Внесение изменений в код
- [Внесение изменений в код](./tools/rfc/RFC-gitflow.md)
## Структура проекта
- [Структура проекта](./tools/rfc/RFC-projects_structure.md)
- [Внесение изменений в код](./tools/rfc/RFC-gitflow.md)
## Документация по структуре проекта
- [Структура проекта](./tools/rfc/RFC-projects_structure.md)
## Ведение документации и комментариев в проекте
- [Ведение документации и комментариев в проекте](./tools/rfc/RFC-documentation.md)
- [Ведение документации и комментариев в проекте](./tools/rfc/RFC-documentation.md)

View File

@@ -8,13 +8,16 @@
## Именование
### Интерфейсы
Утверждены два вида объявления интерфейсов:
1. Все интерфейсы в приложении должны начинаться с заглавной буквы "**I**".
Например: **IAuthRepository**, **IProfileRepository**, **IMainRunner** и т.д.
Таким образом, сразу видно, что работаешь с интерфейсом.
Пример:
```dart
/// Интерфейс - **IUserRepository**
abstract interface class IUserRepository {}
@@ -29,6 +32,7 @@ class UserRepositoryLocal implements IUserRepository {}
```
### Классы - Репозитории
Репозитории должны содержать в конце название источника данных (если используется мок или локальное хранилище).\
Основная реализация, не должна содержать постфикса.
@@ -38,64 +42,82 @@ class UserRepositoryLocal implements IUserRepository {}
Локальное хранилище (например бд или просто имитация данных) - **AuthRepositoryLocal**
### Файлы
Используется snake_case.
Название файла должно иметь следующую структуру: [раздел]_[тип].dart
Пример: user_details_screen.dart, shop_entity.dart
### Классы
Название классов UpperCamelCase.
Для создание приватных классов используем префикс _ . Название класса в конце должно содержать в себе тип.
Пример: **UserEntity**, **AdultDialog**
## Методы
Название метода в начале должно содержать в себе действие(глагол):
- fetch
- put
- update
- delete и так далее
- fetch
- put
- update
- delete и так далее
Пример:
```dart
int fetchFirstElement(){}
```
Пример:
```dart
void updateFirstElement(){};
```
Название метода не должно содержать в себе And/Or
и метод соответственно не должен выполнять подобную логику.
## Переменные и константы
Константы именуются также lowerCamelCase.
Пример:
```dart
const String carItem
```
или
```dart
final userName;
```
## Виджеты
Виджеты именуются UpperCamelCase.
В названии виджетов не должно содержаться слово widget.
### Экраны
Экраны, используемые в роутинге, именуются с постфиксом Screen.
Например, **ShopListScreen**.
### Содержимое экрана
Виджеты, отображающие содержимое экрана, именуются с постфиксом View.
Например, **ShopListView**.
### Глобальные виджеты
Глобальные виджеты именуются с приставкой App.
Например, **AppButton**.
## Структура класса
Объявления элементов класса должны располагаться в следующем порядке:
1. **Constructors**
- constructors
- named-constructors

View File

@@ -2,7 +2,8 @@
## Документация
### Основные правила ведения документации в проекте:
### Основные правила ведения документации в проекте
- документация оформляется над описываемым объектом с использованием '///';
- документацией необходимо покрывать все классы, конструкторы, поля, геттеры, сеттеры, методы, фабрики, все кастомные объекты;
- документация должна:
@@ -13,14 +14,17 @@
## Шаблоны и примеры документации объектов
### Документация классов:
### Документация классов
```dart
/// {@template new_class}
/// Класс для реализации {описание назначения и реализуемого функционала в классе}.
/// Класс для {описание назначения и реализуемого функционала в классе}.
/// {@endtemplate}
class NewClass {}
```
Пример:
```dart
/// {@template app_button}
/// Класс для реализации кастомизированной кнопки.
@@ -28,11 +32,12 @@
class AppButton {}
```
### Документация конструкторов и фабрик:
### Документация конструкторов и фабрик
Если конструктор один, то достаточно указать {@macro new_class}.
{@macro new_class} дублирует на конструктор указанную в рамках описания класса документацию, поэтому описание класса должно в таком случае включать все передаваемые в конструктор параметры
Если конструкторов несколько, описания должны отражать их отличия друг от друга. Фабрики описываются по такому же принципу.
```dart
///{@macro new_class}
const NewClass();
@@ -43,8 +48,10 @@
factory NewClass.fromJson(Map<String, dynamic> json)
```
### Документация полей классов:
### Документация полей классов
В классе необходимо описывать каждое поле по отдельности. Если поле ссылается на другое поле или зависит от него, необходимо это указывать в описании.
```dart
/// Возраст пользователя.
final int age;
@@ -53,7 +60,8 @@
final bool isAdult;
```
### Документация геттеров/сеттеров:
### Документация геттеров/сеттеров
```dart
/// Получения доступа к {описание данных, которые получает геттер}
int get newGetter => ...
@@ -62,8 +70,10 @@
set newSetter (int setterValue) => ...
```
### Документация методов:
### Документация методов
Методы должны описывать их основное назначение. Если метод сложный, требуется указывать дополнительное описание его работы ниже через пропущенную строку. Если метод принимает какие-либо параметры, каждый параметр должен быть описан по отдельности списком с прямой ссылкой. Если метод возвращает какие-либо значения, они должны быть описаны. Желательно указать, что вернет метод в случае возникновения ошибки.
```dart
/// Метод для {описание назначения метода}.
/// {описание особенностей работы метода}.
@@ -73,7 +83,9 @@
...
}
```
Пример:
```dart
/// Метод для расчета температуры.
/// Принимает:
@@ -88,16 +100,20 @@
## Комментарии
### Основные правила комментирования кода в проекте:
### Основные правила комментирования кода в проекте
- комментарии оформляются над описываемым участком кода с использованием '//';
- комментировать необходимо те участки кода, которые действительно нуждаются в дополнительном описании;
- комментарий не должен повторять легко читаемые участки кода
Например:
```dart
if(flag != true) // не нужно описывать как "Если значение не равно true...
```
Например:
```dart
if(isAurora){
// Так как Аврора не может открывать WebView,
@@ -107,7 +123,8 @@
## TODO
### Основные правила создания TODO в проекте:
### Основные правила создания TODO в проекте
- TODO оформляются согласно условиям форматирования линтера с указанием имени разработчика, кто находится в контексте проблемы и сможет ее прокомментировать;
- TODO необходимо оставлять на тех участках кода, которые требуют дальнейшей доработки;
- если известно, в рамках какой задачи будет доработан помечаемый участок кода, ссылку на задаче необходимо оставить в скобках.

View File

@@ -1,21 +1,26 @@
# Ведение проекта в git
## Создание commits/pull-request
- язык описания PR - Русский;
- описание должно отражать краткую суть изменений;
- коммит/PR должен содержать:
- исчерпывающую информацию об изменениях;
- ссылку на задачу в таск-трекер;
- Перечисление deprecated-кода (если есть).
### Типы коммитов согласно convention
* **feat**: — новая функция
* **fix**: — исправление ошибок
* **refactor**: — Изменение кода, которое не исправляет ошибку и не добавляет функции (рефакторинг кода).
* **build**: — изменения, влияющие на систему сборки или внешние зависимости (примеры областей (scope): android, ios, linux и так далее).
* **docs**: — изменения только в документации
* **chore** - добавление/обновление/настройка инструментов и библиотек (пример: pubspec.yaml).
* **test**: — добавление недостающих тестов или исправление существующих тестов.
* **ci**: — изменения в файлах конфигурации и скриптах CI (примеры областей: папка CI).
- **feat**: — новая функция
- **fix**: — исправление ошибок
- **refactor**: — Изменение кода, которое не исправляет ошибку и не добавляет функции (рефакторинг кода).
- **build**: — изменения, влияющие на систему сборки или внешние зависимости (примеры областей (scope): android, ios, linux и так далее).
- **docs**: — изменения только в документации
- **chore** - добавление/обновление/настройка инструментов и библиотек (пример: pubspec.yaml).
- **test**: — добавление недостающих тестов или исправление существующих тестов.
- **ci**: — изменения в файлах конфигурации и скриптах CI (примеры областей: папка CI).
### Правила именования веток
@@ -28,33 +33,37 @@
Пример:
* `feat/PRIME-123_authentication`
* `fix/PRIME-456_typo_in_button`
* `fix/PRIME-456-typo-in-button`
- `feat/PRIME-123_authentication`
- `fix/PRIME-456_typo_in_button`
- `fix/PRIME-456-typo-in-button`
## Схема разработки новой features
![](./tools/res/images/developing.jpg)
![Схема разработки новой features](./tools/res/images/developing.jpg)
## Создание релиза
![](./tools/res/images/release.jpg)
![Схема создания релиза](./tools/res/images/release.jpg)
### Добавление новых features
- создаем ветку feat/(уникальный номер задачи) (название feature),
Пример: <b>PRIME-17 Добавить_локальный_репозиторий</b>;
- вносим изменения;
- делаем коммит только на то, что касается изменений связанных с этой задачей;
- по завершению задачи - создаем Pull Request с названием по правилам https://commitlint.io/.
Пример: <b>feat(app,di,auth,scripts,pubspec): PRIME-17 Добавить локальный репозиторий</b>.
- создаем ветку feat/(уникальный номер задачи) (название feature),
Пример: **PRIME-17 Добавить_локальный_репозиторий**;
- вносим изменения;
- делаем коммит только на то, что касается изменений связанных с этой задачей;
- по завершению задачи - создаем Pull Request с названием по правилам <https://commitlint.io/>.
Пример: **feat(app,di,auth,scripts,pubspec): PRIME-17 Добавить локальный репозиторий**.
Где, PRIME- это обязательный префикс(с помощью него автоматически подтягивается ссылка на задачу), число 17 это уникальный номер задачи из трекера.
В описание к PR добавляем ссылку на задачу и описание внесенных изменений, и пояснения(если требуется);
- после завершения CodeReview создаем squash commit в ветку main (название коммит(а), должно быть такое же как название pull request) ;
- после того как ветка с новой feature попало в main - удаляем ветку;
- после завершения CodeReview создаем squash commit в ветку main (название коммит(а), должно быть такое же как название pull request) ;
- после того как ветка с новой feature попало в main - удаляем ветку;
### Создание release
- создаем ветку из main (release/release_0.0.1+1);
- добавляем в changelog.md описание изменений в этой версии (обычно это список коммитов, от предыдущей версии):
- делаем сборки, отправляем на тестирование;
- получаем баг-лист от тестировщиков, делаем фиксы;
- отправляем на следующий раунд тестирования и.т.д;
- после проверки и получения разрешение от другого разработчика - создаем tag c номером версии (0.0.1+1), создаем релиз, фиксируем версию и squash commit в ветку main и development;
- после того как release попал в main - удаляем ветку;
- создаем ветку из main (release/release_0.0.1+1);
- добавляем в changelog.md описание изменений в этой версии (обычно это список коммитов, от предыдущей версии):
- делаем сборки, отправляем на тестирование;
- получаем баг-лист от тестировщиков, делаем фиксы;
- отправляем на следующий раунд тестирования и.т.д;
- после проверки и получения разрешение от другого разработчика - создаем tag c номером версии (0.0.1+1), создаем релиз, фиксируем версию и squash commit в ветку main и development;
- после того как release попал в main - удаляем ветку;

View File

@@ -0,0 +1,30 @@
# Управление сгенерированными файлами
На проекте встречаются файлы, автоматически создаваемые инструментами генерации кода
(например, файлы с расширениями *.g.dart,*.freezed.dart,
а также файлы, связанные с protobuf и другими генераторами кода).
Необходимость контролировать такие файлы в репозитории вызывает ряд обсуждений.
См [issue](https://github.com/smmarty/flutter_team/issues/22).
## Проблемы включения сгенерированных файлов в репозиторий
1. Частые изменения. Сгенерированные файлы могут автоматически обновляться даже при отсутствии изменений в исходном коде, что приводит к ненужным изменениям в репозитории.
2. Конфликты при слиянии веток. Различия в таких файлах могут вызывать конфликты, которые не связаны с реальными изменениями, усложняя процесс работы над кодом.
3. Усложнение кода при проверке. Изменения в автоматически сгенерированных файлах попадают в PR, затрудняя код-ревью.
4. Недостоверность содержимого main. Нет гарантии, что в основной ветке всегда будут корректные версии сгенерированных файлов.
## Проблемы при добавлении сгенерированных файлов в .gitignore
1. Необходимость предварительной генерации. При добавлении таких файлов в .gitignore для проверки кода в пайплайне необходимо добавлять этап генерации при каждом изменении в PR.
Это критично на крупных проектах, так как генерация файлов может занимать несколько минут.
2. Неудобства для разработчиков. Разработчикам потребуется вручную генерировать файлы на локальной машине при каждом чекауте.
3. Неактуальный код в main. Основная ветка без предварительно сгенерированных файлов станет неработоспособной, а сборка потребует добавления этапа генерации, что увеличит время и нагрузку на серверы.
## Рекомендации
1. Оставлять сгенерированные файлы в репозитории.
Это позволит сохранить работоспособность основного кода без необходимости постоянной генерации файлов на всех этапах.
При этом рекомендуется:
- Периодически актуализировать файлы в основных ветках;
- Контролировать конфликты при слиянии веток и исключать ненужные изменения в этих файлах.
2. Обучение команды. Важно информировать команду о причинах хранения сгенерированных файлов в репозитории и о правилах работы с ними.

View File

@@ -0,0 +1,25 @@
# Управление файлом pubspec.lock
На проекте возникает необходимость определить стратегию работы с файлом pubspec.lock.
Данный файл может либо храниться в репозитории, либо быть добавлен в .gitignore.
Оба варианта имеют свои преимущества и недостатки, которые следует учесть.
См [issue](https://github.com/smmarty/flutter_team/issues/20)
## Аргументы за хранение pubspec.lock
1. Повторяемость сборки. Файл фиксирует конкретные версии зависимостей, обеспечивая единообразие версий для всех разработчиков и сред сборки.
2. Избежание неожиданных изменений. Новые версии зависимостей могут внести изменения, способные нарушить сборку или логику приложения. Наличие pubspec.lock позволяет контролировать изменения и избегать неожиданных обновлений.
3. Стабильность CI/CD. Зафиксированные версии зависимостей способствуют стабильным сборкам и тестам в CI-процессах.
4. Рекомендации Dart. Dart рекомендует хранить pubspec.lock в репозитории для приложений, чтобы обеспечить стабильность окружения. (См. документацию.)
## Аргументы против хранения pubspec.lock
1. Частые изменения. pubspec.lock обновляется при каждом изменении зависимостей, что увеличивает количество коммитов и PR, связанных только с обновлением зависимостей.
## Рекомендации
1. ХРАНИТЬ pubspec.lock для ПРИЛОЖЕНИЙ.
2. НЕ ХРАНИТЬ pubspec.lock ДЛЯ ПАКЕТОВ.
3. Решение для переключаемых зависимостей (GMS/HMS).
При изменении зависимостей, таких как GMS/HMS, может возникать несовместимость версий. Flutter пока не поддерживает флаворы на уровне зависимостей pubspec (см. [Flutter issue #46979](https://github.com/flutter/flutter/issues/46979)),
поэтому рекомендуем хранить по дефолту GMS pubspec.lock как базовый.

View File

@@ -30,7 +30,6 @@
- **/dev.dart** - сборка разработки на моковых репозиториях
- **/stage.dart** - сборка для stage окружения
## Пример структуры feature папок
- **/data** - слой данных
@@ -44,32 +43,38 @@
- **/presentation** - слой представления
- **/screens** - все экраны должны заканчиваться на Screen, например UserProfileScreen.
- **/components** - виджеты, которые необходимы для работы в presentation слое. Например: SuperButton, AppTextFields итд.
# Пояснение к структуре feature папок
## Data (слой данных) Этот слой является поставщиком данных.
- Repository - сущность, которая реализует внутри себя предоставление данных. Должен реализовывать какой либо интерфейс репозитория из domain слоя.
- DTO - Dto(Data Transfer Object) модели, и модели с которыми происходит работа в data слое. Например: UserDto;
## Пояснение к структуре feature папок
## Domain (слой бизнес логики)
- Entity - должны быть в максимально удобном виде для работы внутри Domain и Presentation. Например: UserEntity, ShopEntity;
- State - управления состоянием - state manager
- Service - различные сервисы, для выполнения различных задач.
- interfaces - интерфейсы репозиториев, которые используются в domain слое.
### Data (слой данных) Этот слой является поставщиком данных
## Presentation (слой представления)
- сomponents - widget'ы которые реализуют работу какого либо визуального компонента(Buttons,TextFields,Lists, итд). Например: ShopList, RateButton. Модальные окна.
- screens - widget'ы которые представляют собой экран приложения. Например: UserInfoScreen.
- Repository - сущность, которая реализует внутри себя предоставление данных. Должен реализовывать какой либо интерфейс репозитория из domain слоя.
- DTO - Dto(Data Transfer Object) модели, и модели с которыми происходит работа в data слое. Например: UserDto;
# Основные правила общения объектов между папками
### Domain (слой бизнес логики)
## В рамках всего приложения
- объекты внутри фичи должны быть инкапсулированы и не могут использоваться в других feature;
- если есть необходимость использовать объект в нескольких feature, его нужно вынести в папку app и использовать как глобальный для всего приложения;
- сервис, который должен быть использован в нескольких feature, создается как отдельная feature;
- если создаваемый сервис является платформно-зависимым, его необходимо выносить в app_services. В приложении должен быть только интерфейс.
- Entity - должны быть в максимально удобном виде для работы внутри Domain и Presentation. Например: UserEntity, ShopEntity;
- State - управления состоянием - state manager
- Service - различные сервисы, для выполнения различных задач.
- interfaces - интерфейсы репозиториев, которые используются в domain слое.
## В рамках одной feature
- объекты data слоя не должны ничего знать про объекты слоя presentation. Имеют доступ к объектам entity из domain слоя для преобразования DTO в Entity ;
- объекты domain слоя не должны ничего знать про объекты слоя data, используемый экземпляр репозитория передается в объекты domain слоя через интерфейс репозитория, расположенного в этом же domain слое;
- объекты domain слоя не должны ничего знать про слой presentation, не должны использовать компоненты библиотек ui, material, cupertino, widget и прочих, не должны использовать context;
- объекты presentation слоя не должны ничего знать про объекты слоя data, все взаимодействия непосредственно через объекты слоя domain.
### Presentation (слой представления)
- сomponents - widget'ы которые реализуют работу какого либо визуального компонента(Buttons,TextFields,Lists, итд). Например: ShopList, RateButton. Модальные окна.
- screens - widget'ы которые представляют собой экран приложения. Например: UserInfoScreen.
## Основные правила общения объектов между папками
### В рамках всего приложения
- объекты внутри фичи должны быть инкапсулированы и не могут использоваться в других feature;
- если есть необходимость использовать объект в нескольких feature, его нужно вынести в папку app и использовать как глобальный для всего приложения;
- сервис, который должен быть использован в нескольких feature, создается как отдельная feature;
- если создаваемый сервис является платформно-зависимым, его необходимо выносить в app_services. В приложении должен быть только интерфейс.
### В рамках одной feature
- объекты data слоя не должны ничего знать про объекты слоя presentation. Имеют доступ к объектам entity из domain слоя для преобразования DTO в Entity ;
- объекты domain слоя не должны ничего знать про объекты слоя data, используемый экземпляр репозитория передается в объекты domain слоя через интерфейс репозитория, расположенного в этом же domain слое;
- объекты domain слоя не должны ничего знать про слой presentation, не должны использовать компоненты библиотек ui, material, cupertino, widget и прочих, не должны использовать context;
- объекты presentation слоя не должны ничего знать про объекты слоя data, все взаимодействия непосредственно через объекты слоя domain.