Вы находитесь на странице: 1из 33

СОДЕРЖАНИЕ

ВВЕДЕНИЕ.....................................................................................................................................................................3

КРАТКИЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ..........................................................................................................4

РАСПРОСТРАНЕННЫЕ АРХИТЕКТУРЫ ДЛЯ СОЗДАНИЯ ПРИЛОЖЕНИЙ...........................................5

1. MVC (MODEL VIEW CONTROLLER)...................................................................................................6


2. MVP (MODEL VIEW PRESENTER).......................................................................................................8
3. MVVM (MODEL VIEW VIEW MODEL).............................................................................................10
4. СРАВНИТЕЛЬНЫЙ АНАЛИЗ ПАТТЕРНОВ ПРОЕКТИРОВАНИЯ.............................................................13
1. РАЗЛИЧИЕ МЕЖДУ ЭФЕМЕРНЫМ СОСТОЯНИЕМ И СОСТОЯНИЕМ ПРИЛОЖЕНИЯ.............................15
2. УПРАВЛЕНИЕ СОСТОЯНИЯМИ..........................................................................................................17
3. ПАКЕТ ДЛЯ УПРАВЛЕНИЯ СОСТОЯНИЙ............................................................................................18

ВЫВОД..........................................................................................................................................................................29

СПИСОК ИСПОЛЬЗУЕМЫХ ИСТОЧНИКОВ...................................................................................................30


ВВЕДЕНИЕ

Пользователи мобильных устройств ожидают видеть приятный дизайн,


сглаживание при анимации, высокую отзывчивость. Чтобы соответствовать
таким требования разработчикам нужен определенный инструмент, чтобы
реализовывать новые функциональности без падения производительности и
качества программного продукта.

Flutter - это фреймворк от Google, позволяет создавать нативные


приложения в первую очередь для iOs и Android. Приложения на основе
Flutter используют единую кодовую базу, которая оптимизирована для
процессоров ARM, задействует мощности GPU, способна вызывать API
платформы. Flutter создан для быстрой разработки. Поддерживает
сохранение состояния на лету, изменяете код и меньше, чем за секунду
видим изменения.
Flutter поставляется с широким набором настраиваемых widgetов.
Flutter встраивается в популярные IDE.
Flutter добавляет управление жестами и свои виджеты в IDE, позволяя
полностью управлять каждым пикселем на экране. Подобная гибкость
позволяет создавать адаптивные решения
Flutter следует конвенциям платформы, то есть задействует скролл,
модель навигации, иконки, шрифты итп. поэтому в Google App Store
приложение получает больший рейтинг
Порог вхождения в framework подходит как для новичков, так и для
опытных разработчиков.
Flutter - это open source и применяется по всему миру в корпорациях, в
малом бизнесе и в стартапах.

2
КРАТКИЕ ТЕОРЕТИЧЕСКИЕ СВЕДЕНИЯ

3
РАСПРОСТРАНЕННЫЕ АРХИТЕКТУРЫ ДЛЯ СОЗДАНИЯ
ПРИЛОЖЕНИЙ

Архитектура программного обеспечения — это то, как организованы


и собраны все компоненты системного программного обеспечения. Как они
взаимодействуют друг с другом. А также требования к пользовательскому
интерфейсу. Важны три аспекта:

1. Архитектурный паттерн — как организованы и собраны все


компоненты системного программного обеспечения;
2. Модель обмена сообщениями и наборы API — как они
взаимодействуют друг с другом;
3. Атрибуты качества — требования, которым подчиняется весь
пользовательский интерфейс.

Паттерн проектирования — это обобщенное решение


повторяющихся проблем в проектировании программного обеспечения.
Паттерны проектирования могут быть не связаны с готовым решением,
которое будет непосредственно использовано в коде, но их можно понимать
как описание или шаблон для решения некоторого ряда проблем. Важно
следующее:

1. Это решение повторяющихся проблем разработки программного


обеспечения.
2. Это не образец кода, который будет непосредственно использоваться в
проекте.
3. Это просто шаблон, который помогает решить некоторую проблему

Разница между архитектурой программного обеспечения и паттерном


проектирования определяется двумя важными составляющими:

1. Область применения: Архитектура программного обеспечения имеет


универсальную сферу применения, она должна описываться на более
высоком уровне, например, конфигурация сервера и т.д. В то время как
паттерн проектирования имеет сферу применения на низком уровне,
которая доходит до внутреннего кода проекта.
2. Реализация: Архитектура программного обеспечения — это
организация и сборка компонентов таким образом, чтобы их

4
взаимодействие было очень удобным и безопасным. В то время как
паттерн проектирования — это то, как создаются компоненты.

MVC (Model View Controller)

Рассмотрим нескольких известных паттернов проектирования, которые


используются при разработке на Flutter. Прежде всего, мы начнем с самого
основного паттерна MVC и посмотрим, как MVC используется во Flutter.
MVC расшифровывается как «Model View Controller» и переводится
как «Модель Представление Контроллер», и его основная работа
заключается в том, чтобы иметь разделенную кодовую базу, он направлен на
разделение кода и зон ответственности при разработке программного
обеспечения. Основное внимание в MVC уделяется отделению интерфейса
пользователя UI проекта от бизнес-функциональности и от данных, которые
используются в приложении.
Упорядоченные вещи всегда просты в использовании и обслуживании,
так и MVC делает кодовую базу аккуратной и упорядоченной и достигает
этого с помощью трех своих компонентов.

