4 Commits

Author SHA1 Message Date
Artem Luzin m
ead44f1b1a feat: добавить визуальный элемент 2025-06-17 20:44:23 +07:00
Artem Luzin m
f64f63641e fix: исправил ширину строки 2025-06-17 20:25:24 +07:00
Artem Luzin m
3895b9a7fc fix 2025-06-17 20:09:34 +07:00
Artem Luzin m
f7d4c02623 feat 2025-06-17 20:05:59 +07:00
110 changed files with 1224 additions and 3119 deletions

View File

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

View File

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

3
.gitignore vendored
View File

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

View File

@@ -1,6 +0,0 @@
<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>

View File

@@ -1,6 +0,0 @@
<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>

View File

@@ -1,6 +0,0 @@
<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>

View File

@@ -1,6 +0,0 @@
<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,23 +14,5 @@
"/// {@macro ${TM_FILENAME_BASE}}" "/// {@macro ${TM_FILENAME_BASE}}"
], ],
"description": "DartDoc короткая запись macro с именем файла" "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,12 +1,24 @@
{ {
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "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", "label": "build_runner --delete-conflicting-outputs",
"type": "shell", "type": "shell",
"command": "flutter", "command": "dart",
"args": [ "args": [
"pub",
"run", "run",
"build_runner", "build_runner",
"build", "build",
@@ -50,16 +62,6 @@
"type": "shell", "type": "shell",
"command": "./tools/switch_service.sh aurora", "command": "./tools/switch_service.sh aurora",
"problemMatcher": [] "problemMatcher": []
},
{
"label": "flutter gen-l10n",
"type": "shell",
"command": "flutter",
"args": [
"gen-l10n"
],
"group": "build",
"problemMatcher": [],
} }
] ]
} }

View File

View File

