* refactor(app): обновить структуру приложения и удалить устаревшие провайдеры * refactor(runner): упростить обработку ошибок и улучшить логирование времени инициализации * refactor(runner): улучшить порядок инициализации приложения и обработку ошибок * refactor(app): исправить контекст MediaQuery для предотвращения перерисовки * refactor(pp): удалить главный виджет приложения и заменить его на AppRoot * docs(copilot-instructions): уточнить правила проведения Code Review на русском языке * refactor(linter): добавить правило avoid_catches_without_on_clauses для улучшения обработки исключений --------- Co-authored-by: petrovyuri <petrovyuri@example.com>
20 KiB
Правила разработки Flutter проекта
Этот файл содержит правила и стандарты разработки для проекта. Все правила обязательны к соблюдению при написании кода.
ВАЖНО: Перед каждым PR необходимо отформатировать код (dart format .) и проверить анализатор на отсутствие сообщений (dart analyze).
Правила проведения Code Review
Основные правила проведения Code Review
ВАЖНО: - Комментарии, обзоры и описание Pull Request при проведении code review должны быть на РУССКОМ языке.
Стиль кода
Именование
Интерфейсы
Все интерфейсы в приложении должны начинаться с заглавной буквы "I".
Примеры: IAuthRepository, IProfileRepository, IMainRunner и т.д.
Таким образом, сразу видно, что работаешь с интерфейсом.
Пример:
/// Интерфейс - **IUserRepository**
abstract interface class IUserRepository {}
/// Основная реализация (prod и stage окружения)
class UserRepository implements IUserRepository {}
/// Иная реализация (мок, локальное хранилище) должна содержать
/// постфикс функциональности:
/// - Network - сетевое взаимодействие.
/// - Local - локальное хранилище.
/// - Mock - мок репозиторий.
class UserRepositoryLocal implements IUserRepository {}
Классы - Репозитории
Репозитории должны содержать в конце название источника данных (если используется мок или локальное хранилище). Основная реализация не должна содержать постфикса.
Примеры:
- Интерфейс - IAuthRepository
- Основная реализация (prod и stage окружения) - AuthRepository
- Мок (мок данные) - AuthRepositoryMock
- Локальное хранилище (например бд или просто имитация данных) - AuthRepositoryLocal
Файлы
Используется snake_case. Название файла должно иметь следующую структуру: [раздел]_[тип].dart
Примеры: user_details_screen.dart, shop_entity.dart
Классы
Название классов UpperCamelCase. Для создания приватных классов используем префикс _. Название класса в конце должно содержать в себе тип.
Примеры: UserEntity, AdultDialog
Методы
Название метода в начале должно содержать в себе действие (глагол):
- fetch
- put
- update
- delete
- и так далее
Примеры:
int fetchFirstElement() {}
void updateFirstElement() {}
ВАЖНО: Название метода не должно содержать в себе And/Or, и метод соответственно не должен выполнять подобную логику.
Переменные и константы
Константы именуются также lowerCamelCase.
Примеры:
const String carItem = 'default';
или
final String userName = 'user';
Виджеты
Виджеты именуются UpperCamelCase. В названии виджетов не должно содержаться слово widget.
Экраны
Экраны, используемые в роутинге, именуются с постфиксом Screen.
Пример: ShopListScreen
Содержимое экрана
Виджеты, отображающие содержимое экрана, именуются с постфиксом View.
Пример: ShopListView
Глобальные виджеты
Глобальные виджеты именуются с приставкой App.
Пример: AppButton
Структура класса
Объявления элементов класса должны располагаться в следующем порядке:
- Constructors
- constructors
- named-constructors
- factory-constructors
- Static
- public-static-methods
- private-static-methods
- public-static-const-fields
- private-static-const-fields
- public-static-final-fields
- private-static-final-fields
- public-static-fields
- private-static-fields
- Fields
- public-final-fields
- private-final-fields
- public-fields
- private-fields
- Getters/Setters
- public-getters-setters
- private-getters-setters
- Methods
- overridden-methods
- public-methods
- protected-methods
- private-methods
Ведение документации и комментариев
Документация
Основные правила ведения документации в проекте
- Документация оформляется над описываемым объектом с использованием
/// - Документацией необходимо покрывать все классы, конструкторы, поля, геттеры, сеттеры, методы, фабрики, все кастомные объекты
- Документация должна:
- указывать на назначение объекта
- содержать исчерпывающее описание объекта
- быть краткой и емкой
- быть понятной для любого разработчика
Шаблоны и примеры документации объектов
Документация классов
/// {@template new_class}
/// Класс для {описание назначения и реализуемого функционала в классе}.
/// {@endtemplate}
class NewClass {}
Пример:
/// {@template app_button}
/// Класс для реализации кастомизированной кнопки.
/// {@endtemplate}
class AppButton {}
Документация конструкторов и фабрик
Если конструктор один, то достаточно указать {@macro new_class}. {@macro new_class} дублирует на конструктор указанную в рамках описания класса документацию, поэтому описание класса должно в таком случае включать все передаваемые в конструктор параметры.
Если конструкторов несколько, описания должны отражать их отличия друг от друга. Фабрики описываются по такому же принципу.
/// {@macro new_class}
const NewClass();
/// Создает {описание создаваемого фабрикой объекта}.
/// Принимает:
/// - [json] - {описание параметра}
factory NewClass.fromJson(Map<String, dynamic> json) {}
Документация полей классов
В классе необходимо описывать каждое поле по отдельности. Если поле ссылается на другое поле или зависит от него, необходимо это указывать в описании.
/// Возраст пользователя.
final int age;
/// Индикатор совершеннолетия пользователя. Принимает значение true, когда [age] больше 18 лет.
final bool isAdult;
Документация геттеров/сеттеров
/// Получения доступа к {описание данных, которые получает геттер}
int get newGetter => ...
/// Установка значения для {описание работы сеттера}
set newSetter(int setterValue) => ...
Документация методов
Методы должны описывать их основное назначение. Если метод сложный, требуется указывать дополнительное описание его работы ниже через пропущенную строку. Если метод принимает какие-либо параметры, каждый параметр должен быть описан по отдельности списком с прямой ссылкой. Если метод возвращает какие-либо значения, они должны быть описаны. Желательно указать, что вернет метод в случае возникновения ошибки.
/// Метод для {описание назначения метода}.
/// {описание особенностей работы метода}.
/// Принимает:
/// - [param] - {описание назначения параметра}.
void newMethod({required String param}) {
...
}
Пример:
/// Метод для расчета температуры.
/// Принимает:
/// - [grad] - параметр необходим для расчета температуры.
/// Возвращает:
/// - температуру в градусах.
/// При ошибке расчета возвращается null.
int? calcTemperature({required int grad}) {
// Реализация
}
Комментарии
Основные правила комментирования кода в проекте
- Комментарии оформляются над описываемым участком кода с использованием
// - Комментировать необходимо те участки кода, которые действительно нуждаются в дополнительном описании
- Комментарий не должен повторять легко читаемые участки кода
Примеры неправильного использования:
if (flag != true) // не нужно описывать как "Если значение не равно true..."
Примеры правильного использования:
if (isAurora) {
// Так как Аврора не может открывать WebView,
// то заменяем на открытие внешнего браузера
}
TODO
Основные правила создания TODO в проекте
- TODO оформляются согласно условиям форматирования линтера с указанием имени разработчика, кто находится в контексте проблемы и сможет ее прокомментировать
- TODO необходимо оставлять на тех участках кода, которые требуют дальнейшей доработки
- Если известно, в рамках какой задачи будет доработан помечаемый участок кода, ссылку на задачу необходимо оставить в скобках
Пример:
// TODO(username): Оптимизировать алгоритм сортировки
Ведение проекта в git
Создание commits/pull-request
- Язык описания PR - Русский
- Описание должно отражать краткую суть изменений
- Коммит/PR должен содержать:
- исчерпывающую информацию об изменениях
- ссылку на задачу в таск-трекер
- Перечисление deprecated-кода (если есть)
Типы коммитов согласно convention
- feat: — новая функция
- fix: — исправление ошибок
- refactor: — Изменение кода, которое не исправляет ошибку и не добавляет функции (рефакторинг кода)
- build: — изменения, влияющие на систему сборки или внешние зависимости (примеры областей (scope): android, ios, linux и так далее)
- docs: — изменения только в документации
- chore: — добавление/обновление/настройка инструментов и библиотек (пример: pubspec.yaml)
- test: — добавление недостающих тестов или исправление существующих тестов
- ci: — изменения в файлах конфигурации и скриптах CI (примеры областей: папка CI)
Структура проекта
Рекомендуемая структура проекта (может отличаться в зависимости от проекта и согласований)
- / - папка проекта
- /assets - директория расположения графических ресурсов
- /tools/ - все необходимые инструменты для проекта
- /docs - документация проекта
- /env - папка, с внешними переменными окружения
- /lib - код на Dart, Flutter-приложение
- /app - содержит основные настройки нашего приложения
- /data - общие поставщики данных
- /domain - общий слой
- /presentation - общий слой
- /di - файлы конфигурации зависимостей
- /router - все, что касается роутинга
- /features - фичи приложения, для каждой фичи создается отдельная папка
- /feature_name - подробнее см в разделе Структура feature папок
- /data
- /domain
- /presentation
- /feature_name - подробнее см в разделе Структура feature папок
- /gen - для сгенерированных файлов
- /targets - таргеты для сборок
- /prod.dart - сборка для prod
- /dev.dart - сборка разработки на моковых репозиториях
- /stage.dart - сборка для stage окружения
- /app - содержит основные настройки нашего приложения
Пример структуры feature папок
- /data - слой данных
- /dto - реализация DTO (data transfer object)
- /repository - реализации репозиториев
- /domain - слой бизнес логики
- /entity - модели которые используются для работы в domain/presentation слоях
- /repository - интерфейсы репозиториев, которые используются в domain слое
- /state - state-management
- /service - реализации сервисов
- /presentation - слой представления
- /screens - все экраны должны заканчиваться на Screen, например UserProfileScreen
- /components - виджеты, которые необходимы для работы в presentation слое. Например: SuperButton, AppTextFields итд
Пояснение к структуре feature папок
Data (слой данных)
Этот слой является поставщиком данных.
- Repository - сущность, которая реализует внутри себя предоставление данных. Должен реализовывать какой либо интерфейс репозитория из domain слоя
- DTO - Dto(Data Transfer Object) модели, и модели с которыми происходит работа в data слое. Например: UserDto
Domain (слой бизнес логики)
- Entity - должны быть в максимально удобном виде для работы внутри Domain и Presentation. Например: UserEntity, ShopEntity
- State - управления состоянием - state manager
- Service - различные сервисы, для выполнения различных задач
- interfaces - интерфейсы репозиториев, которые используются в domain слое
Presentation (слой представления)
- components - widget'ы которые реализуют работу какого либо визуального компонента (Buttons, TextFields, Lists, итд). Например: ShopList, RateButton. Модальные окна
- screens - widget'ы которые представляют собой экран приложения. Например: UserInfoScreen
Основные правила общения объектов между папками
В рамках всего приложения
- Объекты внутри фичи должны быть инкапсулированы и не могут использоваться в других feature
- Если есть необходимость использовать объект в нескольких feature, его нужно вынести в папку app и использовать как глобальный для всего приложения
- Сервис, который должен быть использован в нескольких feature, создается как отдельная feature
- Если создаваемый сервис является платформно-зависимым, его необходимо выносить в app_services. В приложении должен быть только интерфейс
В рамках одной feature
- Объекты data слоя не должны ничего знать про объекты слоя presentation. Имеют доступ к объектам entity из domain слоя для преобразования DTO в Entity
- Объекты domain слоя не должны ничего знать про объекты слоя data, используемый экземпляр репозитория передается в объекты domain слоя через интерфейс репозитория, расположенного в этом же domain слое
- Объекты domain слоя не должны ничего знать про слой presentation, не должны использовать компоненты библиотек ui, material, cupertino, widget и прочих, не должны использовать context
- Объекты presentation слоя не должны ничего знать про объекты слоя data, все взаимодействия непосредственно через объекты слоя domain