1. Модель — состоит из источника данных, который может быть, например


БД, API, JSON и т.д. И в некоторых случаях она может состоять из
некоторой бизнес-логики.
2. Представление — это о пользовательском интерфейсе, т.е. отображение
данных и получение ввода от пользователя.
3. Контроллер — он содержит бизнес-логику, т.е. управление тем, какие
данные будут показаны пользователю, обработка пользовательского
ввода и т.д. То есть связывает данные и взаимодействие пользователя с
данными.

Дифференциация этих трех основных компонентов проекта помогает


разработчику писать аккуратный и модульный код, делает код удобным для
повторного использования, а также помогает в параллельной, командной
разработке. Работать над проектом становится очень легко, так одна часть не
влияет на другие части проекта, если в этой части что-то меняется. Но, чтобы
разделить компоненты и одновременно позволить им взаимодействовать,
необходим коммуникационный поток, то есть модель сообщений, через
которые они будут осуществить функциональность.

5
Рисунок 1 – Схема устройства паттерна MVC

На Рисунке 1 видно, как пользователь взаимодействует с частью


проекта. Представление помогает пользователю увидеть данные и
предоставить ввод контроллеру через представление. Контроллер выступает
как «мозг» проекта, выполняет вычисления или манипуляции с данными от
пользователя или берет данные из модели для обработки и поставляет их в
представление, которое показывает нужные данные пользователю.
Представление показывает и получает данные, но напрямую не
взаимодействует с моделью; только контроллер обрабатывает данные, он
играет роль посредника между представлением и моделью в
манипулировании данными и представлении данных.
Представление и модель тесно связаны с контроллером, т.е.
представление знает, как общаться с контроллером, контроллер знает, как
общаться с моделью, а модель и представление работают совместно, не зная
о существовании друг друга.

6
MVP (Model View Presenter)

На ряду с MVC также одной из наиболее часто используемых


архитектур при создании приложений является MVP. Расшифровывается как
«Model View Presenter» и переводится как «Модель Представление
Ведущий»:

1. Модель — это интерфейс, определяющий данные, которые будут


отображены или иным образом обработаны в пользовательском
интерфейсе.
2. Представление — это пассивный интерфейс, который отображает
данные (модель) и направляет пользовательские команды (события)
ведущему для выполнения действий над этими данными.
3. Ведущий (презентатор) — воздействует на модель и представление.
Он извлекает данные из хранилищ (модель) и форматирует их для
отображения в представлении.

Все они связаны таким образом, что представление находится на самом


верхнем уровне, который отвечает за отображение данных пользователю и
получение данных от пользователя, презентатор находится посередине
между представлением и моделью, принимает входные данные от
представления и получает данные от модели.
Презентатор содержит необходимую бизнес-логику пользовательского
интерфейса для представления. Все вызовы из представления делегируются
непосредственно презентеру.
Это сделано для того, чтобы можно было проводить модульное
тестирование. Симулировать (mock ,stab,proxy) представление.
Представление и модель полностью изолированы друг от друга.
Модель может вызывать события, но ведущий подписывается на них для
обновления представления.

7
Рисунок 2 – Схема устройства паттерна MVP

Как видно из Рисунка 2 схема устройства MVP почти такая же, как и в
случае MVC, но здесь контроллер заменен на презентатор. Представление
является самым верхним уровнем архитектуры, взаимодействует с
пользователем и принимает входные данные, которые передаются
презентеру, и, получая данные из модели, отправляет их обратно в
представление, чтобы представить их пользователю. Таким образом, в
соответствии с этим, можно сказать о MVP, что представление более тесно
связано с моделью.
Ведущий отвечает за привязку модели к представлению. В такой
модели легче проводить модульное тестирование, поскольку взаимодействие
с представлением осуществляется через интерфейс. Обычно одному
представлению соответвует один ведущий. Хотя сложные представления
могут иметь несколько ведущих. MVP еще больше изолирует данные от
представления, и поэтому проще поддается тестированию

8
MVVM (Model View View Model)

Модели MVC и MVP не лишены недостатков и на практике иногда


приходится отклониться от строгого разделения данных и представления, в
результате исследований по системному анализу MVVM была представлена
компанией Microsoft в 2005 году.
Если посмотреть на определение MVVM, то это аббревиатура трех
слов, а именно «Model, View и View Model». Концепция MVVM заключается в
построении некоторой модели представления (View Model), которая
представляет данные.

Рисунок 3 – Схема устройства паттерна MVVM

Таким образом, мы видим, что модель представления занимает


ключевую позицию, а именно отправляет и получает данные из модели и
предоставляет их представлению, она также наблюдает за изменениями
данных, произошедшими в представлении, и реагирует соответствующим
образом, опять же через модель для представления.
Чтобы создать адаптируемую кодовую базу, разработчикам
приложений необходимо создать слой View-Model, который может быть
использован несколькими "представлениями". Рассмотрим каждый
компонент по отдельности.
Модель в паттерне проектирования MVVM представляет фактические
данные (возможно, real-time). Например, в случае настоящего банка, этим
элементом будет номер счета, имя и адрес человека.