@@ -1,29 +1 @@
# Файл CODEOWNERS определяет владельцев кода для автоматического назначения ревьюеров Friflex LCC
# 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 MIT License
Copyright (c) 2025 Friflex LLC Copyright (c) 2025 Friflex
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,14 +1,21 @@
<div align="center">
# 🚀 Friflex Flutter Starter - Корпоративный шаблон # 🚀 Friflex Starter - Корпоративный шаблон
![Flutter](https://img.shields.io/badge/Flutter-3.38.1+-02569B?style=for-the-badge&logo=flutter&logoColor=white) </div>
![Dart](https://img.shields.io/badge/Dart-3.10.0+-0175C2?style=for-the-badge&logo=dart&logoColor=white) <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)
![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge) ![License](https://img.shields.io/badge/License-MIT-blue?style=for-the-badge)
Корпоративный стартовый шаблон для разработки масштабируемых Flutter-приложений **Корпоративный стартовый шаблон для разработки масштабируемых Flutter-приложений**
[📋 Документация](#-документация) • [🏗️ Архитектура](#-архитектура) • [🚀 Быстрый старт](#-быстрый-старт) • [🔧 Конфигурация](#-конфигурация) [📋 Документация](#-документация) • [🏗️ Архитектура](#-архитектура) • [🚀 Быстрый старт](#-быстрый-старт) • [🔧 Конфигурация](#-конфигурация)
</div>
--- ---
## 📖 Описание проекта ## 📖 Описание проекта
@@ -24,7 +31,6 @@
- 🌍 Поддержка интернационализации - 🌍 Поддержка интернационализации
- 🎨 UI Kit и система токенов дизайна - 🎨 UI Kit и система токенов дизайна
- 🔍 Инструменты отладки и мониторинга - 🔍 Инструменты отладки и мониторинга
- ⚡ Современный Dart 3.10+ с dot shorthands
## 🎯 Для чего нужен стартер ## 🎯 Для чего нужен стартер
@@ -91,27 +97,20 @@ features/
| Категория | Библиотека | Версия | Описание | | Категория | Библиотека | Версия | Описание |
|-----------|------------|--------|----------| |-----------|------------|--------|----------|
| 🧭 **Навигация** | [go_router](https://pub.dev/packages/go_router) | `17.0.0` | Декларативный роутинг | | 🧭 **Навигация** | [go_router](https://pub.dev/packages/go_router) | `15.1.2` | Декларативный роутинг |
| 🔄 **State Management** | [flutter_bloc](https://pub.dev/packages/flutter_bloc) | `9.1.1` | Управление состоянием | | 🔄 **State Management** | [flutter_bloc](https://pub.dev/packages/flutter_bloc) | `9.1.1` | Управление состоянием |
| 💉 **DI** | Custom InheritedWidget | - | Внедрение зависимостей | | 💉 **DI** | Custom InheritedWidget | - | Внедрение зависимостей |
| 🎨 **Resources** | [flutter_gen](https://pub.dev/packages/flutter_gen) | `5.12.0` | Генерация ресурсов | | 🎨 **Resources** | [flutter_gen](https://pub.dev/packages/flutter_gen) | `5.10.0` | Генерация ресурсов |
| 🌐 **HTTP** | [dio](https://pub.dev/packages/dio) | `5.9.0` | HTTP клиент | | 🌐 **HTTP** | [dio](https://pub.dev/packages/dio) | `5.8.0+1` | 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) | - | Защищенное хранилище | | 🔒 **Secure Storage** | [flutter_secure_storage](https://pub.dev/packages/flutter_secure_storage) | - | Защищенное хранилище |
| 📊 **Logging** | [talker](https://pub.dev/packages/talker_flutter) | `5.0.2` | Логирование и отладка | | 📊 **Logging** | [talker](https://pub.dev/packages/talker_flutter) | `4.8.0` | Логирование и отладка |
| 🎨 **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_lints: 6.0.0` | Корпоративные правила кода | - **🌍 Локализация**: `flutter_localizations` + `intl`
| **🏗️ Code Generation** | `build_runner: 2.10.3` | Генерация кода | - **⚙️ Окружения**: `envied` для управления переменными
| **🌍 Локализация** | `intl: 0.20.2` | Интернационализация |
| **⚙️ Environment** | `envied: 1.3.1` + `envied_generator: 1.3.1` | Управление переменными окружения |
| **🎨 Theme Generator** | `theme_tailor: 3.1.1` | Генерация тем |
## 🗂️ Структура проекта ## 🗂️ Структура проекта
@@ -163,33 +162,33 @@ enum AppEnv {
### 🛠️ Установка ### 🛠️ Установка
#### Клонирование проекта 1. **Клонирование проекта**
```bash ```bash
git clone https://github.com/smmarty/friflex_starter.git git clone https://github.com/smmarty/friflex_starter.git
cd friflex_starter cd friflex_starter
``` ```
#### Установка зависимостей 2. **Установка зависимостей**
```bash ```bash
flutter pub get flutter pub get
``` ```
#### Генерация файлов 3. **Генерация файлов**
```bash ```bash
dart run build_runner build --delete-conflicting-outputs dart run build_runner build --delete-conflicting-outputs
flutter packages pub run flutter_gen flutter packages pub run flutter_gen
``` ```
#### Запуск приложения 4. **Запуск приложения**
```bash ```bash
flutter run flutter run
``` ```
#### Замените название пакета на ваш 5. **Замените название пакета на ваш**
### 🎯 Запуск с разными окружениями ### 🎯 Запуск с разными окружениями
@@ -263,8 +262,6 @@ dart run build_runner build --delete-conflicting-outputs
| 🔄 [RFC-gitflow.md](./tools/rfc/RFC-gitflow.md) | Git workflow | | 🔄 [RFC-gitflow.md](./tools/rfc/RFC-gitflow.md) | Git workflow |
| 🏗️ [RFC-projects_structure.md](./tools/rfc/RFC-projects_structure.md) | Структура проекта | | 🏗️ [RFC-projects_structure.md](./tools/rfc/RFC-projects_structure.md) | Структура проекта |
| 📝 [RFC-documentation.md](./tools/rfc/RFC-documentation.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 |
## 🎯 Особенности и нюансы ## 🎯 Особенности и нюансы
@@ -289,10 +286,15 @@ dart run build_runner build --delete-conflicting-outputs
## 📄 Лицензия ## 📄 Лицензия
Этот проект распространяется под лицензией MIT License. Подробности смотрите в файле [LICENSE](./LICENSE). Этот проект распространяется под лицензией MIT License. Подробности смотрите в файле [LICENSE](./LICENCE).
Copyright © 2025 Friflex LLC. Все права защищены. Copyright © 2025 Friflex LLC. Все права защищены.
--- ---
### Разработано с любовью командой Friflex ❤️
<div align="right">
*Разработано с любовью командой Friflex ❤️*
</div>

View File

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

View File

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

View File

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

@@ -1,15 +1,31 @@
name: app_services name: app_services
description: "Аврора ОС сервисы для приложения" description: "Google сервисы для приложения"
version: 0.0.1 version: 0.0.1
publish_to: none publish_to: none
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ^3.8.0
dependencies: dependencies:
flutter: flutter:
sdk: 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
# для работы с путями в хранилища
path_provider: 2.1.4
path_provider_aurora:
git:
url: https://gitlab.com/omprussia/flutter/packages.git
ref: aurora-path_provider_aurora-0.6.0
path: packages/path_provider_aurora
# Обязательные интерфейсы # Обязательные интерфейсы
i_app_services: i_app_services:
path: ../../i_app_services path: ../../i_app_services

View File

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

View File

@@ -1,50 +0,0 @@
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

@@ -2,19 +2,15 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:i_app_services/i_app_services.dart'; import 'package:i_app_services/i_app_services.dart';
/// {@template app_secure_storage} /// {@template app_secure_storage}
/// Класс для базовой реализации сервиса работы с защищенным хранилищем. /// Класс для Aurora реализации сервис по работе с защищенным хранилищем
/// /// [secretKey] - ключ для шифрования данных, если нужен
/// Использует flutter_secure_storage для безопасного хранения данных.
/// Поддерживает все основные операции с защищенным хранилищем.
/// {@endtemplate} /// {@endtemplate}
final class AppSecureStorage implements ISecureStorage { final class AppSecureStorage implements ISecureStorage {
/// {@macro app_secure_storage}
AppSecureStorage({this.secretKey}); AppSecureStorage({this.secretKey});
@override @override
final String? secretKey; final String? secretKey;
/// Наименование сервиса
static const name = 'BaseAppSecureStorage'; static const name = 'BaseAppSecureStorage';
/// Экземпляр хранилища данных /// Экземпляр хранилища данных

View File

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

View File

@@ -1,29 +0,0 @@
# 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

@@ -1,10 +0,0 @@
# 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

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

View File

@@ -1,135 +0,0 @@
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

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

View File

@@ -1,18 +0,0 @@
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

@@ -1,18 +0,0 @@
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

@@ -1,50 +0,0 @@
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

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

View File

@@ -1,9 +0,0 @@
/// {@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'; static const name = 'IPathProvider';
/// Получение path на внутреннее хранилище приложения /// Получение path на внутренне хранилище приложения
Future<String?> getAppDocumentsDirectoryPath(); Future<String?> getAppDocumentsDirectoryPath();
} }

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

@@ -1,56 +1,33 @@
import 'package:flutter/material.dart'; 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_context_ext.dart';
import 'package:friflex_starter/app/app_providers.dart'; import 'package:friflex_starter/app/app_providers.dart';
import 'package:friflex_starter/app/depends_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/app_theme.dart';
import 'package:friflex_starter/app/theme/theme_notifier.dart'; import 'package:friflex_starter/app/theme/theme_notifier.dart';
import 'package:friflex_starter/di/di_container.dart'; import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/features/error/error_screen.dart'; import 'package:friflex_starter/features/error/error_screen.dart';
import 'package:friflex_starter/features/splash/splash_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/gen/app_localizations.dart';
import 'package:friflex_starter/l10n/localization_notifier.dart'; import 'package:friflex_starter/l10n/localization_notifier.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
/// {@template app} /// Класс приложения
/// Главный виджет приложения, управляющий инициализацией зависимостей
/// и отображением основного интерфейса приложения.
///
/// Отвечает за:
/// - Инициализацию зависимостей приложения
/// - Отображение экрана загрузки во время инициализации
/// - Обработку ошибок инициализации
/// - Настройку провайдеров для темы и локализации
/// {@endtemplate}
class App extends StatefulWidget { class App extends StatefulWidget {
/// {@macro app}
const App({required this.router, required this.initDependencies, super.key}); const App({required this.router, required this.initDependencies, super.key});
/// Роутер приложения для навигации между экранами /// Роутер приложения
final GoRouter router; final GoRouter router;
/// Функция для инициализации зависимостей приложения /// Функция для инициализации зависимостей
/// Возвращает Future с контейнером зависимостей
final Future<DiContainer> Function() initDependencies; final Future<DiContainer> Function() initDependencies;
@override @override
State<App> createState() => _AppState(); State<App> createState() => _AppState();
} }
/// {@template app_state}
/// Состояние главного виджета приложения.
///
/// Управляет процессом инициализации зависимостей и отображением
/// соответствующих экранов в зависимости от состояния инициализации.
/// {@endtemplate}
class _AppState extends State<App> { class _AppState extends State<App> {
/// {@macro app_state}
_AppState();
/// Мутабельная Future для инициализации зависимостей /// Мутабельная Future для инициализации зависимостей
/// Позволяет перезапускать инициализацию при ошибках
late Future<DiContainer> _initFuture; late Future<DiContainer> _initFuture;
@override @override
@@ -69,30 +46,43 @@ class _AppState extends State<App> {
builder: () => FutureBuilder<DiContainer>( builder: () => FutureBuilder<DiContainer>(
future: _initFuture, future: _initFuture,
builder: (_, snapshot) { builder: (_, snapshot) {
return switch (snapshot.connectionState) { switch (snapshot.connectionState) {
// Если состояние не определено, ожидается или активно, то отображаем экран загрузки case ConnectionState.none:
ConnectionState.none || case ConnectionState.waiting:
ConnectionState.waiting || case ConnectionState.active:
ConnectionState.active => const SplashScreen(), // Пока инициализация показываем Splash
ConnectionState.done => return const SplashScreen();
// Если данные получены и не равны null, то отображаем внутренний виджет приложения case ConnectionState.done:
// Иначе отображаем экран ошибки if (snapshot.hasError) {
(snapshot.hasData && snapshot.data != null) return ErrorScreen(
? _App(router: widget.router, diContainer: snapshot.data!) error: snapshot.error,
: ErrorScreen( stackTrace: snapshot.stackTrace,
error: snapshot.error, onRetry: _retryInit,
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),
),
);
}
}, },
), ),
), ),
); );
} }
/// Метод для перезапуска инициализации зависимостей
/// Вызывается при ошибках инициализации для повторной попытки
void _retryInit() { void _retryInit() {
setState(() { setState(() {
_initFuture = widget.initDependencies(); _initFuture = widget.initDependencies();
@@ -100,58 +90,21 @@ class _AppState extends State<App> {
} }
} }
/// {@template app_internal}
/// Внутренний виджет приложения, отображающий основной интерфейс
/// после успешной инициализации зависимостей.
///
/// Настраивает MaterialApp с роутером, темами и локализацией.
/// {@endtemplate}
class _App extends StatelessWidget { class _App extends StatelessWidget {
/// {@macro app_internal} const _App({required this.router});
const _App({required this.router, required this.diContainer});
/// Роутер приложения для навигации
final GoRouter router; final GoRouter router;
/// Контейнер зависимостей
final DiContainer diContainer;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DependsProviders( return MaterialApp.router(
diContainer: diContainer, routerConfig: router,
child: BlocConsumer<UpdateCubit, UpdateState>( darkTheme: AppTheme.dark,
listener: (context, state) { theme: AppTheme.light,
if (state is UpdateSuccessState && themeMode: context.theme.themeMode,
state.updateInfo.updateType == .hard && locale: context.localization.locale,
context.mounted) { localizationsDelegates: AppLocalizations.localizationsDelegates,
router.goNamed(UpdateRoutes.hardUpdateScreenName); supportedLocales: AppLocalizations.supportedLocales,
}
},
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,
themeMode: context.theme.themeMode,
locale: context.localization.locale,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
),
);
},
),
); );
} }
} }

View File

@@ -1,47 +1,14 @@
import 'package:envied/envied.dart'; import 'package:envied/envied.dart';
import 'package:friflex_starter/app/app_config/i_app_config.dart';
import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/app/app_env.dart';
part 'app_config.g.dart'; part 'app_config.g.dart';
/// {@template i_app_config} /// Класс для реализации конфигурации с моковыми данными
/// Интерфейс для конфигурации приложения.
///
/// Определяет обязательные параметры для всех реализаций конфигурации:
/// - Наименование конфигурации
/// - Базовый URL для API
/// - Тип окружения (dev, prod, stage)
/// - Секретный ключ для шифрования данных
/// {@endtemplate}
abstract interface class IAppConfig {
/// {@macro i_app_config}
IAppConfig();
/// Наименование сервиса конфигурации
String get name => 'IAppConfig';
/// Основной адрес для запросов к API
String get baseUrl;
/// Тип окружения (dev, prod, stage)
AppEnv get env;
/// Секретный ключ для шифрования данных
String get secretKey;
}
/// {@template app_config_dev}
/// Класс для реализации конфигурации приложения в режиме разработки.
///
/// Использует переменные окружения из файла env/dev.env.
/// Предназначен для локальной разработки и тестирования.
/// {@endtemplate}
@Envied(name: 'Dev', path: 'env/dev.env') @Envied(name: 'Dev', path: 'env/dev.env')
class AppConfigDev implements IAppConfig { class AppConfigDev implements IAppConfig {
/// {@macro app_config_dev}
AppConfigDev();
@override @override
AppEnv get env => .dev; AppEnv get env => AppEnv.dev;
@override @override
String get name => 'AppConfigDev'; String get name => 'AppConfigDev';
@@ -55,19 +22,11 @@ class AppConfigDev implements IAppConfig {
final String secretKey = _Dev.secretKey; final String secretKey = _Dev.secretKey;
} }
/// {@template app_config_prod} /// Класс для реализации конфигурации с продакшн данными
/// Класс для реализации конфигурации приложения в продакшн режиме.
///
/// Использует переменные окружения из файла env/prod.env.
/// Предназначен для финальной сборки приложения.
/// {@endtemplate}
@Envied(name: 'Prod', path: 'env/prod.env') @Envied(name: 'Prod', path: 'env/prod.env')
class AppConfigProd implements IAppConfig { class AppConfigProd implements IAppConfig {
/// {@macro app_config_prod}
AppConfigProd();
@override @override
AppEnv get env => .prod; AppEnv get env => AppEnv.prod;
@override @override
String get name => 'AppConfigProd'; String get name => 'AppConfigProd';
@@ -81,19 +40,11 @@ class AppConfigProd implements IAppConfig {
final String secretKey = _Prod.secretKey; final String secretKey = _Prod.secretKey;
} }
/// {@template app_config_stage} /// Класс для реализации конфигурации с стейдж данными
/// Класс для реализации конфигурации приложения в стейдж режиме.
///
/// Использует переменные окружения из файла env/stage.env.
/// Предназначен для тестирования в среде, близкой к продакшн.
/// {@endtemplate}
@Envied(name: 'Stage', path: 'env/stage.env') @Envied(name: 'Stage', path: 'env/stage.env')
class AppConfigStage implements IAppConfig { class AppConfigStage implements IAppConfig {
/// {@macro app_config_stage}
AppConfigStage();
@override @override
AppEnv get env => .stage; AppEnv get env => AppEnv.stage;
@override @override
String get name => 'AppConfigStage'; String get name => 'AppConfigStage';

View File

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

View File

@@ -0,0 +1,16 @@
import 'package:friflex_starter/app/app_env.dart';
/// Класс для описания интерфейса конфигурации
abstract interface class IAppConfig {
/// Наименование сервиса
String get name => 'IAppConfig';
/// Основной адрес для запросов к API
String get baseUrl;
/// Тип окружения
AppEnv get env;
/// Секретный ключ для шифрования данных
String get secretKey;
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:friflex_starter/app/theme/app_colors_scheme.dart';
import 'package:friflex_starter/app/theme/theme_notifier.dart'; import 'package:friflex_starter/app/theme/theme_notifier.dart';
import 'package:friflex_starter/di/di_container.dart'; import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/l10n/gen/app_localizations.dart'; import 'package:friflex_starter/l10n/gen/app_localizations.dart';
@@ -10,6 +11,9 @@ extension AppContextExt on BuildContext {
/// Метод для получения экземпляра DIContainer /// Метод для получения экземпляра DIContainer
DiContainer get di => read<DiContainer>(); DiContainer get di => read<DiContainer>();
/// Геттер для получения цветовой схемы
AppColors get colors => Theme.of(this).extension<AppColors>()!;
/// Геттер для получения темы /// Геттер для получения темы
ThemeNotifier get theme => read<ThemeNotifier>(); ThemeNotifier get theme => read<ThemeNotifier>();

View File

@@ -1,9 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:friflex_starter/di/di_container.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'; import 'package:provider/provider.dart';
/// Класс для внедрения глобальных зависимостей /// Класс для внедрения глобальных зависимостей
@@ -23,21 +19,6 @@ final class DependsProviders extends StatelessWidget {
providers: [ providers: [
// Сюда добавляем глобальные блоки, inherited и т.д. // Сюда добавляем глобальные блоки, inherited и т.д.
Provider.value(value: diContainer), // Передаем контейнер зависимостей 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, child: child,
); );

View File

@@ -1,5 +1,5 @@
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:friflex_starter/app/app_config/app_config.dart'; import 'package:friflex_starter/app/app_config/i_app_config.dart';
import 'package:friflex_starter/app/http/i_http_client.dart'; import 'package:friflex_starter/app/http/i_http_client.dart';
import 'package:friflex_starter/features/debug/i_debug_service.dart'; import 'package:friflex_starter/features/debug/i_debug_service.dart';

View File

@@ -1,13 +1,10 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:theme_tailor_annotation/theme_tailor_annotation.dart';
part 'app_colors_scheme.tailor.dart';
/// {@template app_colors} /// {@template app_colors}
/// Класс, реализующий расширение для добавления токенов в цветовую схему /// Класс, реализующий расширение для добавления токенов в цветовую схему
/// {@endtemplate} /// {@endtemplate}
@TailorMixin(themeGetter: ThemeGetter.onBuildContext) class AppColors extends ThemeExtension<AppColors> with DiagnosticableTreeMixin {
class AppColors extends ThemeExtension<AppColors> with _$AppColorsTailorMixin {
/// {@macro app_colors} /// {@macro app_colors}
/// ///
/// Принимает: /// Принимает:
@@ -26,40 +23,81 @@ class AppColors extends ThemeExtension<AppColors> with _$AppColorsTailorMixin {
}); });
/// Цвет тестовый /// Цвет тестовый
@override
final Color testColor; final Color testColor;
/// Цвет элемента текста /// Цвет элемента текста
@override
final Color itemTextColor; final Color itemTextColor;
/// Цвет фона снекбара ошибки /// Цвет фона снекбара ошибки
@override
final Color errorSnackbarBackground; final Color errorSnackbarBackground;
/// Цвет фона снекбара успеха /// Цвет фона снекбара успеха
@override
final Color successSnackbarBackground; final Color successSnackbarBackground;
/// Цвет фона снекбара информации /// Цвет фона снекбара информации
@override
final Color infoSnackbarBackground; final Color infoSnackbarBackground;
/// Цвета светлой темы /// Цвета светлой темы
static const AppColors light = AppColors( static final AppColors light = AppColors(
testColor: Colors.red, testColor: Colors.red,
errorSnackbarBackground: Color(0xFFD24720), errorSnackbarBackground: const Color(0xFFD24720),
successSnackbarBackground: Color(0xFF6FB62C), successSnackbarBackground: const Color(0xFF6FB62C),
infoSnackbarBackground: .fromARGB(255, 220, 108, 77), infoSnackbarBackground: const Color.fromARGB(255, 220, 108, 77),
itemTextColor: Color(0xFFFAF3EB), itemTextColor: const Color(0xFFFAF3EB),
); );
/// Цвета тёмной темы /// Цвета тёмной темы
static const AppColors dark = AppColors( static final AppColors dark = AppColors(
testColor: Colors.green, testColor: Colors.green,
errorSnackbarBackground: Color(0xFF638B8B), errorSnackbarBackground: const Color(0xFF638B8B),
successSnackbarBackground: Color(0xFF93C499), successSnackbarBackground: const Color(0xFF93C499),
infoSnackbarBackground: .fromARGB(255, 35, 147, 178), infoSnackbarBackground: const Color.fromARGB(255, 35, 147, 178),
itemTextColor: Colors.white, itemTextColor: Colors.white,
); );
@override
ThemeExtension<AppColors> copyWith({
Color? testColor,
Color? errorSnackbarBackground,
Color? successSnackbarBackground,
Color? infoSnackbarBackground,
Color? itemTextColor,
}) => AppColors(
testColor: testColor ?? this.testColor,
errorSnackbarBackground:
errorSnackbarBackground ?? this.errorSnackbarBackground,
successSnackbarBackground:
successSnackbarBackground ?? this.successSnackbarBackground,
infoSnackbarBackground:
infoSnackbarBackground ?? this.infoSnackbarBackground,
itemTextColor: itemTextColor ?? this.itemTextColor,
);
@override
ThemeExtension<AppColors> lerp(
covariant ThemeExtension<AppColors>? other,
double t,
) {
if (other is! AppColors) return this;
return AppColors(
testColor: Color.lerp(testColor, other.testColor, t)!,
errorSnackbarBackground: Color.lerp(
errorSnackbarBackground,
other.errorSnackbarBackground,
t,
)!,
successSnackbarBackground: Color.lerp(
successSnackbarBackground,
other.successSnackbarBackground,
t,
)!,
infoSnackbarBackground: Color.lerp(
infoSnackbarBackground,
other.infoSnackbarBackground,
t,
)!,
itemTextColor: Color.lerp(itemTextColor, other.itemTextColor, t)!,
);
}
} }

View File

@@ -1,102 +0,0 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'app_colors_scheme.dart';
// **************************************************************************
// TailorAnnotationsGenerator
// **************************************************************************
mixin _$AppColorsTailorMixin on ThemeExtension<AppColors> {
Color get testColor;
Color get itemTextColor;
Color get errorSnackbarBackground;
Color get successSnackbarBackground;
Color get infoSnackbarBackground;
@override
AppColors copyWith({
Color? testColor,
Color? itemTextColor,
Color? errorSnackbarBackground,
Color? successSnackbarBackground,
Color? infoSnackbarBackground,
}) {
return AppColors(
testColor: testColor ?? this.testColor,
itemTextColor: itemTextColor ?? this.itemTextColor,
errorSnackbarBackground:
errorSnackbarBackground ?? this.errorSnackbarBackground,
successSnackbarBackground:
successSnackbarBackground ?? this.successSnackbarBackground,
infoSnackbarBackground:
infoSnackbarBackground ?? this.infoSnackbarBackground,
);
}
@override
AppColors lerp(covariant ThemeExtension<AppColors>? other, double t) {
if (other is! AppColors) return this as AppColors;
return AppColors(
testColor: Color.lerp(testColor, other.testColor, t)!,
itemTextColor: Color.lerp(itemTextColor, other.itemTextColor, t)!,
errorSnackbarBackground: Color.lerp(
errorSnackbarBackground,
other.errorSnackbarBackground,
t,
)!,
successSnackbarBackground: Color.lerp(
successSnackbarBackground,
other.successSnackbarBackground,
t,
)!,
infoSnackbarBackground: Color.lerp(
infoSnackbarBackground,
other.infoSnackbarBackground,
t,
)!,
);
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is AppColors &&
const DeepCollectionEquality().equals(testColor, other.testColor) &&
const DeepCollectionEquality().equals(
itemTextColor,
other.itemTextColor,
) &&
const DeepCollectionEquality().equals(
errorSnackbarBackground,
other.errorSnackbarBackground,
) &&
const DeepCollectionEquality().equals(
successSnackbarBackground,
other.successSnackbarBackground,
) &&
const DeepCollectionEquality().equals(
infoSnackbarBackground,
other.infoSnackbarBackground,
));
}
@override
int get hashCode {
return Object.hash(
runtimeType.hashCode,
const DeepCollectionEquality().hash(testColor),
const DeepCollectionEquality().hash(itemTextColor),
const DeepCollectionEquality().hash(errorSnackbarBackground),
const DeepCollectionEquality().hash(successSnackbarBackground),
const DeepCollectionEquality().hash(infoSnackbarBackground),
);
}
}
extension AppColorsBuildContext on BuildContext {
AppColors get appColors => Theme.of(this).extension<AppColors>()!;
}

View File

@@ -1,20 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
/// Тип функции для построения виджета с учетом темы
typedef ThemeBuilder = Widget Function(); typedef ThemeBuilder = Widget Function();
/// {@template theme_consumer} /// Виджет для подписки на изменение темы приложения
/// Виджет для подписки на изменения темы приложения.
///
/// Автоматически перестраивает дочерние виджеты при изменении темы,
/// обеспечивая реактивность интерфейса к изменениям настроек темы.
/// {@endtemplate}
class ThemeConsumer extends StatelessWidget { class ThemeConsumer extends StatelessWidget {
/// {@macro theme_consumer}
const ThemeConsumer({required this.builder, super.key}); const ThemeConsumer({required this.builder, super.key});
/// Функция для построения виджета с учетом текущей темы
final ThemeBuilder builder; final ThemeBuilder builder;
@override @override
@@ -27,29 +19,12 @@ class ThemeConsumer extends StatelessWidget {
} }
} }
/// {@template theme_notifier} /// Класс для управления темой приложения
/// Класс для управления темой приложения.
///
/// Отвечает за:
/// - Хранение текущего режима темы (светлая/темная/системная)
/// - Уведомление подписчиков об изменениях темы
/// - Переключение между режимами темы
/// {@endtemplate}
final class ThemeNotifier extends ChangeNotifier { final class ThemeNotifier extends ChangeNotifier {
/// {@macro theme_notifier}
ThemeNotifier();
/// Текущий режим темы приложения
/// По умолчанию используется системная тема
ThemeMode _themeMode = ThemeMode.system; ThemeMode _themeMode = ThemeMode.system;
/// Получение текущего режима темы
ThemeMode get themeMode => _themeMode; ThemeMode get themeMode => _themeMode;
/// Метод для переключения темы приложения.
///
/// Переключает между светлой и темной темой.
/// Если текущая тема светлая, переключает на темную и наоборот.
void changeTheme() { void changeTheme() {
_themeMode = _themeMode == ThemeMode.light _themeMode = _themeMode == ThemeMode.light
? ThemeMode.dark ? ThemeMode.dark

View File

@@ -1,10 +1,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
/// {@template h_box} /// {@template h_box}
/// Виджет для создания вертикального отступа. /// HBox виджет для вертикального отступа (Надстройка над SizedBox)
///
/// Надстройка над SizedBox, предназначенная для создания
/// отступов по вертикали с более понятным названием.
/// {@endtemplate} /// {@endtemplate}
class HBox extends SizedBox { class HBox extends SizedBox {
/// {@macro h_box} /// {@macro h_box}
@@ -12,10 +9,7 @@ class HBox extends SizedBox {
} }
/// {@template w_box} /// {@template w_box}
/// Виджет для создания горизонтального отступа. /// WBox виджет для вертикального отступа (Надстройка над SizedBox)
///
/// Надстройка над SizedBox, предназначенная для создания
/// отступов по горизонтали с более понятным названием.
/// {@endtemplate} /// {@endtemplate}
class WBox extends SizedBox { class WBox extends SizedBox {
/// {@macro w_box} /// {@macro w_box}

View File

@@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:friflex_starter/app/theme/app_colors_scheme.dart'; import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart'; import 'package:friflex_starter/app/ui_kit/app_box.dart';
/// {@template app_snackbar} /// {@template app_snackbar}
@@ -63,7 +63,7 @@ class AppSnackBar extends StatefulWidget {
_show( _show(
context: context, context: context,
message: message, message: message,
type: .error, type: TypeSnackBar.error,
displayDuration: displayDuration, displayDuration: displayDuration,
); );
} }
@@ -81,7 +81,7 @@ class AppSnackBar extends StatefulWidget {
_show( _show(
context: context, context: context,
message: message, message: message,
type: .info, type: TypeSnackBar.info,
displayDuration: displayDuration, displayDuration: displayDuration,
); );
} }
@@ -99,7 +99,7 @@ class AppSnackBar extends StatefulWidget {
_show( _show(
context: context, context: context,
message: message, message: message,
type: .success, type: TypeSnackBar.success,
displayDuration: displayDuration, displayDuration: displayDuration,
); );
} }
@@ -180,11 +180,11 @@ class _AppSnackBarState extends State<AppSnackBar>
CurvedAnimation(parent: _animationController, curve: Curves.easeOut), CurvedAnimation(parent: _animationController, curve: Curves.easeOut),
); );
unawaited(_animationController.forward()); _animationController.forward();
} }
/// Запуск таймера для автоматического закрытия снекбара /// Запуск таймера для автоматического закрытия снекбара
/// Таймер срабатывает по истечении widget.displayDuration /// Таймер срабатывает по истечении [widget.displayDuration]
/// и вызывает метод [_dismiss] для закрытия снекбара /// и вызывает метод [_dismiss] для закрытия снекбара
void _startDismissTimer() { void _startDismissTimer() {
_dismissTimer = Timer(widget.displayDuration, _dismiss); _dismissTimer = Timer(widget.displayDuration, _dismiss);
@@ -192,19 +192,17 @@ class _AppSnackBarState extends State<AppSnackBar>
/// Закрытие снекбара /// Закрытие снекбара
/// Отменяет таймер, если он существует, и запускает обратную анимацию /// Отменяет таймер, если он существует, и запускает обратную анимацию
/// После завершения анимации вызывает функцию widget.onDismiss, если она задана /// После завершения анимации вызывает функцию [widget.onDismiss], если она задана
/// Если виджет не смонтирован, ничего не делает /// Если виджет не смонтирован, ничего не делает
void _dismiss() { void _dismiss() {
if (!mounted) return; if (!mounted) return;
_dismissTimer?.cancel(); _dismissTimer?.cancel();
unawaited( _animationController.reverse().then((_) {
_animationController.reverse().then((_) { if (mounted) {
if (mounted) { widget.onDismiss?.call();
widget.onDismiss?.call(); }
} });
}),
);
} }
@override @override
@@ -245,7 +243,7 @@ class _AppSnackBarState extends State<AppSnackBar>
Flexible( Flexible(
child: Text( child: Text(
widget.message, widget.message,
style: const TextStyle(color: Colors.white), style: TextStyle(color: Colors.white),
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
@@ -266,9 +264,9 @@ class _AppSnackBarState extends State<AppSnackBar>
/// [TypeSnackBar.error] - цвет ошибки /// [TypeSnackBar.error] - цвет ошибки
Color _getBackgroundColor(TypeSnackBar type) { Color _getBackgroundColor(TypeSnackBar type) {
return switch (type) { return switch (type) {
.success => context.appColors.successSnackbarBackground, TypeSnackBar.success => context.colors.successSnackbarBackground,
.error => context.appColors.errorSnackbarBackground, TypeSnackBar.error => context.colors.errorSnackbarBackground,
.info => context.appColors.infoSnackbarBackground, TypeSnackBar.info => context.colors.infoSnackbarBackground,
}; };
} }
} }
@@ -290,21 +288,13 @@ class _Icon extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return switch (type) { return switch (type) {
.success => const Icon( TypeSnackBar.success => Icon(
Icons.check_circle, Icons.check_circle,
color: Colors.white, color: Colors.white,
size: 32, size: 32,
), ),
.error => const Icon( TypeSnackBar.error => Icon(Icons.error, color: Colors.white, size: 32),
Icons.error, TypeSnackBar.info => Icon(Icons.info, color: Colors.white, size: 32),
color: Colors.white,
size: 32,
),
.info => const Icon(
Icons.info,
color: Colors.white,
size: 32,
),
}; };
} }
} }

View File

@@ -1,17 +1,6 @@
/// {@template di_base_repo} /// Миксин репозитория в приложении.
/// Базовый миксин для всех репозиториев в приложении. /// Каждый интерфейс репозитория в приложении должен подмешивать текущий класс
///
/// Предоставляет общую функциональность для всех репозиториев:
/// - Уникальное наименование репозитория
/// - Базовую структуру для DI контейнера
///
/// Каждый репозиторий в приложении должен использовать этот миксин
/// для обеспечения совместимости с системой зависимостей.
/// {@endtemplate}
mixin class DiBaseRepo { mixin class DiBaseRepo {
/// {@macro di_base_repo} /// Наименование репозитория
DiBaseRepo();
/// Наименование репозитория для идентификации в DI контейнере
String get name => 'DiBaseRepo'; String get name => 'DiBaseRepo';
} }

View File

@@ -1,4 +1,5 @@
import 'package:friflex_starter/app/app_config/app_config.dart'; import 'package:friflex_starter/app/app_config/app_config.dart';
import 'package:friflex_starter/app/app_config/i_app_config.dart';
import 'package:friflex_starter/app/app_env.dart'; import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/app/http/app_http_client.dart'; import 'package:friflex_starter/app/http/app_http_client.dart';
import 'package:friflex_starter/app/http/i_http_client.dart'; import 'package:friflex_starter/app/http/i_http_client.dart';
@@ -41,9 +42,9 @@ final class DiContainer {
}) async { }) async {
// Инициализация конфигурации приложения // Инициализация конфигурации приложения
appConfig = switch (env) { appConfig = switch (env) {
.dev => AppConfigDev(), AppEnv.dev => AppConfigDev(),
.prod => AppConfigProd(), AppEnv.prod => AppConfigProd(),
.stage => AppConfigStage(), AppEnv.stage => AppConfigStage(),
}; };
// Инициализация HTTP клиента // Инициализация HTTP клиента

View File

@@ -11,40 +11,26 @@ 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_mock_repository.dart';
import 'package:friflex_starter/features/profile/data/repository/profile_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/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 окружения
/// ///
/// Для того, чтобы репозиторий был автоматически подменен на моковый в stage /// Для того, чтобы репозиторий был автоматически подменен на моковый в stage
/// сборке, необходимо в этом списке указать тип интерфейса репозитория /// сборке, необходимо в этом списке указать название мокового репозитория,
/// обращаясь к соответствующему полю name.
/// ///
/// Пример: /// Пример:
/// ``` /// ```
/// <Type>{ IUpdateRepository } /// [ AuthCheckRepositoryMock().name, ]
/// ``` /// ```
const _mockReposToSwitch = <Type>{IUpdateRepository}; final List<String> _mockReposToSwitch = [];
/// {@template di_repositories} /// Класс для инициализации репозиториев в приложении
/// Класс для инициализации и управления репозиториями приложения.
/// ///
/// Отвечает за: /// По умолчанию репозиторию присваивается моковая реализация.
/// - Инициализацию репозиториев для работы с данными /// В зависимости от окружения либо выполняется подмена репозиторий,
/// - Автоматическое переключение между моковыми и реальными репозиториями /// либо используется моковый.
/// - Уведомление о прогрессе инициализации
/// - Обработку ошибок инициализации репозиториев
///
/// Стратегия инициализации по окружениям:
/// - dev: всегда используются моковые репозитории
/// - prod: всегда используются реальные репозитории
/// - stage: используются моковые репозитории из списка _mockReposToSwitch
/// {@endtemplate}
final class DiRepositories { final class DiRepositories {
/// {@macro di_repositories}
DiRepositories();
/// Интерфейс для работы с репозиторием авторизации /// Интерфейс для работы с репозиторием авторизации
late final IAuthRepository authRepository; late final IAuthRepository authRepository;
@@ -54,77 +40,81 @@ final class DiRepositories {
/// Интерфейс для работы с репозиторием профиля /// Интерфейс для работы с репозиторием профиля
late final IProfileRepository profileRepository; late final IProfileRepository profileRepository;
/// Интерфейс для работы с репозиторием обновлений /// Метод для инициализации репозиториев в приложении
late final IUpdateRepository updatesRepository;
/// Метод для инициализации репозиториев в приложении.
/// ///
/// Принимает: /// Принимает:
/// - [onProgress] - обратный вызов для уведомления о прогрессе инициализации /// - [onProgress] - обратный вызов при прогрессе
/// - [diContainer] - контейнер зависимостей с конфигурацией приложения /// - [diContainer] - контейнер зависимостей
/// - [onError] - обратный вызов для обработки ошибок инициализации
///
/// Последовательность инициализации:
/// 1. Инициализация репозитория авторизации
/// 2. Инициализация репозитория главного сервиса
/// 3. Инициализация репозитория профиля
void init({ void init({
required OnProgress onProgress, required OnProgress onProgress,
required OnError onError, required OnError onError,
required DiContainer diContainer, required DiContainer diContainer,
}) { }) {
onProgress('Начинаем инициализацию репозиториев...'); try {
//Инициализация репозитория авторизации
// Инициализация репозитория обновлений authRepository = _lazyInitRepo<IAuthRepository>(
updatesRepository = _lazyInitRepo<IUpdateRepository>( mockFactory: AuthMockRepository.new,
mockFactory: UpdateMockRepository.new, mainFactory: () => AuthRepository(
mainFactory: UpdateRepository.new, httpClient: diContainer.httpClientFactory(
onProgress: onProgress, diContainer.debugService,
onError: onError, diContainer.appConfig,
environment: diContainer.env, ),
);
// Инициализация репозитория авторизации
authRepository = _lazyInitRepo<IAuthRepository>(
mockFactory: AuthMockRepository.new,
mainFactory: () => AuthRepository(
httpClient: diContainer.httpClientFactory(
diContainer.debugService,
diContainer.appConfig,
), ),
), onProgress: onProgress,
onProgress: onProgress, environment: diContainer.env,
onError: onError, );
environment: diContainer.env, onProgress(authRepository.name);
); } on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория IAuthRepository',
error,
stackTrace,
);
}
// Инициализация репозитория сервиса управления токеном доступа try {
mainRepository = _lazyInitRepo<IMainRepository>( // Инициализация репозитория сервиса управления токеном доступа
mockFactory: MainMockRepository.new, mainRepository = _lazyInitRepo<IMainRepository>(
mainFactory: () => MainRepository( mockFactory: MainMockRepository.new,
httpClient: diContainer.httpClientFactory( mainFactory: () => MainRepository(
diContainer.debugService, httpClient: diContainer.httpClientFactory(
diContainer.appConfig, diContainer.debugService,
diContainer.appConfig,
),
), ),
), onProgress: onProgress,
onProgress: onProgress, environment: diContainer.env,
onError: onError, );
environment: diContainer.env, onProgress(mainRepository.name);
); } on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория IMainRepository',
error,
stackTrace,
);
}
// Инициализация репозитория профиля try {
profileRepository = _lazyInitRepo<IProfileRepository>( // Инициализация репозитория профиля
mockFactory: ProfileMockRepository.new, profileRepository = _lazyInitRepo<IProfileRepository>(
mainFactory: () => ProfileRepository( mockFactory: ProfileMockRepository.new,
httpClient: diContainer.httpClientFactory( mainFactory: () => ProfileRepository(
diContainer.debugService, httpClient: diContainer.httpClientFactory(
diContainer.appConfig, diContainer.debugService,
diContainer.appConfig,
),
), ),
), onProgress: onProgress,
onProgress: onProgress, environment: diContainer.env,
onError: onError, );
environment: diContainer.env, onProgress(profileRepository.name);
); } on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации репозитория IProfileRepository',
error,
stackTrace,
);
}
onProgress( onProgress(
'Инициализация репозиториев завершена! Было подменено репозиториев - ${_mockReposToSwitch.length} (${_mockReposToSwitch.join(', ')})', 'Инициализация репозиториев завершена! Было подменено репозиториев - ${_mockReposToSwitch.length} (${_mockReposToSwitch.join(', ')})',
@@ -135,39 +125,26 @@ final class DiRepositories {
/// В зависимости от окружения инициализируется моковый или сетевой репозиторий. /// В зависимости от окружения инициализируется моковый или сетевой репозиторий.
/// ///
/// Принимает: /// Принимает:
/// - [mockFactory] - функция-фабрика для инициализации мокового репозитория /// - [mockFactory] - функция - фабрика для инициализации репозитория для управления моковыми запросами
/// - [mainFactory] - функция-фабрика для инициализации основного репозитория /// - [mainFactory] - функция - фабрика для инициализации основного репозиторий
/// - [onProgress] - обратный вызов для уведомления о прогрессе /// - [onProgress] - обратный вызов при прогрессе
/// - [onError] - обратный вызов для обработки ошибок инициализации
/// - [environment] - окружение приложения для определения стратегии инициализации
///
/// Возвращает:
/// - Экземпляр репозитория в зависимости от окружения
///
/// Throws:
/// - Перебрасывает исключение, если инициализация репозитория завершилась с ошибкой
T _lazyInitRepo<T extends DiBaseRepo>({ T _lazyInitRepo<T extends DiBaseRepo>({
required AppEnv environment, required AppEnv environment,
required T Function() mainFactory, required T Function() mainFactory,
required T Function() mockFactory, required T Function() mockFactory,
required OnProgress onProgress, required OnProgress onProgress,
required OnError onError,
}) { }) {
try { final mockRepo = mockFactory();
final repo = switch (environment) { final mainRepo = mainFactory();
.dev => mockFactory(),
.prod => mainFactory(),
.stage =>
_mockReposToSwitch.contains(T) ? mockFactory() : mainFactory(),
};
// throw Exception('Тестовая - ошибка инициализации репозитория $T'); final repo = switch (environment) {
onProgress(repo.name); AppEnv.dev => mockRepo,
return repo; AppEnv.prod => mainRepo,
} on Object catch (error, stackTrace) { AppEnv.stage =>
onError('Ошибка инициализации репозитория $T', error, stackTrace); _mockReposToSwitch.contains(mockRepo.name) ? mockRepo : mainRepo,
// Перебрасываем исключение дальше, чтобы не скрыть ошибку инициализации };
rethrow;
} onProgress(repo.name);
return repo;
} }
} }

View File

@@ -3,45 +3,26 @@ import 'package:friflex_starter/di/di_container.dart';
import 'package:friflex_starter/di/di_typedefs.dart'; import 'package:friflex_starter/di/di_typedefs.dart';
import 'package:i_app_services/i_app_services.dart'; import 'package:i_app_services/i_app_services.dart';
/// {@template di_services} /// Класс для инициализации сервисов
/// Класс для инициализации и управления сервисами приложения.
///
/// Отвечает за:
/// - Инициализацию сервисов для работы с путями
/// - Инициализацию сервисов для работы с защищенным хранилищем
/// - Уведомление о прогрессе инициализации
/// - Обработку ошибок инициализации сервисов
/// {@endtemplate}
final class DiServices { final class DiServices {
/// {@macro di_services} /// Сервис для работы с путями
DiServices();
/// Сервис для работы с путями файловой системы
late final IPathProvider pathProvider; late final IPathProvider pathProvider;
/// Сервис для работы с защищенным локальным хранилищем /// Сервис для работы с локальным хранилищем
late final ISecureStorage secureStorage; late final ISecureStorage secureStorage;
/// Сервис для работы с геолокацией /// Метод для инициализации репозиториев в приложении
late final ILocationService locationService;
/// Метод для инициализации сервисов в приложении.
/// ///
/// Принимает: /// Принимает:
/// - [onProgress] - обратный вызов для уведомления о прогрессе инициализации /// - [onProgress] - обратный вызов при прогрессе
/// - [diContainer] - контейнер зависимостей с конфигурацией приложения /// - [diContainer] - контейнер зависимостей
/// - [onError] - обратный вызов для обработки ошибок инициализации /// - [onError] - обратный вызов при ошибке
///
/// Последовательность инициализации:
/// 1. Инициализация сервиса путей (AppPathProvider)
/// 2. Инициализация защищенного хранилища (AppSecureStorage)
void init({ void init({
required OnProgress onProgress, required OnProgress onProgress,
required OnError onError, required OnError onError,
required DiContainer diContainer, required DiContainer diContainer,
}) { }) {
try { try {
// throw Exception('Тестовая - ошибка инициализации сервиса путей');
pathProvider = const AppPathProvider(); pathProvider = const AppPathProvider();
onProgress(AppPathProvider.name); onProgress(AppPathProvider.name);
} on Object catch (error, stackTrace) { } on Object catch (error, stackTrace) {
@@ -56,17 +37,6 @@ final class DiServices {
onError('Ошибка инициализации ${ISecureStorage.name}', error, stackTrace); onError('Ошибка инициализации ${ISecureStorage.name}', error, stackTrace);
} }
try {
locationService = const AppLocationService();
onProgress(AppLocationService.name);
} on Object catch (error, stackTrace) {
onError(
'Ошибка инициализации ${ILocationService.name}',
error,
stackTrace,
);
}
onProgress('Инициализация сервисов завершена!'); onProgress('Инициализация сервисов завершена!');
} }
} }

View File

@@ -1,17 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// {@template auth_screen} /// {@template AuthScreen}
/// Экран авторизации пользователя.
/// ///
/// Отвечает за:
/// - Отображение формы входа в приложение
/// - Обработку процесса аутентификации
/// - Навигацию после успешной авторизации
///
/// В текущей реализации является заглушкой для будущей функциональности.
/// {@endtemplate} /// {@endtemplate}
class AuthScreen extends StatelessWidget { class AuthScreen extends StatelessWidget {
/// {@macro auth_screen} /// {@macro AuthScreen}
const AuthScreen({super.key}); const AuthScreen({super.key});
@override @override

View File

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

View File

@@ -1,41 +1,19 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:friflex_starter/app/ui_kit/app_box.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/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} /// {@template ComponentsScreen}
/// Экран для демонстрации и тестирования компонентов приложения. /// Экран для демонстрации компонентов приложения.
///
/// Отвечает за:
/// - Демонстрацию различных типов снекбаров (ошибка, успех, информация)
/// - Тестирование кастомных UI компонентов
/// - Предоставление примеров использования компонентов
/// - Валидацию корректности работы компонентов
/// {@endtemplate} /// {@endtemplate}
class ComponentsScreen extends StatefulWidget { class ComponentsScreen extends StatefulWidget {
/// {@macro components_screen} /// {@macro ComponentsScreen}
const ComponentsScreen({super.key}); const ComponentsScreen({super.key});
@override @override
State<ComponentsScreen> createState() => _ComponentsScreenState(); State<ComponentsScreen> createState() => _ComponentsScreenState();
} }
/// {@template components_screen_state}
/// Состояние экрана компонентов.
///
/// Управляет отображением различных типов снекбаров
/// и демонстрирует их функциональность.
/// {@endtemplate}
class _ComponentsScreenState extends State<ComponentsScreen> { class _ComponentsScreenState extends State<ComponentsScreen> {
/// {@macro components_screen_state}
_ComponentsScreenState();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -73,35 +51,6 @@ class _ComponentsScreenState extends State<ComponentsScreen> {
}, },
child: const Text('Показать снекбар с информацией'), 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,8 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart'; import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/features/debug/debug_routes.dart'; import 'package:friflex_starter/features/debug/debug_routes.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@@ -37,42 +35,42 @@ class DebugScreen extends StatelessWidget {
const HBox(16), const HBox(16),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
unawaited(context.pushNamed(DebugRoutes.iconsScreenName)); context.pushNamed(DebugRoutes.iconsScreenName);
}, },
child: const Text('Экран с иконками'), child: const Text('Экран с иконками'),
), ),
const HBox(16), const HBox(16),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
unawaited(context.pushNamed(DebugRoutes.themeScreenName)); context.pushNamed(DebugRoutes.themeScreenName);
}, },
child: const Text('Экран настроек темы'), child: const Text('Экран настроек темы'),
), ),
const HBox(16), const HBox(16),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
unawaited(context.pushNamed(DebugRoutes.tokensScreenName)); context.pushNamed(DebugRoutes.tokensScreenName);
}, },
child: const Text('Экран с токенами'), child: const Text('Экран с токенами'),
), ),
const HBox(16), const HBox(16),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
unawaited(context.pushNamed(DebugRoutes.uiKitScreenName)); context.pushNamed(DebugRoutes.uiKitScreenName);
}, },
child: const Text('Экран UI Kit'), child: const Text('Экран UI Kit'),
), ),
const HBox(16), const HBox(16),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
unawaited(context.pushNamed(DebugRoutes.langScreenName)); context.pushNamed(DebugRoutes.langScreenName);
}, },
child: const Text('Экран локализации'), child: const Text('Экран локализации'),
), ),
const HBox(16), const HBox(16),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () {
await context.pushNamed(DebugRoutes.componentsScreenName); context.pushNamed(DebugRoutes.componentsScreenName);
}, },
child: const Text('Экран компонентов'), child: const Text('Экран компонентов'),
), ),

