Всем привет, на связи Surf. В практике разработки Flutter-проектов мы используем внутреннее решение для создания чистой архитектуры под названием Elementary. Если вы уже знакомы с прародителем Elementary — Model-Widget-WidgetModel (MWWM), тогда вам будет всё достаточно знакомо и понятно.  Если нет, то пристегните ремни.  Меня зовут Влад Коношенко, я Flutter-разработчик. В статье разберём, как работает Elementary: на примере покажу, как отделить слой представления от бизнес-логики и сделать кодовую базу более чистой и поддерживаемой. Пакет доступен на pub.dev. Исходный код можно посмотреть на GitHub. Elementary — библиотека, которая предоставляет механизмы для написания приложения по правилам Clean Architecture с разделением модулей на чёткие блоки. Опирается на паттерн Model-View-ViewModel (MVVM).  Прочитайте подробнее о том, как устроен Elementary. В этой статье покажем, насколько просто использовать библиотеку.  Идея библиотеки — в разделении ответственности классов: UI, бизнес-логики и презентационной логики. Получаются независимые друг от друга модули, имеющие чёткую структуру. Для понимая достаточно запомнить три сущности.  ElementaryWidget — представление, в котором формируется структура экрана. WidgetModel (WM) содержит презентационную логику. ElementaryModel содержит бизнес-логику и логику компонента.  Чтобы оценить простоту работы с Elementary, реализуем простое приложение прогноза погоды. Оно состоит из двух экранов: выбора города и прогноза погоды.  Готовое приложение смотрите в моём репозитории на GitHub.  Исходник на Github Для работы с данными используется класс Model. Он имеет параметры AddressService и AppModel. Принимаем их как параметры класса для возможности легко тестировать класс.  Сервис — это объект, который будет обрабатывать данные о погоде. Для примера его реализация не важна. AppModel будет хранить данные выбранной локации. Cоздадим публичный метод для доступа к репозиторию. Мы не должны позволять иметь доступ к репозиторию в WM, так как это будет нарушать принцип единой ответственности. Готовый класс выглядит следующим образом: Виджет модель хранит данные, необходимые для UI. В нашем случае — поле поиска и список городов, подходящих под параметры поиска. WM наследуется от класса WidgetModel. Модель, которую получаем в качестве параметра, передаём в super метод. Далее эта модель доступна как поле класса WidgetModel — как показано в примере. К ней обращаемся через model. Также подготовим функцию создания WM createSelectAddressWM, которая принимает в себя контекст, чтоб мы смогли взять зависимости через di и передать их в модель. Эта функция будет вызываться на слое UI. Всю вёрстку оставляем в методе build. Важный момент: при создании виджета передаём функцию создания WM, которую написали на предыдущем шаге. Исходник на Github Модель будет содержать сервис погоды и Location для получения информации о том, какая локация выбрана. В методе getWeather выполняем запрос на получение данных о погоде. В данном случае WM — прослойка для предоставления данных виджет-элементу. Содержит лишь один метод повторной попытки загрузки данных в случае ошибки. Для отображения текущего состоянии данных используем EntityStateNotifier. Этот класс входит в состав пакета Elementary и содержит механизмы, основанные на работе ValueNotifier.  У EntityStateNotifier есть методы content, error, loading. С их помощью устанавливаем текущее состояние без каких-либо дополнительных флагов. Дополнительный абстрактный класс IWeatherWm позволяет определить значения, к которым мы предоставляем доступ со стороны виджета. Экран содержит стандартный build, в котором используем виджет EntityStateNotifierBuilder (поставляется в пакете Elementary).  Этот виджет имеет 3 важных колбека: errorBuilder, loadingBuilder, builder. Каждый соответствует состоянию EntityStateNotifier из нашей модели. Как итог — это позволяет разделить состояния на отдельные виджеты и повысить читаемость кода. Передавайте в модель все зависимости для получения данных в текущем модуле. В WM нужно передавать сервисы и обертки для работы с контекстом (theme, dialogs, navigation). Создавайте систему виджетов, которые будут обновляться независимо друг от друга при помощи различных билдеров (StreamBuilder, ValueListenableBuilder).  Предпочтительнее использовать ValueChangeNotifiers или потоки, чтобы избегать лишних обновлений интерфейса. Использование интерфейсов для построения UI не позволяет нарушать закон Деметры. Не стоит обращаться к контексту в UI через wm.context. Не стоит обращаться к модели через wm.model.  Все стили и локализацию лучше получать из WM, обеспечивая UI только актуальной информацией. Все необходимые зависимости должны передаваться как параметры, чтобы иметь возможность подменить их на этапе тестирования. Иногда у виджета может отсутствовать работа с данными. Тогда можно создать StubModel и передавать ее как заглушку.  Мы продолжаем развивать это решение, чтобы сделать разработку на Flutter ещё более комфортной и лёгкой. Актуальная версия доступна на pub.dev. Подписывайтесь на канал новостей об Elementary, чтобы не пропустить обновления. Читайте также: Как устроен Elementary >> Элементарное тестирование, или тестирование Elementary >>  Больше полезного про Flutter — в телеграм-канале Surf Flutter Team. Публикуем кейсы, лучшие практики, новости и вакансии Surf, а также проводим прямые эфиры. Присоединяйтесь!  Flutter разработчик