9
Для лучшего понимания можно сказать, что модель может отражать только
фактические данные, а не характеристики или какие-либо особенности,
конфигурации, связанные с приложением. Это означает, что вы не можете
манипулировать тем, как данные будут представлены или отформатированы.
Каждый элемент в наборе данных будет представлять свою собственную
модель. В основном модель хранится вдали от логической части для
поддержания аккуратности кода, но иногда она включает в себя и логику
проверки.
Привязка данных концептуально выглядит следующим образом.
Имеется некий элемент управления, например – текстовое поле, в который
мы хотим вывести некоторые данные. Мы настраиваем связь этого поля с
нужным нам свойством некоего объекта, который содержит данные, которые
надо вывести в поле. Текстовое поле называют целевым объектом привязки
или приемником, объект, содержащий данные, называют источником
данных. Существуют разные типы привязки данных:
1. Односторонняя (one way) привязка данных приводит к тому, что
информация берется из источника, направляется в приемник, при
этом, если в приемнике происходит изменение данных, эти
изменения не влияют на источник. Но если информация изменилась
в источнике данных, изменения отразятся и на приемнике.
2. Двусторонняя (two way) привязка, при ней изменения, которым
данные подвергнуться в приемнике, отражаются и на источнике
данных. При этом описание привязок данных осуществляется в
XAML-коде страницы, то есть, программный код для реализации
этого механизма не нужен.
Существуют и другие типы привязки, например, одноразовая (One time) –
данные из источника передаются в приемник один раз, например, при смене
контекста данных или при загрузке приложения, затем связь между ними не
поддерживается. Передачу данных между источником и приемником
выполняют механизмы системы привязки данных. К привязке данных
относится такое понятие как конвертер данных – программный механизм,
которые выполняет преобразование данных.
Представление в паттерне проектирования MVVM представляет собой
интерфейс, который видит конечный пользователь. Этот элемент использует
широкий набор библиотек для более точного, детального представления
данных. Этот элемент использует поведение, связанное с моделью, например,
идентификацию пользователя или ответ на пользовательский ввод данных.
Представление обрабатывает действия пользователя, но только те, что
определены в функциональных возможностях модели.

10
Модель представления является наиболее важным элементом
архитектуры MVVM, который представляет часть View отдельно от Model.
Этот элемент распределяет фактические данные, а части представления -
отформатированные или обновленные данные, являясь контроллером,
выступая интерфейсом между ними. Модель представления связывает
исходные данные, представленные моделью, и их визуальное представление.
При этом модель представления не обладает "знаниями" о том, как именно
данные используются в представлении – то есть в интерфейсе приложения.
Задача модели представления заключается в подготовке данных к
отображению и в предоставлении этих данных представлению, то есть –
интерфейсным механизмам – через систему привязки данных.
То же самое касается команд. В модели представления может быть определен
некий метод, который вызывается каким-либо
элементом управления из представления, но этот метод не имеет информации
о том, какой именно элемент управления
его вызывает. Благодаря подобному разделению представления (интерфейса)
и логики представления (модели представления)
и достигается гибкость работы над различными частями приложения,
созданного по методологии MVVM
Модель представления также имеет доступ к методам, вызовам
функций и свойствам, которые могут помочь поддерживать фактическое
состояние View. Кроме того, подобный высокий уровень доступа также
позволяет манипулировать моделью или модерировать ее в соответствии с
поведением в представлении, одновременно параллельно работая с
представлением.
Поскольку свойства модели и представления поддерживают
постоянную синхронизацию друг с другом, пользователи могут
беспрепятственно взаимодействовать с приложением. Улучшается
отзывчивость.

11
Сравнительный анализ паттернов проектирования

Паттерн проектирования — это подход к проектированию,


пришедший из крупномасштабных приложений, который можно применять
в приложениях для любой платформы. Помимо MVC используются MVP,
MVVM и д.р.
Модель MVP является производной от MVC. Ключевое различие с
MVC заключается в двух основных моментах:
1. в зависимости каждого слоя от других слоев;
2. насколько тесно слои связаны друг с другом.
Самая распространенная проблема, с которой сталкиваются
разработчики приложений, — это согласованность всей кодовой базы. Даже
небольшое изменение в одном потоке создает множество ошибок при
тестировании приложения. Архитектура MVVM способна преодолеть эту
трудность. Архитектура MVVM позволяет достичь этой цели с помощью
своей модульности. это происходит потому, что модель представления
независима от представления.
Таблица 1 – Сравнение паттернов проектирования

Таким образом, модель архитектуры MVVM выглядит как наиболее


подходящей, для разработки мобильного приложения.

12
АРХИТЕКТУРНЫЕ ОСОБЕННОСТИ FLUTTER.

UI, пользовательский интерфейс.


Интерфейс пользователя, он же пользовательский интерфейс (с англ.
User Interface) - интерфейс, обеспечивающий передачу информации между
пользователем-человеком и программно-аппаратными компонентами
компьютерной системы
Реактивные приложения.
Все современные приложения для мобильных устройств являются
реактивными. Реактивные приложения строятся на основе компонентов,
которые взаимодействуют асинхронно, реагируя на события пользователя и
системы. Они построены на каркасе, ориентированном на события и
передачу сообщений. В результате они становятся масштабируемыми,
отзывчивыми и отказоустойчивыми.
Фреймворк.
Фреймворк (иногда фреймворк; англицизм, неологизм от framework —
остов, каркас, рама, структура) — программная платформа, определяющая
структуру программной системы; программное обеспечение, облегчающее
разработку и объединение разных компонентов большого программного
проекта. «Фреймворк» диктует правила построения архитектуры
приложения, задавая на начальном этапе разработки поведение по
умолчанию — «каркас», который нужно будет расширять, и изменять
согласно указанным требованиям. «Фреймворк» может включать:
вспомогательные программы, библиотеки кода, язык сценариев и другое ПО,
облегчающее разработку и объединение разных компонентов большого
программного проекта. Обычно объединение происходит за счёт
использования единого API.
Flutter является реактивным фреймворком. Используя Flutter можно
создавать части пользовательского интерфейса с нуля, а не модифицировать
их, то есть Flutter оптимизирован на перерисовку даже при каждом кадре,
если это необходимо, однако рекомендованный подход — это декларативное
программирование. Это означает, что Flutter строит свой пользовательский
интерфейс, отражая текущее состояние приложения:

