inherited widget flutter что это

InheritedWidget во Flutter

Перевод статьи подготовлен для студентов курса «Flutter Mobile Developer».

Корни деревьев виджетов во Flutter могут уходить очень глубоко…

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

Если не держать его под контролем, этот шаблон может очень легко расползтись по всей кодовой базе. Лично мы параметризовали более 30 виджетов таким образом. Почти половину рабочего времени виджет получал параметры только для того, чтобы передать их далее, как в MyWidget из примера, приведенного выше.

Состояние MyWidget не зависит от параметров, и тем не менее, он перестраивается каждый раз, когда меняются параметры!

Конечно, должен быть способ получше…

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

Если дополнить вышеприведенный пример, используя InheritedWidget, вот что мы получим:

InheritedWidget должен быть небольшой

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

Используйте const для создания ваших виджетов

Виджеты уровня страницы не могут преодолевать границу маршрута

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

Вот что видит Flutter:

С точки зрения Flutter иерархии навигации не существует. Каждая страница (или scaffold) представляет собой дерево виджетов, привязанное к виджету приложения. Следовательно, когда вы используете Navigator.push для отображения этих страниц, они не наследуют виджет, несущий родительский контекст. В приведенном выше примере, вам нужно будет в явном виде передавать контекст Student из страницы Student в страницу Student Bio.

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

Источник

InheritedWidget class Null safety

Base class for widgets that efficiently propagate information down the tree.

To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.dependOnInheritedWidgetOfExactType.

Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.

Implementing the of method

The convention is to provide a static method of on the InheritedWidget which does the call to BuildContext.dependOnInheritedWidgetOfExactType. This allows the class to define its own fallback logic in case there isn’t a widget in scope. In the example above, the value returned will be null in that case, but it could also have defaulted to a value.

Sometimes, the of method returns the data rather than the inherited widget; for example, in this case it could have returned a Color instead of the FrogColor widget.

Occasionally, the inherited widget is an implementation detail of another class, and is therefore private. The of method in that case is typically put on the public class instead. For example, Theme is implemented as a StatelessWidget that builds a private inherited widget; Theme.of looks for that inherited widget using BuildContext.dependOnInheritedWidgetOfExactType and then returns the ThemeData.

Читайте также:  какой мусор можно выбрасывать в синий контейнер в подмосковье

Calling the of method

When using the of method, the context must be a descendant of the InheritedWidget, meaning it must be «below» the InheritedWidget in the tree.

Источник

Передача параметров с помощью InheritedWidget в Flutter

Пример использования библиотеки InheritedWidget в Flutter приложении для передачи параметров между виджетами.

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

Часто нужно передавать какие либо данные между виджетами.

Можно передавать через конструктор. Предположим, у нас есть счетчик. У меня есть экран HomePage, а в нем я буду скрывать кнопку после 3х нажатий. Но количество нажатий выводит виджет CounterText вложенный в CounterBar.

Можно запустить код на сайте DartPad и посмотреть результат.

Но как это будет выглядеть при передачи через 5, 10 и более виджетов? А что, если нужно добавить или перенести виджет в другое дерево? Придется переписать много виджетов.

Достать виджет из контекста можно с помощью метода findAncestorStateOfType, в результате пробежав вверх по дереву, можно найти нужный нам виджет.

Реализация с использованием findAncestorStateOfType:

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

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

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

На помощь приходит InheritedWidget. Мы можем обернуть виджет в InheritedWidget и получить доступ к этому InheritedWidget ниже по дереву из BuildContext. Данный виджет специально предназначен для передачи данных через BuildContext любому и быстрому доступу к этим данным.

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

Еще одна особенность виджета, это наличие метода updateShouldNotify.

Чтобы обновить данные в InheritedWidget, мы должны пересоздать данный виджет передав новые данные. После этого будет вызван метод:

В котором нужно вернуть true если необходимо уведомить об изменении данных и перерисовать виджеты, которые подписаны на обновления. То есть, виджеты, которые обратятся к InheritedWidget подпишутся на уведомления о изменении данных, что позволит вызвать метод build в подписавшихся виджетах.

