DuckDuckPizza — это современное мобильное приложение для заказа пиццы. Главной изюминкой приложения является интеллектуальная поисковая система, которая позволяет пользователям находить идеальную пиццу по конкретному набору ингредиентов. В приложении также реализованы другие стандартные функции, включая интерактивный список всех доступных пицц.
- FVM (Flutter Version Manager)
- Flutter 3.41.6
- Gemini 3.5
Для обеспечения масштабируемости, простоты тестирования и поддержки кода, проект спроектирован по лучшим практикам.
Ниже приведена подробная структура каталогов нашего приложения:
- /lib/app
# Здесь содержатся все основные директории и модули приложения
- /data
# Директория, отвечающая за хранение всего, что связано с данными
- /enums
- /services
# Хранилище Сервисов (Services).
# В этой архитектуре репозитории — это классы-посредники для связи между контроллером и данными.
# Контроллерам не нужно знать источник данных. Вы можете использовать несколько репозиториев в контроллере.
# Репозитории разделяются по сущностям (обычно на основе таблиц БД или эндпоинтов API).
# Репозиторий содержит функции запроса данных из локального API или базы данных.
# Пример: для таблицы/сущности User репозиторий предоставляет методы сохранения, редактирования, удаления и обновления,
# обращаясь к провайдеру данных. Репозиторий является обязательным для инициализации контроллера.
- /example_service.dart
- service.dart
- /provider
# Провайдеры данных (API, локальная БД Sqlite/Hive, Firebase, SharedPreferences)
- api_provider.dart
- db_provider.dart
- storage_provider.dart
# Здесь размещаются асинхронные HTTP-запросы и функции непосредственного взаимодействия с БД
- /model
# Классы моделей данных, отвечающие за абстрагирование объектов приложения (с методами toJson / fromJson)
- model.dart
- /modules
# Каждый модуль представляет собой изолированный экран, содержащий Page, GetXController и Binding.
# Мы трактуем каждый экран как независимый модуль с собственным контроллером и зависимостями.
# Если на экране используются переиспользуемые виджеты, уникальные только для этого экрана, они помещаются в local_widgets.
- /my_module
- page.dart
- controller.dart
- binding.dart
- repository.dart
- /local_widgets
# Биндинги (Binding) служат для внедрения зависимостей, связывая маршруты с менеджером состояний и менеджером зависимостей.
# Биндинг позволяет гибко управлять жизненным циклом контроллеров и освобождать память (dispose), когда экран закрывается.
# Использование внутренних репозиториев в модулях позволяет избежать дублирования импортов глобальных репозиториев
# и делает поддержку кода более быстрой и точечной.
# Репозиторий модуля указывает контроллеру, какие именно провайдеры данных нужно использовать.
- /global_widgets
# Общие виджеты, которые повторно используются в различных модулях приложения
- /routes
# Файлы маршрутизации приложения. Разделены на два файла и два класса для чистоты кода:
- routes.dart
# Содержит константы путей (например: class AppRoutes { const home = '/home'; })
- pages.dart
# Содержит массив настроенных маршрутов GetPage (например: GetPage(name: AppRoutes.home, page: () => HomePage()))
- /core
- /errors
# Обработка ошибок и кастомные классы исключений
- /values
- strings.dart
# Глобальные строковые константы (например, "Войти", "Назад", "Подтвердить")
- colors.dart
# Глобальная палитра цветов приложения
- /languages
# Файлы локализации и переводов для мультиязычных приложений
- /from
- pt-br.dart
- en-au.dart
- assets.dart
# Константы путей к ресурсам (изображениям, анимациям, иконкам)
# Пример: static const String logo = 'assets/images/logo.svg';
- /theme
# Глобальные темы оформления текстовых стилей, цветов и компонентов
- text_theme.dart
- color_theme.dart
- app_theme.dart
- /utils
# Утилиты приложения (маски ввода, хелперы, расширения, валидаторы)
- /extensions
# Методы расширения (Extension methods) для добавления функционала в существующие классы
- example_remove_underlines.dart
- /functions
# Глобальные функции (например, расчет процентов от размера экрана)
- get_percent_size.dart
- /helpers
# Вспомогательные классы, маски для полей ввода, глобальные ключи форм
- masks.dart
- keys.dart
- main.dart
# Главная точка входа в приложение Flutter
- /repos # Дополнительно: используется при архитектуре микрофронтендов (Micro Front-End)
- /dependencies
- /core
Здесь хранится всё, что связано с бизнес-логикой данных: модели, сервисы и провайдеры. Если вы решите использовать модульный подход, папка data сохранит эту роль, обеспечивая доступ к данным для всех модулей приложения, в то время как внутри конкретного модуля будет содержаться только то, что критически необходимо для работы его экрана.
Note
В отличие от других архитектур, здесь термин Provider используется исключительно для выполнения HTTP-запросов (например, через Dio/GetConnect) или для прямого взаимодействия с базами данных.
Если в приложении используются как запросы к серверу, так и локальное кэширование, внутри создаются отдельные файлы (например, api_provider.dart и db_provider.dart). Запросы к API можно группировать в одном месте или разделять по сущностям на ваше усмотрение.
Классы моделей описывают структуру данных. Они обязательно содержат два метода сериализации:
fromJson— преобразует входящий JSON (например, ответ сервера) в строго типизированный объект Dart.toJson— сериализует объект Dart в карту (Map/JSON) перед отправкой на сервер.
Репозиторий группирует функционал провайдеров данных, которые требуются для работы конкретного модуля. Контроллер обращается только к репозиторию, не задумываясь о том, откуда именно берутся данные — из сети, локального кэша или моков.
Модули инкапсулируют в себе всё необходимое для одного экрана: Binding для управления зависимостями, Page для отрисовки пользовательского интерфейса и Controller для реактивного управления состоянием. Это снижает связность кода и упрощает отладку.
Контроллер — это мозг вашего экрана. Здесь объявляются реактивные переменные с суффиксом .obs, изменение которых автоматически обновляет привязанные UI-компоненты.
Important
Правило контроллера: каждый контроллер должен иметь одну и только одну зависимость от репозитория, которая передается при его инициализации в виджете GetX.
Если на одной странице вам требуются данные из двух принципиально разных сущностей (и, соответственно, двух репозиториев), рекомендуется использовать два независимых виджета GetX на одной странице.
Warning
Единственное исключение, когда один контроллер может использоваться для нескольких страниц — если все эти страницы работают исключительно с одним и тем же репозиторием данных.
Такой строгий подход гарантирует, что при обновлении какого-то конкретного значения перерисовываться будут только те виджеты, которые действительно зависят от этого значения, что колоссально повышает производительность Flutter-приложения.
Используются для декларативного управления зависимостями. В биндингах происходит отложенная (lazy) или мгновенная инициализация репозиториев, контроллеров и провайдеров. Благодаря биндингам, страницы (Page), наследующие GetView, могут получать доступ к своим контроллерам без ручного создания экземпляров.
Страницы представляют собой View-слой приложения. Все страницы модуля наследуются от GetView<YourController>, что обеспечивает быстрый и безопасный доступ к соответствующему контроллеру модуля без лишнего бойлерплейта.