Рисунок 4 –Пользовательский интерфейс как функция состояния

13
Когда состояние приложения меняется (например, пользователь
переключает тумблер на экране настроек), происходит изменение состояния,
которое вызывает перерисовку пользовательского интерфейса.
Нет никаких императивных изменений самого пользовательского
интерфейса (например, widget.setText) - вы изменяете состояние, и
пользовательский интерфейс перестраивается автоматически с нуля. По сути,
во Flutter мы описываем, как должен выглядеть пользовательский интерфейс
для любого состояния.
Язык программирования Dart.
Для разработки с Flutter используется язык программирования под
названием Dart. Это также язык Google, созданный в октябре 2011 года, но
значительно улучшившийся в последние годы. Dart фокусируется на
развитии вёрстки веб-страниц; его можно с легкостью использовать для
создания мобильных и веб-приложений.

14
Разновидности состояний.

Различие между эфемерным состоянием и состоянием приложения.

Состояние приложения — это все, что существует в памяти во время


работы приложения. Сюда входят ресурсы приложения, все переменные,
которые фреймворк Flutter хранит о пользовательском интерфейсе, состояния
анимации, текстуры, шрифты и так далее. Хотя это максимально широкое
определение состояния является правильным, оно не очень полезно для
составления архитектуры приложения.
Во-первых, мы не управляем некоторыми состояниями (например,
текстурами). За нас это делает фреймворк. Поэтому более точное
определение состояния – это "любые данные необходимые для перестройки
пользовательского интерфейса в любой момент времени".
Во-вторых, состояние, которым мы управляем самостоятельно, можно
разделить на два концептуальных типа: эфемерное состояние и состояние
приложения.
Эфемерное состояние (иногда называемое состоянием
пользовательского интерфейса или локальным состоянием) — это состояние,
которое можно описать один виджетом. Состояние, которое не является
эфемерным, которое нужно совместно использовать во многих виджетах
и которое требуется сохранить между сеансами пользователя, называется
состоянием приложения (иногда его также называют общим состоянием).

Примеры состояния приложения.


1. Предпочтения пользователя;
2. Информация о входе в систему;
3. Уведомления в приложении для социальных сетей;
4. Корзина в приложении для электронной коммерции;
5. Состояние прочитанных/непрочитанных статей в новостном
приложении.
Управления состоянием приложения зависит от сложности и характера
приложения, предыдущего опыта команды и многих других аспектов.
Как получить состояние? Например, если в контексте конкретного
приложения выбранная вкладка в нижней навигационной панели не является
эфемерным состоянием. Вам может понадобиться изменить ее извне класса,
сохранить ее между сессиями и так далее. В этом случае переменная _index
часто является состоянием приложения.

15
Не существует четкого, универсального правила, позволяющего
определить, является ли конкретная переменная эфемерной или состоянием
приложения. Иногда приходится изменять из одного в другое. Например, вы
начнете с некоторого явно эфемерного состояния, но по мере роста
функциональности, его, возможно, придется перевести в состояние
приложения.
Итак, в любом приложении Flutter есть два концептуальных типа
состояния. Эфемерное состояние может быть реализовано с помощью State и
setState() и часто является локальным для одного виджета. Остальное — это
состояние вашего приложения.
Оба типа находят свое место в любом приложении Flutter, и разделение
между ними зависит от ваших собственных предпочтений и сложности
приложения.

Рисунок 5 – Разновидности концептуальных состояний приложения

16
Управление состояниями.

Создание приложения начинают с составления макета


пользовательского интерфейса для приложения. Помимо сложности макета
добавляется сложность интерактивности приложения - способность
взаимодействовать с внешним миром.
Аспекты интерактивности:
1. потоки данных через приложение
2. изменение состояния приложения
3. взаимодействие виджетов
Стоимость ошибки в проектировании интерактивности может быть очень
большой по ходу развития проекта.

Прагматический подход управления состояниями.


Методология разработки, при которой необходимо управлять
состояниями и соблюдать требования к хорошему коду:
1. Код должен быть читаем и поддерживаем
2. Легко тестируемый, flutter поддерживает сохранение состояния и
изменение кода при исполнении приложения.
3. Код должен быть производительный.
Нет готового универсального решения, всегда присутствуют особые случаи.
Обычно это изменение состояния виджета в результате сетевого
взаимодействия или системного вызова. Рассмотрим пример подхода.
Определение реакции в ответ на внешнее воздействие.
Допустим на главной странице приложения 2 виджета Piechart и
слайдер. Слайдер знает о своем положении. Piechart может рисовать свое
состояние. Если нужно реагировать на изменения, Piechart нужно хранить
состояние. Итак, нужно связать 2 состояния положение слайдера и состояние
Piechart.
Применим наивный подход. Объявим глобальную переменную для
передачи данных по состоянию. Таким образом мы получили сильно
связанные компоненты, для масштабируемости это плохо. Такой подход
чреват дальнейшими ошибками
Более корректным будет ООП подход. Объявим вспомогательный
класс очереди, который будет инкапсулировать хранение всех происходящих
в системе событий. Очередь ответственна за оповещение нужных
компонентов о происходящих событиях.