Еще один полезный метод, это getElementForInheritedWidgetOfExactType. Этот метод вернет виджет так же быстро, но не подпишется на обновления. Это полезно, если нужно получить доступ например из initState.

Теперь немного изменим старый код:

Код очень похож на прошлый, мы обернули CounterBar в CounterState для передачи стейта ниже через BuildContext. Но теперь для частых вызовов c глубокой вложенностью виджетов этот код будет работать быстрее.

Рекомендую так же ознакомиться с пакетом Provider, основанном на InheritedWidget.

Источник

Флаттер: как правильно использовать Inherited Widget?

Как правильно использовать InheritedWidget? До сих пор я понимал, что это дает вам возможность распространять данные по дереву виджетов. В крайнем случае, если вы укажете RootWidget, он будет доступен из всех виджетов в дереве на всех маршрутах, и это нормально, потому что каким-то образом я должен сделать свою ViewModel / Model доступной для моих виджетов, не прибегая к глобальным или синглетонам.

Читайте также:  какой официальный праздник в июле 2021

НО InheritedWidget является неизменным, так как я могу его обновить? И что более важно, как мои Stateful Widgets запускаются для восстановления их поддеревьев?

К сожалению, документация здесь очень непонятна, и после большого обсуждения никто, похоже, не знает, как правильно ее использовать.

Я добавляю цитату из Брайана Игана:

Да, я вижу это как способ распространения данных по дереву. Что меня смущает из документации по API:

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

Когда я впервые прочитал это, я подумал:

Я мог бы вставить некоторые данные в InheritedWidget и изменить его позже. Когда эта мутация произойдет, она перестроит все виджеты, которые ссылаются на мой InheritedWidget. Что я нашел:

Чтобы изменить состояние InheritedWidget, вам нужно обернуть его в StatefulWidget. Затем вы фактически изменяете состояние StatefulWidget и передаете эти данные в InheritedWidget, который передает данные всем его дочерним элементам. Однако в этом случае, кажется, перестраивается все дерево под StatefulWidget, а не только виджеты, которые ссылаются на InheritedWidget. Это верно? Или он каким-то образом узнает, как пропустить виджеты, которые ссылаются на InheritedWidget, если updateShouldNotify возвращает false?

3 ответа

Проблема исходит из вашей цитаты, которая неверна.

Исправленная цитата будет:

Дело в том, что: когда вы создаете новый виджет; флаттер сравнит его со старым. Повторно используйте это «Элемент», который указывает на RenderBox. И изменить свойства RenderBox.

Окей, но как это ответит на мой вопрос?

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

Если вы все поняли, вы должны были уже угадать решение:

Конечный результат в реальном коде будет:

Но разве создание нового InheritedWidget не перестроит все дерево?

Нет, это не обязательно. Поскольку у вашего нового InheritedWidget потенциально может быть тот же самый дочерний элемент, что и раньше. И под точным я имею в виду тот же пример. Виджеты, которые имеют тот же экземпляр, что и раньше, не восстанавливаются.

InheritedWidget управляет централизованными данными приложения и передает их ребенку, как мы можем хранить здесь количество корзин, как объяснено здесь:

[BuildContext.inheritFromWidgetOfExactType] получает ближайший виджет заданного типа, который должен быть типом конкретного подкласса InheritedWidget, и регистрирует этот контекст компоновки с этим виджетом таким образом, чтобы при изменении этого виджета (или вводе нового виджета этого типа, или виджет исчезает), этот контекст сборки перестраивается, чтобы он мог получать новые значения из этого виджета.

Источник

Русские Блоги

[Flutter] Введение в использование InheritedWidget и InheritedModel

Во Flutter есть четыре вида виджетов

среди них StatelessWidget с StatefulWidget Наиболее часто рассматривается, классифицируется с точки зрения государственного управления; RenderObjectWidget Это базовый класс всех виджетов, которые необходимо отобразить.