View File

@@ -2,16 +2,11 @@ import 'package:flutter/material.dart';
import 'package:friflex_starter/app/ui_kit/app_box.dart'; import 'package:friflex_starter/app/ui_kit/app_box.dart';
import 'package:friflex_starter/gen/assets.gen.dart'; import 'package:friflex_starter/gen/assets.gen.dart';
/// {@template icons_screen} /// {@template IconsScreen}
/// Экран для отображения всех доступных иконок приложения. /// Экран для отрисовки иконок
///
/// Отвечает за:
/// - Отображение списка всех SVG иконок из assets/icons/
/// - Предоставление возможности просмотра иконок для разработчиков
/// - Демонстрацию использования системы генерации ресурсов
/// {@endtemplate} /// {@endtemplate}
class IconsScreen extends StatelessWidget { class IconsScreen extends StatelessWidget {
/// {@macro icons_screen} /// {@macro IconsScreen}
const IconsScreen({super.key}); const IconsScreen({super.key});
@override @override
@@ -35,20 +30,19 @@ class IconsScreen extends StatelessWidget {
} }
} }
/// {@template item_icon} // Приватный класс для реализации элемента списка иконок
/// Виджет для отображения отдельной иконки в списке.
///
/// Отображает SVG иконку вместе с её названием файла
/// для удобства идентификации в процессе разработки.
/// {@endtemplate}
class _ItemIcon extends StatelessWidget { class _ItemIcon extends StatelessWidget {
/// {@macro item_icon} /// Создает экземпляр элемента списка иконок
///
/// Принимает:
/// - [icon] - иконка
/// - [name] - название иконки
const _ItemIcon({required this.icon, required this.name}); const _ItemIcon({required this.icon, required this.name});
/// SVG иконка для отображения /// Иконка
final Widget icon; final Widget icon;
/// Название файла иконки для идентификации /// Название иконки
final String name; final String name;
@override @override

View File

@@ -1,18 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.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} /// {@template LangScreen}
/// Экран для отладки и тестирования локализации приложения. /// Экран для отладки языков приложения
///
/// Отвечает за:
/// - Демонстрацию переключения между поддерживаемыми языками
/// - Отображение локализованных строк с разными шрифтами
/// - Тестирование системы локализации и шрифтов
/// - Показ текущего языка приложения
/// {@endtemplate} /// {@endtemplate}
class LangScreen extends StatelessWidget { class LangScreen extends StatelessWidget {
/// {@macro lang_screen} /// {@macro LangScreen}
const LangScreen({super.key}); const LangScreen({super.key});
@override @override
@@ -40,12 +35,18 @@ class LangScreen extends StatelessWidget {
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Тестовое слово bold: ${context.l10n.helloWorld}', 'Тестовое слово bold: ${context.l10n.helloWorld}',
style: TextStyle(color: context.appColors.testColor), style: TextStyle(
color: context.colors.testColor,
fontFamily: Assets.fonts.montserratBold,
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Тестовое слово medium: ${context.l10n.helloWorld}', 'Тестовое слово medium: ${context.l10n.helloWorld}',
style: TextStyle(color: context.appColors.testColor), style: TextStyle(
color: context.colors.testColor,
fontFamily: FontFamily.montserrat,
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text('Текущий язык: ${context.l10n.localeName}'), Text('Текущий язык: ${context.l10n.localeName}'),

View File

@@ -1,23 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:friflex_starter/app/app_context_ext.dart'; import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/app/theme/app_colors_scheme.dart';
/// {@template theme_screen} /// {@template ThemeScreen}
/// Экран для отладки и тестирования темы приложения. /// Экран для отладки темы приложения
///
/// Отвечает за:
/// - Демонстрацию переключения между светлой и темной темами
/// - Отображение тестовых цветов из цветовой схемы
/// - Показ текущего режима темы
/// - Тестирование системы управления темами
/// {@endtemplate} /// {@endtemplate}
class ThemeScreen extends StatelessWidget { class ThemeScreen extends StatelessWidget {
/// {@macro theme_screen} /// {@macro ThemeScreen}
const ThemeScreen({super.key}); const ThemeScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colors = context.appColors; final colors = context.colors;
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Theme')), appBar: AppBar(title: const Text('Theme')),
body: Center( body: Center(
@@ -32,7 +25,7 @@ class ThemeScreen extends StatelessWidget {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
ColoredBox( ColoredBox(
color: context.appColors.testColor, color: context.colors.testColor,
child: const SizedBox(height: 100, width: 100), child: const SizedBox(height: 100, width: 100),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),

View File

@@ -1,17 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// {@template tokens_screen} /// {@template TokensScreen}
/// Экран для отображения и управления токенами аутентификации. /// Экран для отображения токенов
///
/// Отвечает за:
/// - Отображение текущих токенов доступа и обновления
/// - Демонстрацию работы с токенами в приложении
/// - Тестирование функциональности аутентификации
///
/// В текущей реализации является заглушкой для будущей функциональности.
/// {@endtemplate} /// {@endtemplate}
class TokensScreen extends StatelessWidget { class TokensScreen extends StatelessWidget {
/// {@macro tokens_screen} /// {@macro TokensScreen}
const TokensScreen({super.key}); const TokensScreen({super.key});
@override @override

View File

@@ -1,18 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// {@template ui_kit_screen} /// {@template UiKitScreen}
/// Экран для демонстрации и тестирования компонентов UI Kit. /// Экран для отрисовки UI Kit
/// /// и тестирования его компонентов.
/// Отвечает за:
/// - Отображение всех доступных компонентов UI Kit
/// - Демонстрацию использования кастомных виджетов
/// - Тестирование стилей и тем оформления
/// - Предоставление примера использования UI компонентов
///
/// В текущей реализации является заглушкой для будущих компонентов.
/// {@endtemplate} /// {@endtemplate}
class UiKitScreen extends StatelessWidget { class UiKitScreen extends StatelessWidget {
/// {@macro ui_kit_screen} /// {@macro UiKitScreen}
const UiKitScreen({super.key}); const UiKitScreen({super.key});
@override @override

View File

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

View File

@@ -5,41 +5,19 @@ import 'package:friflex_starter/features/profile/domain/repository/i_profile_rep
part 'profile_event.dart'; part 'profile_event.dart';
part 'profile_state.dart'; part 'profile_state.dart';
/// {@template profile_bloc}
/// Bloc для управления состоянием профиля пользователя.
///
/// Обрабатывает события загрузки данных профиля и управляет
/// соответствующими состояниями (загрузка, успех, ошибка).
/// {@endtemplate}
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> { class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
/// {@macro profile_bloc}
ProfileBloc(this._profileRepository) : super(ProfileInitialState()) { ProfileBloc(this._profileRepository) : super(ProfileInitialState()) {
// Регистрируем обработчики событий в конструкторе // Вам необходимо добавлять только
// один обработчик событий в конструкторе
on<ProfileEvent>((event, emit) async { on<ProfileEvent>((event, emit) async {
// Обрабатываем событие загрузки профиля
if (event is ProfileFetchProfileEvent) { if (event is ProfileFetchProfileEvent) {
await _fetchProfile(event, emit); await _fetchProfile(event, emit);
} }
// Обрабатываем событие выхода из профиля
else if (event is ProfileLogoutProfileEvent) {
await _logout(event, emit);
}
}); });
} }
/// Репозиторий для работы с данными профиля
final IProfileRepository _profileRepository; final IProfileRepository _profileRepository;
/// Метод для загрузки данных профиля пользователя.
///
/// Принимает:
/// - [event] - событие с ID пользователя для загрузки
/// - [emit] - функция для эмиссии состояний
///
/// Последовательность состояний:
/// 1. ProfileWaitingState - начало загрузки
/// 2. ProfileSuccessState - успешная загрузка с данными
/// 3. ProfileErrorState - ошибка загрузки с сообщением
Future<void> _fetchProfile( Future<void> _fetchProfile(
ProfileFetchProfileEvent event, ProfileFetchProfileEvent event,
Emitter<ProfileState> emit, Emitter<ProfileState> emit,
@@ -49,7 +27,6 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
final data = await _profileRepository.fetchUserProfile(event.id); final data = await _profileRepository.fetchUserProfile(event.id);
emit(ProfileSuccessState(data: data)); emit(ProfileSuccessState(data: data));
} on Object catch (error, stackTrace) { } on Object catch (error, stackTrace) {
// Обработка ошибки при загрузке профиля
emit( emit(
ProfileErrorState( ProfileErrorState(
message: 'Ошибка при загрузке профиля', message: 'Ошибка при загрузке профиля',
@@ -57,19 +34,6 @@ class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
stackTrace: stackTrace, stackTrace: stackTrace,
), ),
); );
// Пробрасываем исключение в BlocObserver, для логирования или обработки
addError(error, stackTrace);
} }
} }
/// Метод для выхода из профиля пользователя.
Future<void> _logout(
ProfileLogoutProfileEvent event,
Emitter<ProfileState> emit,
) async {
// Здесь можно добавить логику выхода из профиля
// Например, очистка токенов, данных пользователя и т.д.
// В данном примере просто эмитим начальное состояние
emit(ProfileInitialState());
}
} }

View File

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

@@ -3,22 +3,17 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:friflex_starter/app/app_context_ext.dart'; import 'package:friflex_starter/app/app_context_ext.dart';
import 'package:friflex_starter/features/profile/domain/bloc/profile_bloc.dart'; import 'package:friflex_starter/features/profile/domain/bloc/profile_bloc.dart';
/// {@template profile_screen} // Класс экрана, где мы инициализируем ProfileBloc
/// Экран профиля пользователя. // и вызываем событие ProfileFetchProfileEvent
///
/// Отвечает за:
/// - Инициализацию ProfileBloc с репозиторием профиля
/// - Отображение данных профиля пользователя
/// - Обработку состояний загрузки, успеха и ошибок
/// {@endtemplate}
class ProfileScreen extends StatelessWidget { class ProfileScreen extends StatelessWidget {
/// {@macro profile_screen}
const ProfileScreen({super.key}); const ProfileScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final profileRepository = context.di.repositories.profileRepository; final profileRepository = context.di.repositories.profileRepository;
// Инициализируем ProfileBloc с репозиторием и загружаем данные профиля // Здесь мы инициализируем ProfileBloc
// и вызываем событие ProfileFetchProfileEvent
// Или любые другие события, которые вам нужны
return BlocProvider( return BlocProvider(
create: (context) => create: (context) =>
ProfileBloc(profileRepository) ProfileBloc(profileRepository)
@@ -28,16 +23,8 @@ class ProfileScreen extends StatelessWidget {
} }
} }
/// {@template profile_screen_view} /// Виджет, который отображает UI экрана
/// Виджет для отображения UI экрана профиля.
///
/// Отображает данные профиля в зависимости от состояния ProfileBloc:
/// - Индикатор загрузки во время получения данных
/// - Данные профиля при успешной загрузке
/// - Сообщение об ошибке при неудачной загрузке
/// {@endtemplate}
class _ProfileScreenView extends StatelessWidget { class _ProfileScreenView extends StatelessWidget {
/// {@macro profile_screen_view}
const _ProfileScreenView(); const _ProfileScreenView();
@override @override

View File

@@ -1,83 +1,39 @@
import 'dart:async';
import 'package:flutter/material.dart'; 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_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/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'; import 'package:go_router/go_router.dart';
/// {@template root_screen} /// Класс для реализации корневой страницы приложения
/// Корневой экран приложения с навигационной структурой. class RootScreen extends StatelessWidget {
/// /// Создает корневую страницу приложения
/// Отвечает за: ///
/// - Отображение основного навигационного интерфейса /// Принимает:
/// - Управление переключением между основными разделами приложения /// - [navigationShell] - текущая ветка навигации
/// - Отображение кнопки отладки в не-продакшн окружениях
/// - Интеграцию с GoRouter для навигации
/// {@endtemplate}
class RootScreen extends StatefulWidget {
/// {@macro root_screen}
const RootScreen({required this.navigationShell, super.key}); const RootScreen({required this.navigationShell, super.key});
/// Текущая ветка навигации от GoRouter /// Текущая ветка навигации
/// Содержит информацию о текущем состоянии навигации
final StatefulNavigationShell navigationShell; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
floatingActionButton: context.di.env != .prod floatingActionButton: context.di.env != AppEnv.prod
? FloatingActionButton( ? FloatingActionButton(
child: const Icon(Icons.bug_report), child: const Icon(Icons.bug_report),
onPressed: () { onPressed: () {
unawaited(context.pushNamed(DebugRoutes.debugScreenName)); context.pushNamed(DebugRoutes.debugScreenName);
}, },
) )
: null, : null,
body: widget.navigationShell, body: navigationShell,
bottomNavigationBar: BottomNavigationBar( bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[ items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Главная'), BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Профиль'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Профиль'),
], ],
currentIndex: widget.navigationShell.currentIndex, currentIndex: navigationShell.currentIndex,
onTap: widget.navigationShell.goBranch, onTap: navigationShell.goBranch,
), ),
); );
} }

View File

@@ -1,88 +0,0 @@
# Модуль 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

@@ -1,42 +0,0 @@
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

@@ -1,23 +0,0 @@
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

@@ -1,35 +0,0 @@
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

@@ -1,16 +0,0 @@
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

@@ -1,38 +0,0 @@
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

@@ -1,56 +0,0 @@
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

@@ -1,103 +0,0 @@
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

@@ -1,42 +0,0 @@
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

@@ -1,20 +0,0 @@
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

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

View File

@@ -1,5 +1,3 @@
// dart format width=80
/// GENERATED CODE - DO NOT MODIFY BY HAND /// GENERATED CODE - DO NOT MODIFY BY HAND
/// ***************************************************** /// *****************************************************
/// FlutterGen /// FlutterGen
@@ -7,7 +5,7 @@
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: deprecated_member_use,directives_ordering,implicit_dynamic_list_literal,unnecessary_import // ignore_for_file: directives_ordering,unnecessary_import,implicit_dynamic_list_literal,deprecated_member_use
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@@ -15,6 +13,34 @@ import 'package:flutter_svg/flutter_svg.dart' as _svg;
import 'package:lottie/lottie.dart' as _lottie; import 'package:lottie/lottie.dart' as _lottie;
import 'package:vector_graphics/vector_graphics.dart' as _vg; 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 { class $AssetsIconsGen {
const $AssetsIconsGen(); const $AssetsIconsGen();
@@ -37,18 +63,25 @@ class $AssetsLottieGen {
} }
class Assets { class Assets {
const Assets._(); Assets._();
static const $AssetsFontsGen fonts = $AssetsFontsGen();
static const $AssetsIconsGen icons = $AssetsIconsGen(); static const $AssetsIconsGen icons = $AssetsIconsGen();
static const $AssetsLottieGen lottie = $AssetsLottieGen(); static const $AssetsLottieGen lottie = $AssetsLottieGen();
} }
class SvgGenImage { class SvgGenImage {
const SvgGenImage(this._assetName, {this.size, this.flavors = const {}}) const SvgGenImage(
: _isVecFormat = false; this._assetName, {
this.size,
this.flavors = const {},
}) : _isVecFormat = false;
const SvgGenImage.vec(this._assetName, {this.size, this.flavors = const {}}) const SvgGenImage.vec(
: _isVecFormat = true; this._assetName, {
this.size,
this.flavors = const {},
}) : _isVecFormat = true;
final String _assetName; final String _assetName;
final Size? size; final Size? size;
@@ -69,7 +102,6 @@ class SvgGenImage {
String? semanticsLabel, String? semanticsLabel,
bool excludeFromSemantics = false, bool excludeFromSemantics = false,
_svg.SvgTheme? theme, _svg.SvgTheme? theme,
_svg.ColorMapper? colorMapper,
ColorFilter? colorFilter, ColorFilter? colorFilter,
Clip clipBehavior = Clip.hardEdge, Clip clipBehavior = Clip.hardEdge,
@deprecated Color? color, @deprecated Color? color,
@@ -89,7 +121,6 @@ class SvgGenImage {
assetBundle: bundle, assetBundle: bundle,
packageName: package, packageName: package,
theme: theme, theme: theme,
colorMapper: colorMapper,
); );
} }
return _svg.SvgPicture( return _svg.SvgPicture(
@@ -104,8 +135,7 @@ class SvgGenImage {
placeholderBuilder: placeholderBuilder, placeholderBuilder: placeholderBuilder,
semanticsLabel: semanticsLabel, semanticsLabel: semanticsLabel,
excludeFromSemantics: excludeFromSemantics, excludeFromSemantics: excludeFromSemantics,
colorFilter: colorFilter: colorFilter ??
colorFilter ??
(color == null ? null : ColorFilter.mode(color, colorBlendMode)), (color == null ? null : ColorFilter.mode(color, colorBlendMode)),
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
cacheColorFilter: cacheColorFilter, cacheColorFilter: cacheColorFilter,
@@ -118,7 +148,10 @@ class SvgGenImage {
} }
class LottieGenImage { class LottieGenImage {
const LottieGenImage(this._assetName, {this.flavors = const {}}); const LottieGenImage(
this._assetName, {
this.flavors = const {},
});
final String _assetName; final String _assetName;
final Set<String> flavors; final Set<String> flavors;
@@ -135,8 +168,11 @@ class LottieGenImage {
_lottie.LottieImageProviderFactory? imageProviderFactory, _lottie.LottieImageProviderFactory? imageProviderFactory,
Key? key, Key? key,
AssetBundle? bundle, AssetBundle? bundle,
Widget Function(BuildContext, Widget, _lottie.LottieComposition?)? Widget Function(
frameBuilder, BuildContext,
Widget,
_lottie.LottieComposition?,
)? frameBuilder,
ImageErrorWidgetBuilder? errorBuilder, ImageErrorWidgetBuilder? errorBuilder,
double? width, double? width,
double? height, double? height,
@@ -146,9 +182,6 @@ class LottieGenImage {
bool? addRepaintBoundary, bool? addRepaintBoundary,
FilterQuality? filterQuality, FilterQuality? filterQuality,
void Function(String)? onWarning, void Function(String)? onWarning,
_lottie.LottieDecoder? decoder,
_lottie.RenderCache? renderCache,
bool? backgroundLoading,
}) { }) {
return _lottie.Lottie.asset( return _lottie.Lottie.asset(
_assetName, _assetName,
@@ -173,9 +206,6 @@ class LottieGenImage {
addRepaintBoundary: addRepaintBoundary, addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality, filterQuality: filterQuality,
onWarning: onWarning, onWarning: onWarning,
decoder: decoder,
renderCache: renderCache,
backgroundLoading: backgroundLoading,
); );
} }

15
lib/gen/fonts.gen.dart Normal file
View File

@@ -0,0 +1,15 @@
/// 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,20 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
/// Тип функции для построения виджета с учетом локализации
typedef LocalizationBuilder = Widget Function(); typedef LocalizationBuilder = Widget Function();
/// {@template localization_consumer} /// Виджет для перестройки виджета в зависимости от локализации
/// Виджет для подписки на изменения локализации приложения.
///
/// Автоматически перестраивает дочерние виджеты при изменении языка,
/// обеспечивая реактивность интерфейса к изменениям настроек локализации.
/// {@endtemplate}
class LocalizationConsumer extends StatelessWidget { class LocalizationConsumer extends StatelessWidget {
/// {@macro localization_consumer}
const LocalizationConsumer({required this.builder, super.key}); const LocalizationConsumer({required this.builder, super.key});
/// Функция для построения виджета с учетом текущей локализации
final LocalizationBuilder builder; final LocalizationBuilder builder;
@override @override
@@ -27,34 +19,12 @@ class LocalizationConsumer extends StatelessWidget {
} }
} }
/// {@template localization_notifier} /// Класс для управления локализацией
/// Класс для управления локализацией приложения.
///
/// Отвечает за:
/// - Хранение текущей локали приложения
/// - Уведомление подписчиков об изменениях языка
/// - Переключение между поддерживаемыми языками
/// {@endtemplate}
final class LocalizationNotifier extends ChangeNotifier { final class LocalizationNotifier extends ChangeNotifier {
/// {@macro localization_notifier} Locale _locale = const Locale('en', 'US');
LocalizationNotifier();
/// Текущая локаль приложения
/// По умолчанию используется русский язык
Locale _locale = const Locale('ru', 'RU');
/// Получение текущей локали
Locale get locale => _locale; Locale get locale => _locale;
/// Получение текущего языка в виде кода языка
String get language => _locale.languageCode;
/// Метод для изменения локали приложения.
///
/// Принимает:
/// - [locale] - новая локаль для установки
///
/// Уведомляет всех подписчиков об изменении локали.
void changeLocal(Locale locale) { void changeLocal(Locale locale) {
_locale = locale; _locale = locale;
notifyListeners(); notifyListeners();

View File

@@ -1,3 +1,4 @@
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/runner/app_runner.dart'; import 'package:friflex_starter/runner/app_runner.dart';
void main() => AppRunner(.prod).run(); void main() => AppRunner(AppEnv.prod).run();

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/runner/app_runner.dart'; import 'package:friflex_starter/runner/app_runner.dart';
void main() => AppRunner(.dev).run(); void main() => AppRunner(AppEnv.dev).run();

View File

@@ -1,3 +1,4 @@
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/runner/app_runner.dart'; import 'package:friflex_starter/runner/app_runner.dart';
void main() => AppRunner(.prod).run(); void main() => AppRunner(AppEnv.prod).run();

View File

@@ -1,3 +1,4 @@
import 'package:friflex_starter/app/app_env.dart';
import 'package:friflex_starter/runner/app_runner.dart'; import 'package:friflex_starter/runner/app_runner.dart';
void main() => AppRunner(.stage).run(); void main() => AppRunner(AppEnv.stage).run();

View File

@@ -5,18 +5,23 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _fe_analyzer_shared name: _fe_analyzer_shared
sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "91.0.0" version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.4.1" version: "6.11.0"
ansicolor: ansicolor:
dependency: transitive dependency: transitive
description: description:
@@ -76,18 +81,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: build name: build
sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "2.4.2"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
name: build_config name: build_config
sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.1.2"
build_daemon: build_daemon:
dependency: transitive dependency: transitive
description: description:
@@ -96,14 +101,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.4" 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: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
sha256: "7b5b569f3df370590a85029148d6fc66c7d0201fc6f1847c07dd85d365ae9fcd" sha256: "058fe9dce1de7d69c4b84fada934df3e0153dd000758c4d65964d0166779aa99"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.10.3" 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"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -116,10 +137,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: built_value name: built_value
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d sha256: "8b158ab94ec6913e480dc3f752418348b5ae099eb75868b5f4775f0572999c61"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.12.0" version: "8.9.4"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@@ -148,10 +169,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: code_builder name: code_builder
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.11.0" version: "4.10.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@@ -204,10 +225,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dart_style name: dart_style
sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.3" version: "2.3.8"
dartx: dartx:
dependency: transitive dependency: transitive
description: description:
@@ -216,22 +237,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
name: dio name: dio
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.9.0" version: "5.8.0+1"
dio_web_adapter: dio_web_adapter:
dependency: transitive dependency: transitive
description: description:
@@ -244,18 +257,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: envied name: envied
sha256: cd95ddf0982e53f0b6664e889d4a9ce678b3907a59a5047923404375ef6dcacc sha256: a4e2b1d0caa479b5d61332ae516518c175a6d09328a35a0bc0a53894cc5d7e4d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.1.1"
envied_generator: envied_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: envied_generator name: envied_generator
sha256: "81ad332912f1b31afbd2b913aff9ec7b032e97f4ba7e419f52d02bb90637e77c" sha256: "894f6c5eb624c60a1ce6f642b6fd7ec68bc3440aa6f1881837aa9acbbeade0c8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.1.1"
equatable: equatable:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -313,26 +326,26 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_gen name: flutter_gen
sha256: eac4863b65813aacbf16ecc07e7c271ab82fb2d95181825348f1fb7b03fd52da sha256: "4117a3ea6b26a910c715bd58abcc5a90447e70930a5b98249e94c41da9e849bb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.12.0" version: "5.10.0"
flutter_gen_core: flutter_gen_core:
dependency: transitive dependency: transitive
description: description:
name: flutter_gen_core name: flutter_gen_core
sha256: b6bafbbd981da2f964eb45bcb8b8a7676a281084f8922c0c75de4cfbaa849311 sha256: "3eaa2d3d8be58267ac4cd5e215ac965dd23cae0410dc073de2e82e227be32bfc"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.12.0" version: "5.10.0"
flutter_gen_runner: flutter_gen_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_gen_runner name: flutter_gen_runner
sha256: c99b10af9d404e3f46fd1927e7d90099779e935e86022674c4c2a9e6c2a93b29 sha256: e74b4ead01df3e8f02e73a26ca856759dbbe8cb3fd60941ba9f4005cd0cd19c9
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.12.0" version: "5.10.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -398,10 +411,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_svg name: flutter_svg
sha256: "055de8921be7b8e8b98a233c7a5ef84b3a6fcc32f46f1ebf5b9bb3576d108355" sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.1.0"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -412,70 +425,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
geoclue: frontend_server_client:
dependency: transitive dependency: transitive
description: description:
name: geoclue name: frontend_server_client
sha256: c2a998c77474fc57aa00c6baa2928e58f4b267649057a1c76738656e9dbd2a7f sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.1.1" version: "4.0.0"
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: glob:
dependency: transitive dependency: transitive
description: description:
@@ -488,10 +445,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: go_router name: go_router
sha256: c92d18e1fe994cb06d48aa786c46b142a5633067e8297cff6b5a3ac742620104 sha256: "0b1e06223bee260dee31a171fb1153e306907563a0b0225e8c1733211911429a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "17.0.0" version: "15.1.2"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@@ -508,14 +465,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.3.4" version: "5.3.4"
gsettings:
dependency: transitive
description:
name: gsettings
sha256: "1b0ce661f5436d2db1e51f3c4295a49849f03d304003a7ba177d01e3a858249c"
url: "https://pub.dev"
source: hosted
version: "0.2.8"
hashcodes: hashcodes:
dependency: transitive dependency: transitive
description: description:
@@ -555,14 +504,6 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" 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: image_size_getter:
dependency: transitive dependency: transitive
description: description:
@@ -607,26 +548,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker name: leak_tracker
sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.0.1" version: "10.0.9"
leak_tracker_flutter_testing: leak_tracker_flutter_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_flutter_testing name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.10" version: "3.0.9"
leak_tracker_testing: leak_tracker_testing:
dependency: transitive dependency: transitive
description: description:
name: leak_tracker_testing name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@@ -647,10 +588,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: lottie name: lottie
sha256: "8ae0be46dbd9e19641791dc12ee480d34e1fd3f84c749adc05f3ad9342b71b95" sha256: c5fa04a80a620066c15cf19cc44773e19e9b38e989ff23ea32e5903ef1015950
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.3.2" version: "3.3.1"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@@ -671,10 +620,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.17.0" version: "1.16.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@@ -699,22 +648,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" 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: path:
dependency: transitive dependency: transitive
description: description:
@@ -855,26 +788,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: share_plus name: share_plus
sha256: "3424e9d5c22fd7f7590254ba09465febd6f8827c8b19a44350de4ac31d92d3a6" sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "12.0.0" version: "10.1.4"
share_plus_platform_interface: share_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: share_plus_platform_interface name: share_plus_platform_interface
sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.0" version: "5.0.2"
shared_preferences: shared_preferences:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences name: shared_preferences
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.3" version: "2.3.5"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
@@ -948,18 +881,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: source_gen name: source_gen
sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723"
url: "https://pub.dev"
source: hosted
version: "1.3.8"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@@ -1012,42 +937,42 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: talker name: talker
sha256: "82de443cadfb6c41d457e7774c7890a91c73af3c2f17f3f7c01670bb58d5f5a1" sha256: a664f5eae5284e94aa8c535e0400ab71a78fa2480ad18a7344dec928b5c56805
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "4.8.0"
talker_bloc_logger: talker_bloc_logger:
dependency: "direct main" dependency: "direct main"
description: description:
name: talker_bloc_logger name: talker_bloc_logger
sha256: e631fcc9454cd86639888a9bb4654582bbc8c64c7dcc913401bfecb8892ec759 sha256: "1af998d6cbafba00f66bbf49d77132275d3b113c6ac53ce0dc9d9981206f9cea"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "4.8.0"
talker_dio_logger: talker_dio_logger:
dependency: "direct main" dependency: "direct main"
description: description:
name: talker_dio_logger name: talker_dio_logger
sha256: "5bbecc237f3d2c4af9348da5a0086321ed6dd6bf9857d723b1f54f61c810cff2" sha256: "296a20ce600ccca7801deb5a530c28d03500c520f25f1e40c3bae727f63dd5eb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "4.8.0"
talker_flutter: talker_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: talker_flutter name: talker_flutter
sha256: "4f7a8d739237a3a3c8ba4dddcdbc1f9d9dec143811641dbafebd6b70f947f8ca" sha256: "480c51bba7ac0dcab23be5e9545214a43179625bc5f787326248e94af8316aee"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "4.8.0"
talker_logger: talker_logger:
dependency: "direct main" dependency: "direct main"
description: description:
name: talker_logger name: talker_logger
sha256: "8218836d871ea5ab1ec616cffe3cdae84e8fb44022d5cc04c95d7b220572b8fb" sha256: b5d0bd04229eeccdf765cabf73e964ee28528c749104b56867c8d2eb764a45ee
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "4.8.0"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@@ -1060,26 +985,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.7" version: "0.7.4"
theme_tailor:
dependency: "direct dev"
description:
name: theme_tailor
sha256: "1ed7eeb5362e61aac4719e3ca4cd701fd6a74a507a911424eb6caef9b28a7fef"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
theme_tailor_annotation:
dependency: "direct main"
description:
name: theme_tailor_annotation
sha256: "867848486f33dc4dff7649e3c6525133ecee410b5d97c77e22f1cb146baa1158"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
time: time:
dependency: transitive dependency: transitive
description: description:
@@ -1088,6 +997,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.5" version: "2.1.5"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@@ -1164,10 +1081,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.1.4"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
@@ -1241,5 +1158,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.10.0 <4.0.0" dart: ">=3.8.0 <4.0.0"
flutter: ">=3.38.1" flutter: ">=3.32.0"

View File

@@ -6,34 +6,32 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 0.0.1+1 version: 0.0.1+1
environment: environment:
sdk: ">=3.10.0 <4.0.0" sdk: ^3.8.0
flutter: ">=3.38.1" flutter: ">=3.32.0"
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
cupertino_icons: 1.0.8 cupertino_icons: 1.0.8
envied: 1.3.1 envied: 1.1.1
go_router: 17.0.0 go_router: 15.1.2
flutter_bloc: 9.1.1 flutter_bloc: 9.1.1
provider: 6.1.5 provider: 6.1.5
dio: 5.9.0 dio: 5.8.0+1
intl: 0.20.2 intl: 0.20.2
flutter_svg: 2.2.2 flutter_svg: 2.1.0
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
lottie: 3.3.2 lottie: 3.3.1
# Пакеты для отладки # Пакеты для отладки
talker_flutter: 5.0.2 talker_flutter: 4.8.0
talker_dio_logger: 5.0.2 talker_dio_logger: 4.8.0
talker_bloc_logger: 5.0.2 talker_bloc_logger: 4.8.0
talker_logger: 5.0.2 talker_logger: 4.8.0
equatable: 2.0.7 equatable: 2.0.7
theme_tailor_annotation: 3.1.1
### основной сервис с интерфейсами ### основной сервис с интерфейсами
i_app_services: i_app_services:
path: ./app_services/i_app_services path: ./app_services/i_app_services
@@ -42,27 +40,39 @@ dependencies:
### В зависимости от платформы ### ### В зависимости от платформы ###
app_services: app_services:
path: app_services/base/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: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
envied_generator: 1.3.1 envied_generator: 1.1.1
build_runner: 2.10.3 build_runner: 2.4.15
flutter_gen_runner: 5.12.0 flutter_gen_runner: 5.10.0
flutter_gen: 5.12.0 flutter_gen: 5.10.0
flutter_lints: 6.0.0 flutter_lints: 6.0.0
theme_tailor: 3.1.1
flutter: flutter:
uses-material-design: true uses-material-design: true
generate: true generate: true
assets: assets:
- assets/icons/ - assets/icons/
- assets/fonts/
- assets/lottie/ - 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: flutter_gen:
integrations: integrations:
flutter_svg: true flutter_svg: true

Some files were not shown because too many files have changed in this diff Show More