17
Пакет для управления состояний.

Provider - компонент для управления состояний. С помощью Provider


решается ряд важных проблем:
1. Происходит освобождение памяти. Часто после обработки состояния
требуется освобождение ресурсов, например, состояние может быть не
просто числом, а сложным объектом - открытое соединение с базой
данных и после чтения БД нужно закрыть его. На этот случай
Package:provider содержит метод освобождения dispose.
2. Состояния могут быть совершенно разнородны и хранить их все в
одном классе непрактично. Package:provider содержит виджет
MultipleProvider, который позволяет разбить разные типы состояний по
подклассам.
3. Обработка состояний может приводить к многократной перерисовке
экрана, поэтому Package:provider позволяет организовать
иерархическое хранение состояний.
4. Измерение производительности. Нужно стараться менять как можно
меньше виджетов, избежать обновления всего дерева виджетов на
экране. На практике приходиться искать компромиссы. Поэтому в
flutter встроен профилировщик. Это позволяет выдвигать гипотезы,
тестировать их и переходить к следующим шагам в разработке, что
часто дает прирост в 12-15% производительности и может быть
критичным и определяющим.
5. Если присутствует только один виджет, Package:provider использовать
нет необходимости.
6. Flutter содержит тестовый фреймворк, который позволяет проверять
виджеты при наличии других виджетов.
7. Package:provider наполняется тестовыми событиями для нужных
виджетов и таким образом выполняется интеграционное тестирование.
Управление состояний также можно провести через Redux, Rx, hooks и
т.д.,но именно пакет provider прост для понимания, там мало кода. В нем
также используются концепции, которые применимы в любом другом
подходе.
Для иллюстрации рассмотрим приложение интернет магазина. Оно имеет
два отдельных экрана: каталог и корзину(представленные виджетами
MyCatalog и MyCart соответственно). Можно предложить ту же структуру
приложении для социальных сетей (если заменить «catalog» на «wall»(стена
пользователя), а «cart» на «favorites»(избранное)).

18
Рисунок 6 – Экраны приложения интернет магазина.

Слева направо: экран каталога товаров, экран при выборе товаров и экран корзины.

Экран каталога включает в себя пользовательскую панель приложений


(MyAppBar) и прокручиваемое представление множества элементов списка
(MyListItems).

Рисунок 7 – Архитектура приложения интернет магазина.

Таким образом, у нас есть по крайней мере 5 подклассов Widget.


Многим из них нужен доступ к состоянию, которое "принадлежит" другим.
Например, каждый MyListItem должен иметь возможность добавлять себя в
19
корзину. Также может понадобиться посмотреть, находится ли текущий
отображаемый элемент уже в корзине. Это подводит нас к первому вопросу:
где мы должны разместить текущее состояние корзины?
Отделение состояния.

Во Flutter имеет смысл хранить состояние вне виджетов, которые его


используют. В таких декларативных фреймворках, как Flutter, если вы хотите
изменить пользовательский интерфейс, вам придется его перестраивать. Нет
простого способа сделать MyCart.updateWith(somethingNew). Другими
словами, сложно императивно изменить виджет извне, вызвав на нем метод.
И даже если бы мы смогли это сделать, мы бы боролись с фреймворком, это
против его архитектуры.
Неправильный вариант:

void myTapHandler() {
var cartWidget = somehowGetMyCartWidget();
cartWidget.updateWith(item);
}

Получаем неправильный виджет:


Widget build(BuildContext context) {
return SomeWidget(
//начальное состояние корзины
);
}

Придется учитывать текущее состояние пользовательского интерфейса и


применять к нему новые данные. Таким образом трудно избежать ошибок.
Но на самом деле во Flutter мы создаем новый виджет каждый раз,
когда меняется его содержимое. Вместо MyCart.updateWith(somethingNew)
(вызов метода) вы используете MyCart(contents) (конструктор). Поскольку
создавать новые виджеты возможно только в методах построения их
родителей, если вы хотите изменить содержимое, оно должно жить в
родителе MyCart или выше по иерархии.

Правильный вариант:

void myTapHandler(BuildContext context) {


var cartModel = somehowGetMyCartModel(context);
cartModel.add(item);
}

Теперь у MyCart есть только один вариант для создания любой версии
пользовательского интерфейса.
20
Widget build(BuildContext context) {
var cartModel = somehowGetMyCartModel(context);
return SomeWidget(
// только единожды описать интерфейс.
// ···
);
}

В нашем примере содержимое корзины должно принадлежать MyApp.


Всякий раз, когда оно меняется, он перестраивает MyCart как прародитель
(подробнее об этом позже). Поэтому MyCart не нужно беспокоиться о своем
жизненном цикле - он просто объявляет, что показывать для любого данного
содержимого. Когда содержимое это меняется, старый виджет MyCart
исчезает и полностью и заменяется новым.

Cart

Рисунок 8. Отделение состояния. Содержимое корзины хранится в классе приложения.

Именно это имеют в виду, когда говорят, что виджеты неизменяемы.


