flutter buildcontext что такое
Перевод статьи Flutter in Context автора Greg Perry
Детальный разбор класса BuidContext
(Эта статья является частью серии Decode Flutter Series)
Вы уже знакомы с контекстными объектами? Я имею в виду объекты класса BuildContext с именем context, который постоянно передается функции build(), а также является необходимым параметром для множества статических функций:
Они помогают идти вверх или сквозь «дерево рендеринга» (или же «дерево виджетов»). Мы подробно рассмотрим эти объекты в этой статье, заглянем «под капот» фреймворка Flutter и выясним, из чего именно состоит этот объект BuiltContext под названием context. А это означает, что мы «пойдем» сквозь код. Собственно, не буду долго тянуть и держать вас в неведении и прямо сейчас рассажу вам, что именно это за объект: это элемент.
Нажмите на скриншот, чтобы открыть отрывок кода.
Я всегда предпочитаю использовать скриншоты в своих статьях для отображения самого концепта, а не просто кода. К тому же, я считаю, что с ними проще работать. Однако, вы можете кликнуть на изображение, чтобы открыть сам код в gist или в Github. Также эту статью лучше читать не на мобильном устройстве, а через компьютер.
Not In Context
Так уж получилось, что мы не собираемся подробно рассматривать сам класс BuildContext. Хоть он и является ключевым во фреймворке Flutter, просто представления о том, что он делает в приложении, будет вполне достаточно. К тому же, это абстрактный класс – вы должны создать подкласс и реализовать все поля и функции, что он содержит.
Ниже предоставлен скриншот класс со всей его документацией и удаленными устаревшими функциями – просто чтобы дать вам представление об его роли во Flutter. Вы даже можете узнать некоторые функции и даже удивиться тому, в каком классе они находятся. Затем мы определим точный подкласс, который использует BuildContext. Правда, эту загадку я уже раскрыл выше.
Элементы виджетов
Отойдем немного назад и посмотрим сперва на класс StatelessWidget. Ниже расположен скриншот одного такого класса со всей его документацией, чтобы вы могли рассмотреть, из чего он состоит. Не очень-то много всего, правда? Это абстрактный класс, и его подкласс, конечно, должен реализовывать метод build() – это мы уже знаем. Однако, что насчет метода createElement()? Он создает другой класс под названием StatelessElement и,фактически, ссылается на себя в качестве параметра.
Я решил оставить всю немногочисленную документацию класса StatelessElement. Обратите внимание, указанный в конструкторе параметр widget класса StatelessWidget передается родительскому классу ComponentElement, туда мы и перейдем:
Класс ComponentElement становится более обширным, так что я сделал скриншот начала этого класса. Это тоже абстрактный класс, что имеет смысл, поскольку он содержит тот самый необходимый для выполнения метод build(). Сейчас мы возвращаемся назад через иерархию классов, однако, мы еще посетим эти «промежуточные» классы через некоторое время. Сейчас мы должны обратить внимание на класс Element.
Опять-таки, мы смотрим только на начало этого класса. Он гораздо больше тех, что мы рассматривали ранее: в целом, он состоит из 90 методов и полей. В самом деле, довольно важный «элемент». Однако, мы пришли к цели. Что вы видите?
Класс Element реализует другой класс, BuildContext. В отличие от, к примеру, Java, любой класс в языке программирования Dart может быть использован, как интерфейс – вы просто добавляете его название в конце декларации класса сразу после ключевого слова implements.
Конечно, если только ваш класс не является абстрактным, вы должны реализовать все методы и поля, составляющие этот класс. В случае с классом Elements, мы реализуем класс BuildContext со всей его составляющей – не удивительно, что теперь мы видим огромное количество полей и методов. После выполнения этой непростой задачки, мы наконец-то можем заключить, что, как и во всех ориентированных на объект языках, объект типа Element может быть передан функциям или конструкторам как объект типа BuildContext.
Документация Flutter поясняет, что BuildContext было решено использовать в качестве интерфейса с целью препятствия прямому манипулированию объектами Element – что бы это ни значило. К тому же, кто вообще захочет разбираться с 90 методами и полями класса?
Так что, теперь вы знаете, что при размещении контрольной точки в своей любимой IDE прямо в строке метода build(), параметр контекста (context), переданный этму методу (в случае StatelessWidget) – тот самый объект StatelessElement, создание которого мы видели в скриншоте StatelessWidget. Обратите внимание, что это касается и StatefulWidget – только с объектом StatefulElement. Оба эти типа виджетов представлены ниже:
Помните, что у каждого виджета есть свой объект Element (или же объект BuildContext). Element/BuildContext может принадлежать только одному виджету. Когда вы только начинали изучать Flutter, скорее всего, вы пришли к выводу, что все виджеты, составляющие ваше приложение, расположены в древообразном порядке, и это правда, только не совсем. Это их сообщающиеся объекты Element, связанные друг с другом. Каждый элемент (контекст) содержит отсылку на свой «родительский» элемент, который создает тот самый элемент (контекст) при запуске приложения Flutter. Можно сказать, что приложение Flutter состоит из связанных друг с другом объектов BuildContext, которые и составляют «древо объектов BuildContext». Они остаются неизменными в течение всего цикла приложения, в то время как их аналоги-«виджеты» могут быть удалены и воссозданы вновь и вновь.
Поместим это в контекст
Я воспользуюсь приложением из другой статьи для демонстрации «процесса наполнения», задействованного при запуске приложения и того, как каждый виджет включает в себя свой собственный аналог элемента (контекста). Домашний экран приложения со всем его кодом представлен ниже. На первый взгляд, мы имеем десять виджетов:
Скриншоты ниже показывают, что происходит при вызове метода createElement для StatelessWidget. Как только это происходит, в «родительском» методе элемента, inflateWidget(), создается соответствующий объект элемента виджета. На среднем скриншоте отображена эта функция и показано, как недавно созданный объект элемента вызывает свою собственный метод mount(). Это происходит там же, где и «родительский» элемент привязывается к полю дочернего экземпляра _parent, добавляясь к древу рендеринга. Каждому виджету соответствует свой элемент/контекст объекта.
А теперь с Context
При помощи ссылки на объекты BuildContext у вас появляется все необходимое для возврата к дереву, чтобы получить все «выше» созданные экземпляры Widgets и объекты State. К примеру, все эти функции «of» перечислены в начале этой статьи, и каждая из них содержит функцию из BuidContext, реализованную и определенную в классе Element:
Тема оформления
Напоследок, разберем еще парочку более популярных исполнений. Работая с Flutter, вы обнаружите, что статичная функция Theme.of() используется несколько раз при работе фреймворка. Ниже слева есть скриншот, показывающий весьма распространенное действие – получение объекта ThemeData для применения определенного стиля для виджета Text. На скриншоте справа отображен определенный в классе BuidContext метод dependInheritWidgetOfExactType, используемый для поиска определенного «типа InheritedWidget» в объекте Map под названием _inheritedWidgets.
Ищем Scaffold
В приложениях Flutter также весьма распространен метод Scaffold.of(). Он содержит прочие методы, определенные в классе BuildContext и реализованные в классе Element. Также он идет вверх через весь список связанных объектов Element, чтобы найти ранее определенный объект State. Практически целый цикл связан с возвращением через связанный список объектов Element ( каждый из них связан со своим «родителем») в целях поисках виджета определенного типа. В нашем случае выполняется поиск Scaffold, определенного ранее в древе рендеринга, а затем возвращается его связанный объект State под названием ScaffoldState.
Пока что остановимся на этом. Класс Element реализует класс BuildContext, и, возможно, чуть позже мы обратимся к ряду других функций, с помощью которых можно извлекать множество «элементов» вашего приложения, если они уже были созданы и упомянуты как часть древа рендеринга.
What does BuildContext do in Flutter?
What does BuildContext do, and what information do we get out of it?
https://flutter.io/widgets-intro/#basic-widgets on the 9th instance of the term BuildContext there is an example, but it’s not clear how it is being used. It’s part of a much larger set of code that loses me, and so I am having a hard time understanding just what BuildContext is.
Can someone explain this in simple/very basic terms?
2 Answers 2
BuildContext is, like it’s name is implying, the context in which a specific widget is built.
If you’ve ever done some React before, that context is kind of similar to React’s context (but much smoother to use) ; with a few bonuses.
Generally speaking, there are 2 use cases for context :
The second point is kinda rare. On the other hand, the first point is used nearly everywhere.
Notice the context here. It’ll be used to get the closest instance of NavigatorState widget above in the tree. Then call the method pushNamed on that instance.
BuildContext is really useful when you want to pass data downward without having to manually assign it to every widgets’ configurations for example ; you’ll want to access them everywhere. But you don’t want to pass it on every single constructor.
You could potentially make a global or a singleton ; but then when confs change your widgets won’t automatically rebuild.
And then, use it this way :
And even cooler is that all widgets who call inheritFromWidgetOfExactType(Configuration) will automatically rebuild when the configurations change.
Как работает Flutter
Как Flutter работает на самом деле?
Что такое Widgets, Elements, BuildContext, RenderOject, Bindings.
Вступление
В прошлом году (прим: в 2018), когда я начал свое путешествие в сказочный мир Flutter, в Интернете было очень мало информации по сравнению с тем, что есть сегодня. Сейчас, несмотря на то, что уже написано много материалов, лишь небольшая их часть рассказывает о том, как на самом деле работает Flutter.
Что же такое Widgets (виджеты), Elements (элементы), BuildContext? Почему Flutter быстрый? Почему иногда он работает не так, как ожидается? Что такое деревья и зачем они нужны?
В 95% случаев при написании приложения вы будете иметь дело только с виджетами, чтобы что-то отображать на экране или взаимодействовать с ним. Но неужели вы никогда не задумывались, как вся эта магия работает внутри? Как система узнает, когда обновить экран и какие части должны быть обновлены?
Содержание:
Часть 1: Предыстория
В первой части статьи представлены некоторые ключевые понятия, которые будут использованы во второй части материала и помогут лучше понять Flutter.
Немного об устройстве
Давайте начнем с конца и вернемся к основам.
Когда вы смотрите на свое устройство или, точнее, на приложение, запущенное на вашем устройстве, вы видите только экран.
На самом деле, всё, что вы видите – это пиксели, которые вместе составляют 2-мерное изображение, и когда вы касаетесь экрана пальцем, устройство распознает только положение вашего пальца на стекле.
Вся магия приложения (с визуальной точки зрения) в большинстве случаев заключается в обновлении этого изображения на основе следующих взаимодействий:
Визуализация изображения на экране обеспечивается аппаратным обеспечением (дисплеем), которое регулярно (обычно 60 раз в секунду) обновляет дисплей. Эта называется «частотой обновления» и выражается в Гц (Герцах).
Дисплей получает информацию для отображения от GPU (Graphics Processing Unit), представляющего собой специализированную электронную схему, оптимизированную и предназначенную для быстрого формирования изображения из некоторых данных (полигонов и текстур). Количество раз в секунду, которое графический процессор может генерировать «изображение» (=буфер кадров) для отображения и отправки его на аппаратное обеспечение, называется кадровой частотой (прим: frame rate). Это измеряется с помощью блока кадров в секунду (например, 60 кадров в секунду или 60fps).
Вы, возможно, спросите меня, почему я начал эту статью с понятий 2-мерного изображения, отображаемого GPU / аппаратным обеспечением и датчиком физического стекла и какова связь с обычными виджетами Flutter?
Думаю, что будет легче понять, как на самом деле работает Flutter, если мы посмотрим на него с этой точки зрения, так как одна из главных целей приложения Flutter – создать это 2-мерное изображение и дать возможность взаимодействовать с ним. Также потому, что во Flutter, хотите верьте, хотите нет, почти все обусловлено необходимостью обновления экрана быстро и в нужный момент!
Интерфейс между кодом и устройством
Так или иначе, все интересующиеся Flutter уже видели следующую картинку, которая описывает архитектуру высокого уровня Flutter.
Когда мы пишем приложение Flutter, используя Dart, мы остаемся на уровне Flutter Framework (выделено зеленым цветом).
Flutter Framework взаимодействует с Flutter Engine (синим цветом) через слой абстракции, называемый Window. Этот уровень абстракции предоставляет ряд API для косвенного взаимодействия с устройством.
Также через этот уровень абстракции Flutter Engine уведомляет Flutter Framework, когда:
Управление Flutter Framework рендерингом Flutter Engine
В это сложно поверить, но это правда. За исключением некоторых случаев (cм. ниже) ни один код Flutter Framework не выполняется без запуска рендеринга Flutter Engine.
(Между нами говоря, на самом деле можно применить визуальное изменение без вызова от Flutter Engine, но это не рекомендуется делать)
Вы меня спросите: «Если какой-то код, связанный с жестом, выполняется и вызывает визуальное изменение или если я использую timer для задания периодичности задачи, которая приводит к визуальным изменениям (например, анимация), то как это работает?»
Если вы хотите, чтобы произошло визуальное изменение или чтобы какой-то код выполнялся на основе таймера, то вам нужно сообщить Flutter Engine, что что-то должно быть отрисовано.
Обычно при следующем обновлении Flutter Engine обращается к Flutter Framework для выполнения некоторого кода и в конечном итоге предоставляет новую сцену для рендеринга.
Поэтому важный вопрос заключается в том, как движок Flutter организует все поведение приложения на основе рендеринга.
Чтобы вам получить представление о внутренних механизмах, посмотрите на следующую анимацию:
Краткое объяснение (более подробная информация будет позже):
RenderView и RenderObject
Прежде чем погружаться в детали, связанные с потоком действий, самое время ввести понятие Rendering Tree.
Как уже говорилось ранее, всё в конечном итоге преобразуется в пиксели, которые будут отображаться на экране, и Flutter Framework преобразует Widgets, которые мы используем для разработки приложения, в визуальные блоки, которые будут отображаться на экране.
Данные визуальные части соответствуют объектам, называемым RenderObject, которые используются для:
Набор всех RenderObject формирует дерево, называемое Render Tree. В верхней части этого дерева (= root) мы находим RenderView.
RenderView представляет общую поверхность для объектов Render Tree и является специальной версией RenderObject.
Визуально мы могли бы представить все это следующим образом:
Cвязь между Widget и RenderObject будет рассмотрена далее. А пока пришло время немного углубиться…
Инициализация bindings
Во время вызова метода runApp() Flutter Framework инициализирует интерфейсы между собой и Flutter Engine. Эти интерфейсы называются bindings (прим: привязки).
Введение в привязки
Привязки предназначены для того, чтобы быть связующим звеном между фреймворком и движком Flutter. Только с помощью привязок можно обмениваться данными между Flutter Framework и Flutter Engine.
(Есть только одно исключение из этого правила – RenderView, но мы обсудим это позже).
Каждая привязка отвечает за обработку набора конкретных задач, действий, событий, сгруппированных по области деятельности.
На момент написания этой статьи во Flutter Framework насчитывается 8 привязок.
Ниже приведены 4 из них, которые будут рассмотрены в этой статье:
Для полноты картины упомяну и остальные 4:
Можно также упомянуть WidgetsFlutterBinding, но на самом деле это не является привязкой, а скорее своего рода «инициализатором привязки».
На следующей диаграмме показано взаимодействие между привязками, которые я собираюсь рассмотреть далее, и Flutter Engine.
Давайте посмотрим на каждую из этих «основных» привязок.
SchedulerBinding
У этой привязки есть две основные обязанности:
Когда SchedulerBinding запрашивает «тревожное пробуждение»?
Когда Ticker должен отработать новый tick
Например, у вас есть анимация, вы ее запускаете. Анимация кадрируется с помощью Ticker, который с регулярным интервалом (= tick) вызывается для выполнения обратного вызова. Чтобы запустить такой обратный вызов, нам нужно сказать Flutter Engine, чтобы он «разбудил» нас при следующем обновлении (= Begin Frame). Это запустит обратный вызов ticker для выполнения его задачи. Если ticker все еще нужно продолжить выполнение, то в конце своей задачи он вызовет SchedulerBinding для планирования другого кадра.
Когда надо обновить отображение
Например, надо отработать событие, которое приводит к визуальному изменению (пример: обновление цвета части экрана, прокрутка, добавление / удаление чего-либо с экрана), для этого нам нужно предпринять необходимые шаги, чтобы в конечном итоге показать на экране обновленное изображение. В этом случае, когда происходит такое изменение, Flutter Framework вызывает SchedulerBinding для планирования другого кадра с помощью Flutter Engine. (Позже мы увидим, как это работает на самом деле)
GestureBinding
Данная привязка слушает взаимодействие с движком в терминах «пальца» (= жест).
В частности, он отвечает за прием данных, относящихся к пальцу, и за определение того, с какой частью (частями) экрана работают жесты. Затем он соответственно уведомляет об этом / этих частях.
RendererBinding
Эта привязка является связующим звеном между Flutter Engine и Render Tree. Она отвечает за:
Чтобы предоставить изменения, которые будут отображаться на экране, RendererBinding отвечает за управление PipelineOwner и инициализацию RenderView.
PipelineOwner — это своего рода оркестратор, который знает, что нужно сделать с RenderObject в соответствии с компоновой, и координирует эти действия.
WidgetsBinding
Данная привязка прослушивает изменения, применяемые пользователем через настройки устройства, которые влияют на язык (= locale) и семантику.
Я предполагаю, что на более позднем этапе развития Flutter все события, связанные с семантикой, будут перенесены в SemanticsBinding, но на момент написания этой статьи это еще не так.
Кроме этого, WidgetsBinding является связующим звеном между виджетами и Flutter Engine. Она отвечает за:
Обработка изменений структуры виджетов осуществляется с помощью BuildOwner.
BuildOwner отслеживает, какие виджеты нуждаются в перестройке, и обрабатывает другие задачи, которые применяются к структуре виджетов в целом.
Часть 2. От виджетов к пикселям
Теперь, когда мы познакомились с основами внутренней работы Flutter, пришло время поговорить о виджетах.
Во всей документации Flutter вы прочитаете, что всё Widgets (виджеты).
Это почти правильно. Но для того, чтобы быть немного более точным, я бы скорее сказал:
Со стороны разработчика, всё, что связано с пользовательским интерфейсом с точки зрения компоновки и взаимодействия, делается с помощью виджетов.
К чему такая точность? К тому, что Widget позволяет разработчику определить часть экрана с точки зрения размеров, содержания, компоновки и взаимодействия, НО за этим есть гораздо большее. Так что же такое Widget на самом деле?
Неизменяемая конфигурация
Если вы посмотрите исходный код Flutter, то заметите следующее определение класса Widget.
Аннотация «@immutable» очень важна и говорит нам, что любая переменная в классе Widget должна быть FINAL, другими словами: «определена и назначена ОДИН РАЗ ДЛЯ ВСЕХ«. Таким образом, после создания экземпляр Widget больше не сможет изменить свои внутренние переменные.
Так как Widget неизменяемый, то можно его считать статичной конфигурацией
Иерархическая структура виджетов
Когда вы разрабатываете с помощью Flutter, вы определяете структуру своего экрана(ов), используя виджеты примерно так:
В этом примере используется 7 виджетов, которые вместе образуют иерархическую структуру. Очень упрощенная схема, основанная на данном коде, выглядит следующим образом:
Как можно заметить, представленная схема выглядит, как дерево, где SafeArea является его корнем.
Лес за деревьями
Как вы уже знаете, виджет сам по себе может быть агрегацией других виджетов. В качестве примера можно изменить предыдущий код следующим образом:
Данный вариант предполагает, что виджет «MyOwnWidget» сам будет отображать SafeArea, Scaffold. Но самое главное в этом примере заключается в том, что
Widget может представлять лист, узел в дереве, даже само дерево или, почему бы и нет, лес деревьев.
Понимание Element в дереве
Как будет показано далее, чтобы иметь возможность генерировать пиксели, которые составляют изображение, отображаемое на устройстве, Flutter должен знать в деталях все маленькие части, которые составляют экран, и, чтобы определить все части, ему необходимо знать раскрытие всех виджетов.
Чтобы проиллюстрировать данный момент, рассмотрим принцип матрёшки: в закрытом состоянии вы видите только 1 куклу, но она содержит другую, которая в свою очередь содержит ещё одну и так далее.
Когда Flutter «раскроет» все виджеты (часть экрана), это будет похоже на получение всех кукол (часть целого).
На картинке ниже показана часть конечной иерархической структуры виджетов, соответствующая предыдущему коду. Желтым цветом я выделил виджеты, которые были упомянуты в коде ранее, чтобы вы могли определить их в финальном дереве.
Формулировка «дерево виджетов» (Widget tree) существует только для облегчения понимания, поскольку программисты используют виджеты, но во Flutter НЕТ дерева виджетов!
На самом деле, правильнее будет сказать «дерево элементов» (tree of Elements)
Настало время ввести понятие элемента (Element).
Каждому виджету соответствует один элемент. Элементы связаны друг с другом и образуют дерево. Следовательно элемент является ссылкой на что-то в дереве.
Для начала подумайте об элементе как о узле, который имеет родителя и, возможно, ребенка. Связывая их вместе через отношение родитель — ребёнок, мы получаем древовидную структуру.
Как вы можете видеть, элемент указывает на один виджет, а также может указывать на RenderObject.
Даже лучше… Element указывает на Widget, который создал этот Element!
Давайте подведём итоги:
Элементы определяют, как части отображаемых блоков связаны друг с другом
Для того, чтобы лучше представить, где понятие элемент подходит, давайте рассмотрим следующее визуальное представление:
Как вы можете заметить, дерево элементов является фактической связью между виджетами и RenderObjects.
Но почему Widget создает Element?
3 категории виджетов
Во Flutter виджеты разделены на 3 категории, лично я называю их следующим образом (но это только мой способ классифицировать их):
Основная задача этих виджетов состоит в том, чтобы хранить некоторую информацию (которая должна быть доступной для виджетов), части древовидной структуры, основанной на Proxy. Примером таких виджетов является InheritedWidget или LayoutId.
Эти виджеты не принимают непосредственного участия в формировании пользовательского интерфейса, но используются для получения информации, которую они могут предоставить.
Данные виджеты имеют непосредственное отношение к компоновке экрана, поскольку они определяют (или используются для определения) размеры, положение, отрисовку. Типичными примерами являются: Row, Column, Stack, а также Padding, Align, Opacity, RawImage.
Это другие виджеты, которые предоставляют непосредственно не окончательную информацию, связанную с размерами, позициями, внешним видом, а скорее данные (или подсказки), которые будут использоваться для получения той самой финальной информации. Эти виджеты обычно называются компонентами.
Примеры: RaisedButton, Scaffold, Text, GestureDetector, Container.
В этом PDF-файле перечислена большая часть виджетов, сгруппированных по категориям.
Почему это разделение важно? Потому что в зависимости от категории виджета, соответствующий тип элемента связан с…
Типы элементов
Есть несколько типов элементов:
Как вы можете видеть на картинке выше, элементы делятся на 2 основных типа:
Эти элементы напрямую не отвечают за отрисовку какой-либо части отображения.
Данные элементы отвечают за части отображаемого изображения на экране.
Отлично! Столько информации, но как всё это связано друг с другом и почему об этом интересно рассказать?
Как виджеты и элементы работают вместе
Во Flutter вся механика основана на инвалидации элемента или renderObject.
Инвалидация элемента может быть сделана следующими способами:
Результатом инвалидации является то, что на соответствующий элемент появляется ссылка в списке dirty элементов.
Инвалидация renderObject означает, что структура элементов никак не меняется, но происходит изменение на уровне renderObject, например:
Результатом такой инвалидации является ссылка на соответствующий renderObject в списке объектов рендеринга (renderObjects), которые необходимо перестроить или перекрасить.
Независимо от типа инвалидации вызывается SchedulerBinding (помните такое?) для запроса к Flutter Engine, чтобы тот запланировал новый кадр.
Это именно тот момент, когда Flutter Engine «будит» SchedulerBinding и происходит вся магия.
onDrawFrame()
Ранее в этой статье мы отметили, что у SchedulerBinding две основные обязанности, одна из которых заключается в готовности обрабатывать запросы, создаваемые Flutter Engine, связанные с перестроением кадра. Это идеальный момент, чтобы сосредоточиться на этом.
Ниже на частичной диаграмме последовательности показано, что происходит, когда SchedulerBinding получает запрос onDrawFrame() от Flutter Engine.
Шаг 1. Элементы
Вызывается WidgetsBinding, и данная привязка сначала рассматривает изменения, связанные с элементами. WidgetsBinding вызывает метод buildScope объекта buildOwner, так как BuildOwner отвечает за обработку дерева элементов. Этот метод проходит по списку dirty элементов и запрашивает их перестроение (rebuild).
Основными принципами данного метода-перестроения ( rebuild() ) являются:
Следующая анимация попытается сделать это объяснение немного нагляднее.
Примечание по виджетам и элементам
Для нового виджета создаётся элемент конкретного типа, соответствущего категории виджета, а именно:
У каждого из этих типов элементов есть свое собственное поведение. Например:
Шаг 2. renderObjects
Теперь после завершения всех действий, связанных с dirty элементами, Element Tree является стабильным. Поэтому пришло время рассмотреть процесс визуализации.
Поскольку RendererBinding отвечает за обработку Render Tree, WidgetsBinding вызывает метод drawFrame RendererBinding.
Ниже на частичной диаграмме показана последовательность действий, выполняемых во время запроса drawFrame().
На этом шаге выполняются следующие действия:
В конце этого потока действий экран устройства обновляется.
Часть 3: Обработка жестов
Жесты (= события, связанные с действиями пальца на стекле) обрабатываются с помощью GestureBinding.
Когда Flutter Engine отправляет информацию о событии, связанном с жестом, через window.onPointerDataPacket API, то GestureBinding перехватывает её, выполняет некоторую буферизацию и:
Надеюсь, сейчас понятно, насколько важны renderObjects.
Часть 4: Анимации
Эта часть статьи посвящена понятию анимации и глубокому пониманию Ticker.
Когда вы работаете с анимациями, то вы обычно используете AnimationController или любой виджет для анимаций (прим: AnimatedCrossFade).
Во Flutter всё, что связано с анимациями, относится к Ticker. У Ticker, когда он активен, есть только одна задача: «он просит SchedulerBinding зарегистрировать обратный вызов и сообщить Flutter Engine, что надо разбудить его, когда появится новый обратный вызов». Когда Flutter Engine готов, он вызывает SchedulerBinding через запрос: «onBeginFrame«. SchedulerBinding обращается к списку обратных вызовов ticker и выполняет каждый из них.
Каждый tick перехватывается «заинтересованным» контроллером для его обработки. Если анимация завершена, то ticker «отключён», иначе ticker запрашивает SchedulerBinding для планирования нового обратного вызова. И так далее.
Полная картина
Теперь мы узнали, как работает Flutter:
BuildContext
Напоследок вернёмся к диаграмме, которая показывает различные типы элементов, и рассмотрим сигнатуру корневого Element:
Мы видим тот самый всем известный BuildContext! Но что это такое?
BuildContext — это интерфейс, определяющий ряд геттеров и методов, которые могут быть реализованы элементом. В основном BuildContext используется в методе build() StatelessWidget или State для StatefulWidget.
Это означает, что большинство разработчиков постоянно работают с элементами, даже не зная об этом.
Насколько полезным может быть BuildContext?
Поскольку BuildContext соответствует элементу, связанному с виджетом, а также местоположению виджета в дереве, то BuildContext может быть полезен, когда надо:
Забавы ради
ПРЕДУПРЕЖДЕНИЕ
Пожалуйста, не используйте этот код!
Его единственная задача – продемонстрировать, что StatelessWidget может запрашивать обновление.
Если вам нужно некоторое состояние с виджетом, пожалуйста, используйте StatefulWidget.
Заключение
Вы скажете: «Ещё одна длинная статья». Но я подумал, что вам было бы интересно узнать, как построена архитектура Flutter, и решил напомнить, что всё было разработано, чтобы быть эффективным, масштабируемым и открытым для будущих расширений. Кроме того, ключевые понятия, такие как Widget, Element, BuildContext, RenderObject, не всегда очевидны для восприятия. Могу только надеяться, что эта статья была для вас полезной.
Ждите новых новостей уже скоро. А пока позвольте пожелать вам успешного программирования.
PS Всю критику, вопросы и предложения по переводу буду рад услышать в (личных) сообщениях.
PSS Ещё раз ссылка на оригинальную статью Flutter internals от Didier Boelens, так как одной в шапке перевода для такого большого материала мало)