InheritedWidget

To obtain the nearest instance of a particular type of inherited widget from a build context, use BuildContext.inheritFromWidgetOfExactType.

Inherited widgets, when referenced in this way, will cause the consumer to rebuild when the inherited widget itself changes state.

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

Читайте также:  какой лучше холодильник хаер или самсунг

Демо кода

Затем сравните использование или отсутствие кода InheritedWidget Разница:

Нажмите +, и 0 наверху изменится, но текст в середине не изменится.

Традиционная реализация

После изменения состояния кнопки все виджеты A, B и C будут перестроены.

Используя Flutter Performance в AndroidStudio, вы можете увидеть, что виджеты A, B и C участвовали в перестройке.

Реализовано с использованием InheritedWidget

Вы можете видеть, что при изменении состояния ни виджет B, ни C не перестраиваются.

Описание ключевого кода

против InheritedWidget Объясняются ключевые классы в версии.

WidgetA、WidgetC

В традиционной версии WidgetA и C передают состояние и обратный вызов родительского элемента через конструктор.
В версии InheritedWidget его можно получить с помощью следующего статического метода

Далее подробно рассмотрим статический метод получения состояния. HomePage.of

HomePage

Вот несколько ключевых методов получения превосходных виджетов:

method description
inheritFromWidgetOfExactType Получить последний виджет верхнего уровня данного типа. Виджет должен быть подклассом InheritedWidget и зарегистрировать входящий контекст в виджете верхнего уровня. Когда виджет верхнего уровня изменяется, виджет, удерживаемый этим контекстом, будет перестроен чтобы получить новый из значения виджета. Вот как дочерний элемент регистрируется в InheritedWidget.
inheritFromWidgetOfExactType Он используется только для получения последнего виджета верхнего уровня данного типа и не будет перестроен из-за изменений в виджете верхнего уровня.
ancestorInheritedElementForWidgetOfExactType Функция такая же, как и inheritFromWidgetOfExactType, но будут найдены только подклассы InheritedWidget, поэтому виджет более высокого уровня можно найти со сложностью O (1).

Следовательно, widgetA перестраивается с изменением родительского виджета, а widgetB не перестраивается.

_MyInheritedWidget

Унаследовано от InheritedWidget, поэтому дочерний виджет может передавать inheritFromWidgetOfExactType Получать.

updateShouldNotify Контролируйте, нужно ли дочернему виджету ощущать свое изменение, если он возвращает true, то передайте inheritFromWidgetOfExactType Зарегистрированный дочерний виджет перестраивается после его изменений

Тогда посмотри на это еще раз HomePageState

HomePageState

При традиционном способе написания виджеты A, B и C создаются и возвращаются непосредственно в сборке, поэтому при изменении состояния дочерние виджеты будут воссозданы и перестроены;

TopPage

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

InheritedModel

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

InheritedModel Унаследованный от InheritedWidget, вы можете передать строковый ключ ( aspect ) Чтобы указать конкретный дочерний виджет для восстановления.

Кратко рассмотрим разницу в реализации между версией InheritedModel и версией InheritedWidget.

Использовать InheritedModel.inheritFrom Получить виджет

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

Более частичное обновление

Если widgetA такой же, мы надеемся и дальше контролировать частичное обновление его дочерних виджетов.

Если вы хорошо понимаете BuildContext с InheritedWidget Механизм регистрации легко реализовать:

Создайте виджет анонимного класса через Builder, а затем HomePage.of Переместитесь в его интерьер. на данный момент InheritedWidget Контекст, в котором зарегистрирован, больше не является widgetA, а представляет собой виджет анонимного класса, поэтому он может обеспечить частичное обновление widgetA.

Не использовать InheritedWidget

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

Наконец

Многие компоненты во Flutter реализованы на основе InheritedWidget, например Scoped Model 、 BLoC(Business Logic of component) Подождите, если вы хотите освоить использование этих расширенных функций, начните с понимания InheritedWidget.

Источник

Сказочный портал