Они не меняются - они заменяются.
Итак, сохранили состояние корзины, теперь посмотрим, как получить к
нему доступ. Когда пользователь нажимает на один из товаров в каталоге, он
добавляется в корзину. Но поскольку корзина фактически находится над
MyListItem, как это сделать?
Простой вариант – MyApp предоставляет обратный вызов, который
MyListItem может вызвать при нажатии на него. Функции языка dart
являются объектами, поэтому вы можете передавать их как угодно. Поэтому
внутри MyCatalog можно определить следующее:

21
@override
Widget build(BuildContext context) {
return SomeWidget(
//создать виджет и передать ссылку на метод.
MyListItem(myTapCallback),
);
}

void myTapCallback(Item item) {


print('user tapped on $item');
}

Это работает, но для состояния приложения, которое нужно изменять


из разных объектов, придется передавать множество обратных вызовов, что
сделает код слабочитаемым.
Во Flutter есть механизмы, позволяющие виджетам предоставлять
данные и сервисы своим потомкам (другими словами, не только своим детям,
но и всем виджетам, расположенным ниже их). Как и следовало ожидать от
Flutter, где все является виджетом, эти механизмы - просто специальные
виды виджетов-InheritedWidget, InheritedNotifier, InheritedModel и другие. Но
мы будем использовать пакет provider, который использует низкоуровневые
виджеты, но прост в использовании.

name: my_name
description: Blah blah blah.

# ...

dependencies:
flutter:
sdk: flutter

provider: ^6.0.0

dev_dependencies:
# ...

Прежде чем работать с провайдером, нужно добавить зависимость от


него в свой pubspec.yaml. С провайдером вам не нужно беспокоиться об
обратных вызовах или InheritedWidgets. Необходимо понимать три класса:

22
1. ChangeNotifier — это простой класс, включенный в Flutter SDK, который
предоставляет слушателям уведомления об изменениях. Другими словами,
если что-то является ChangeNotifier, вы можете подписаться на его
изменения (это разновидность Observable - паттерна проектирования). В
провайдере ChangeNotifier — это один из способов инкапсуляции состояния
вашего приложения. Для очень простых приложений можно обойтись одним
ChangeNotifier. В сложных приложениях будет несколько моделей, а значит,
и несколько ChangeNotifier.
В нашем примере приложения для покупок мы хотим управлять
состоянием корзины в ChangeNotifier. Мы создаем новый класс, который
расширяет его, следующим образом:

class CartModel extends ChangeNotifier {


/// Internal, private state of the cart.
final List<Item> _items = [];

/// неизменяемый список продуктов в корзине.


UnmodifiableListView<Item> get items =>
UnmodifiableListView(_items);

/// цена всех продуктов в корзине (допустим каждый по 42).


int get totalPrice => _items.length * 42;

/// добавить [item] в корзину. Только таким образом или через


[removeAll]
/// корзину можно изменять.
void add(Item item) {
_items.add(item);
// уведомить все виджеты которые подписаны на изменения
корзины. Пересоздание их.
notifyListeners();
}

/// очистить корзину.


void removeAll() {
_items.clear();
// уведомить все виджеты которые подписаны на изменения
корзины. Пересоздание их.
notifyListeners();
}
}

Единственный код, специфичный для ChangeNotifier, - это вызов


notifyListeners(). Вызывайте этот метод каждый раз, когда модель изменяется
таким образом, что это может изменить пользовательский интерфейс вашего
приложения. Все остальное в CartModel - это сама модель и ее бизнес-логика.
ChangeNotifier является частью flutter:foundation и не зависит от каких-либо
23
классов более высокого уровня во Flutter. Он легко тестируется (даже не
нужно использовать для него виджетное тестирование).
Например, вот простой модульный тест CartModel:

test('adding item increases total cost', () {


final cart = CartModel();
final startingPrice = cart.totalPrice;
cart.addListener(() {
expect(cart.totalPrice, greaterThan(startingPrice));
});
cart.add(Item('Dash'));
});

2. ChangeNotifierProvider — это виджет, который предоставляет экземпляр


ChangeNotifier своим потомкам. Разместить ChangeNotifierProvider следует
над виджетами, которые должны к нему обращаться. В случае с CartModel
это означает, что где-то над MyCart и MyCatalog. Не стоит размещать
ChangeNotifierProvider выше, чем это необходимо (потому что это загрязняет
область видимости). Но в нашем случае единственным виджетом, который
находится поверх MyCart и MyCatalog, является MyApp.

void main() {
runApp(
ChangeNotifierProvider(
create: (context) => CartModel(),
child: const MyApp(),
),
);
}

Обратите внимание, что мы определяем конструктор, который создает новый


экземпляр CartModel. ChangeNotifierProvider не перестраивает CartModel без
крайней необходимости. Он также автоматически вызывает dispose() для
CartModel, когда экземпляр больше не нужен. Если нужно предоставить
более одного класса, можно использовать MultiProvider:

void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) =>
CartModel()),
Provider(create: (context) => SomeOtherClass()),
],
child: const MyApp(),

24
),
);
}

3. Consumer. Теперь, когда CartModel предоставлена виджетам нашего


приложения через объявление ChangeNotifierProvider в верхней части, мы
можем начать ее использовать с помощью виджета Consumer.

return Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Total price: ${cart.totalPrice}');
},
);

Мы должны указать тип модели, к которой хотим получить доступ. В


данном случае нам нужна CartModel, поэтому мы пишем
Consumer<CartModel>. Если вы не укажете тип (<CartModel>), провайдер не
поймет нас. Провайдер основан на типах, и без типа он не знает, чего мы
хотим.
Единственный обязательный аргумент виджета Consumer — это
builder. Builder — это функция, которая вызывается всякий раз, когда
ChangeNotifier изменяется. (Другими словами, когда вы вызываете
notifyListeners() в вашей модели, вызываются все методы builder всех
соответствующих виджетов Consumer).

Конструктор вызывается с тремя аргументами:


1. Первый — это контекст, который вы также получаете в каждом методе
build.
2. Вторым аргументом функции builder является экземпляр
ChangeNotifier. Можно использовать данные в модели, чтобы
определить, как должен выглядеть пользовательский интерфейс в
любой момент времени.
3. Третий аргумент - child - служит для оптимизации. Если у вас есть
большое поддерево виджетов под Consumer, которое не меняется при
изменении модели, вы можете создать его один раз и получить через
конструктора.

25
return Consumer<CartModel>(
builder: (context, cart, child) => Stack(
children: [
// допустим SomeExpensiveWidget очень сложно трудоемко
перерисовать создаем его единожды.
if (child != null) child,
Text('Стоимость: ${cart.totalPrice}'),
],
),
// Build the expensive widget here.
child: const SomeExpensiveWidget(),
);

Лучше всего помещать consumer виджеты как можно глубже в дереве. Мы же


не хотим перестраивать большие части пользовательского интерфейса только
потому, что где-то изменилась какая-то деталь.

Непарвильно:
return Consumer<CartModel>(
builder: (context, cart, child) {
return HumongousWidget(
// ...
child: AnotherMonstrousWidget(
// ...
child: Text('Стоимость: ${cart.totalPrice}'),
),
);
},
);

Правильно:
return HumongousWidget(
// ...
child: AnotherMonstrousWidget(
// ...
child: Consumer<CartModel>(
builder: (context, cart, child) {
return Text('Стоимость: ${cart.totalPrice}');
},
),
),
);

В заключении можно сказать, что для управления состояниями


виджетов, следует применять высокоуровневые классы из пакета provider из-
за их удобства и разработанности методологической базы. тот механизм
может применяться для простого внедрения зависимостей и даже как основа
для более расширенных вариантов управления состоянием
26
Виджеты.

Виджет - примитив графического интерфейса пользователя, имеющий


стандартный внешний вид и выполняющий стандартные действия.
Пользовательский интерфейс создается на основе виджетов.
Виджеты описывает, как должно выглядеть их визуальное
представление с учетом их текущей конфигурации и состояния. Когда
состояние виджета меняется, он перестраивает свое описание, которое
фреймворк сравнивает с предыдущим описанием, чтобы определить
минимальные изменения, необходимые в дереве рендеринга для перехода из
одного состояния в другое.
Flutter поставляется с набором мощных базовых виджетов, из которых
наиболее часто используются следующие:

Виджет Текст.
Виджет Text позволяет создавать в приложении стилизованный текст.
Виджет Строка, колонка.
Эти гибкие виджеты позволяют создавать гибкие макеты в горизонтальном
(Row) и вертикальном (Column) направлениях.
Дизайн этих объектов основан на веб-модели компоновки flexbox.
Виджет Стек.
Вместо линейной ориентации (по горизонтали или вертикали) виджет Stack
позволяет размещать виджеты друг над другом
в порядке рисования. Вы можете использовать виджет Positioned для
дочерних элементов стека, чтобы расположить их относительно верхнего,
правого, нижнего или левого края стека. Стеки основаны на модели
компоновки абсолютного позиционирования в Интернете.
Виджет Контейнер.
Виджет Container позволяет создать прямоугольный визуальный элемент.
Контейнер можно украсить с помощью BoxDecoration,
например, фоном, границей или тенью. Контейнер также может иметь поля,
набивку и ограничения, применяемые к его размерам. Кроме того, контейнер
может быть преобразован в трехмерном пространстве с помощью матрицы.

взять скрины основных виджетов


https://docs.flutter.dev/development/ui/widgets/basics
Flutter предоставляет ряд виджетов, которые помогают создавать
приложения в соответствии с популярным макетом Material Design.
Приложение Material начинается с виджета MaterialApp, который
создает ряд виджетов в корне приложения, включая Navigator, который

27
управляет стопкой виджетов, обозначенных строками, также известными как
"маршруты".
Навигатор позволяет плавно переходить от одного экрана приложения
к другому. Использование виджета MaterialApp совершенно необязательно,
но является очень частой практикой.

Второстепенные особенности фреймворка.


1. Управление жестами
Изменение виджетов в ответ на ввод(механизм управления состояниями)
Реакцию на события жизненного цикла виджета (функции обратного вызова)
2. Ввод с клавиатуры

Различие между отзывчивыми и адаптивными приложениями.


Flutter спроектирован для создания приложений, которые будут
отлично выглядеть и работать на любой платформе.Это означает, что
приложение может отображаться на экранах самых разных размеров - от
часов, складного телефона с двумя экранами до монитора высокой четкости.
Мы хотим получить адаптивное и отзывчивое приложение.Эти
термины похожи, но это не одно и то же.
Адаптивное и отзывчивое приложения можно рассматривать как
отдельные аспекты приложения: адаптивное приложение, может быть не
отзывчивым, или наоборот. Приложение может быть как адаптивным, так и
неадаптивным.
Отзывчивость приложения.
Как правило, отзывчивое приложение имеет макет, настроенный под
доступный размер экрана. Это означает , что пользовательский интерфейс
перестраивается, если пользователь изменяет размер окна или ориентацию
устройства. Это особенно необходимо, когда одно и то же приложение может
работать на различных устройствах, от часов, телефона, планшета до
ноутбука или настольного компьютера.

Адаптивность приложения
Адаптация приложения для работы на различных типах устройств,
таких как мобильные и настольные, требует работы с мышью и клавиатурой,
а также с сенсорным вводом. Это также означает, что существуют разные
требования относительно визуальной плотности пикселей, выбора
компонентов (например, каскадные меню вместо списков), использования
специфических для платформы функций (например, всплывающих окон
верхнего уровня) и т. д.

28
Алгоритм создания UI в Flutter.

1.Выбрать виджеты для обеспечения адаптивности и отзывчивости


Align , AspectRatio, ConstrainedBox и другие

2.Решить проблему визуальной плотности.Различные устройства ввода


обеспечивают разный уровень точности, что требует разного размера
областей отображения. Класс VisualDensity во Flutter позволяет регулировать
плотность представления во всем приложении, например, сделать кнопку
больше (и, следовательно,ее легче нажимать) на сенсорном устройстве.

3.Нужна ли контекстная верстка? Если нужно не просто изменение


плотности, то применим более процедурный подход для настройки
параметров, расчета размеров, в итоге произойдет полная замена виджетов
или полная перестройки пользовательского интерфейса
в соответствии с конкретным форм-фактором(соотношением размеров).

4.Нужна ли сегментация по видам устройств? Можно поставлять


специальный дизайн для каждого устройства или класса устройств.

5.Унификация дизайна. Единый дизайн для приложения создаем с помощью


таких значений стиля, как padding, spacing, форма углов, размер шрифта и так
далее, это достигается через вспомогательные классы.

6.Обеспечить поддержку горячих клавиш, сочетания клавиш, движения


мыши.

Идиомы и нормы, конвенции платформы.

В конце концов, необходимо учитывать при создании адаптивных


приложений - это стандарты платформы. Каждая платформа имеет свои
собственные идиомы и нормы; номинальные стандарты, они определяют
ожидания пользователей относительно того, как должно вести себя
приложение. Отчасти благодаря Интернету пользователи привыкли вести
себя адаптивно, привыкать, но следование стандартам платформы все еще
может дать значительные преимущества.

29
Следование стандартам платформы.
Снижение когнитивной нагрузки - благодаря соответствию
существующей модели мышления пользователя, выполнение задач
становится интуитивно понятным, что требует меньше размышлений,
повышает производительность
и снижает разочарование.

Укрепление доверия - пользователи могут испытывать отторжение,


настороженность, если приложения не соответствуют их ожиданиям. И
наоборот, знакомый пользовательский интерфейс может укрепить доверие
пользователей и улучшить восприятие.

Flutter предлагает широкий выбор инструментов для обеспечения


качества UI ,повышения отзывчивости и адаптивности, что следовательно
ведет к повышению рейтинга приложения.

30
ВЫВОД

Плюсы разработки на Flutter.


Flutter имеет множество преимуществ перед своими конкурентами. Эти
преимущества присущи языку программирования dart и набору средств
разработки; они решают задачи, с которыми не справляются другие языки.

1. Одна кодовая база для всех платформ.

Прошли те времена, когда приходилось писать код для Android и другую


кодовую базу для устройств iOS. Это значительно сокращает время
разработки, снижает затраты и позволяет запускать приложение намного
быстрее.

2. Принцип «Все описываем виджетом» предлагает множество


возможностей.

Пользовательские виджеты Flutter — это удобство при создании визуальных


эффектов, при этом не нужно беспокоиться о разнообразии устройств.

3. Развитые библиотеки.

Flutter использует библиотеку Skia Graphics Library, это быстрая графическая


библиотекой с открытым исходным кодом. Оа перерисовывает
пользовательский интерфейс каждый раз при изменении представления.
Flutter это быстрая загрузка и плавная работа приложения.

4. Быстрое тестирование с горячей перезагрузкой

Функция горячей перезагрузки значительно ускоряет разработку


приложения. С Flutter нет необходимости перезагружать приложение, чтобы
увидеть каждое изменение, которое вы вносите в код. Вы можете легко
вносить изменения в свое приложение в режиме реального времени, поэтому
у вас больше возможностей экспериментировать с кодом и исправлять
ошибки на ходу.

31
Минусы разработки приложений Flutter

Безупречных технологических решений не бывает, и Flutter не исключение.


Минусы Flutter не совсем решающие, обозначим несколько причин, по
которым он может быть не идеальным набором инструментов для
конкретного приложения.

1. Большой размер файла из-за виджетов

Приложения Flutter довольно большие и «тяжелые» для начала. Они


занимают много места и требуют больше времени для загрузки или
обновления.

2. Комплексное обновление

Обновление требований к программированию в операционных системах


требует обновления модулей Flutter. Поскольку модули интегрированы в
программу как неотъемлемые элементы, последнюю также необходимо
перекомпилировать и переустановить на устройствах.


3. Ограниченный набор инструментов и библиотек

Flutter — довольно новая среда разработки, поэтому в некоторых случаях вы


не сможете получить нужные функции в текущей библиотеке. Flutter
потребуется некоторое время для создания определенных инструментов,
расширения функционала и развития сообщества.

32
СПИСОК ИСПОЛЬЗУЕМЫХ ИСТОЧНИКОВ

33

Вам также может понравиться