[Перевод] Основные концепции Flutter: Widget, State, Context и InheritedWidget

Программа перевода самородков Flutter

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

Сложность:новичок

предисловие

во флаттереWidget,StateиContext— одна из самых важных концепций, которую должен полностью понимать каждый разработчик Flutter.

Хотя существует много документации, ни одна из них не объясняет это ясно.

Я собираюсь объяснить эти концепции своими словами, зная, что это может расстроить некоторых чистых теоретиков, но настоящая цель этой статьи — попытаться прояснить следующие темы:

  • Разница между виджетом с сохранением состояния и виджетом без сохранения состояния
  • что такое контекст
  • Что такое состояние и как его использовать
  • Связь между контекстом и его объектом состояния
  • InheritedWidget и как распространять информацию в дереве виджетов
  • концепция реконструкции

Эта статья также была опубликована вMedium - Flutter Community.

Часть 1: Концепции

Концепция виджета

существуетFlutter, почти все естьWidget.

положитьWidgetДумайте об этом как о визуальном компоненте (или компоненте, который взаимодействует с визуальными аспектами приложения).

Когда вам нужно создать что-либо, прямо или косвенно связанное с компоновкой, вы используетеWidget.

Концепция дерева виджетов

WidgetОрганизована в виде дерева.

Виджеты, содержащие другие виджеты, называютсяРодительский виджет(илиКонтейнер виджетов). включен вРодительский виджетВиджет называетсяДетский виджет.

давайте использоватьFlutterАвтоматически сгенерированное базовое приложение для иллюстрации этого. Ниже приведен упрощенный код, толькоbuildметод:

@override
Widget build(BuildContext){
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new Text(
              'You have pushed the button this many times:',
            ),
            new Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: new Icon(Icons.add),
      ),
    );
}

Если мы теперь рассмотрим этот базовый пример, мы получим следующую структуру дерева виджетов (Ограничьте список виджетов, присутствующих в коде):

state diagram basic

Концепция контекста

Еще одно важное понятие –Context.

ContextПросто ссылка на расположение виджета в древовидной структуре всех созданных виджетов.

Короче говоря,contextКак часть дерева виджетов, в это дерево добавляется виджет, соответствующий контексту.

Одинcontextтолько подчиненныйОдинвиджет.

Если у виджета «А» есть дочерние виджеты, то виджет «А»contextбудет иметь непосредственное отношение кдочерний контекстизродительский контекст.

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

Если мы теперь попытаемся проиллюстрировать на диаграмме вышеContextконцепт, получаем(Все еще очень упрощенный вид) каждый цвет представляет собойcontext(Кроме MyApp, это другое):

state diagram basic context

Контекстная видимость (Краткое описание):

что-тоВиден только в своем собственном контексте или в своем родительском контексте.

С приведенным выше описанием мы можем извлечь его из дочернего контекста, легко найтипредок(= родитель) Виджет.

В качестве примера рассмотрим Scaffold > Center > Column > Text: context.ancestorWidgetOfExactType(Scaffold) => возвращает первый Scaffold, получая древовидную структуру из контекста Text.

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

Тип виджета

Виджеты бывают 2 типов:

Stateless Widget

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

Другими словами, эти виджеты не заботятся ни о какихРазнообразие.

Такой виджет называетсяStateless Widget.

Типичными примерами таких виджетов могут быть текст, строка, столбец и контейнер и т. д. При построении мы просто передаем им некоторые параметры.

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

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

Жизненный цикл виджета без сохранения состояния

Следующее сStateless WidgetСоответствующая типовая структура кода.

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

class MyStatelessWidget extends StatelessWidget {

	MyStatelessWidget({
		Key key,
		this.parameter,
	}): super(key:key);

	final parameter;

	@override
	Widget build(BuildContext context){
		return new ...
	}
}

Даже если есть другой метод, который можно переопределить (createElement), последний почти никогда не переписывается. Тольконеобходимостьпереписанbuildметод.

Жизненный цикл такого виджета без сохранения состояния довольно прост:

  • инициализация
  • Рендер через build()

Stateful Widget

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

удерживаемый виджетомданныеМножество может меняться в течение своего существования, такие данные называютсяState.

Эти виджеты называютсяStateful Widget.

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

Государственная концепция

StateОпределенныйStatefulWidgetэкземпляр "поведение".

Он содержит дляВзаимодействие / вмешательствоИнформация о виджете:

  • поведение
  • макет

применительно кStateЛюбые изменения заставят виджетреконструкция.

Связь между состоянием и контекстом

заStateful Widget,StateиContextСвязанный. и эта ассоциацияпостоянныйиз,Stateобъект никогда не изменит своеcontext.

Несмотря на то, что контекст виджета можно перемещать по древовидной структуре,Stateвсе равно будет сcontextСвязанный.

когдаStateиContextКогда связано,StateСчитаетсясмонтированный.

фокус:

Государственный объектиcontextсвязаны, это означает, чтоГосударственный объектдаНет(прямое интервьюдругой контекст! (Мы обсудим это позже).


Жизненный цикл виджета с отслеживанием состояния

Теперь, когда основные понятия раскрыты, пришло время углубиться...

Следующее сStateful WidgetСоответствующая типовая структура кода.

Поскольку основная цель этой статьи — использовать «переменные» данные для объясненияStateконцепции, я намеренно пропущу все, что связано с Stateful WidgetперезаписываемыйОбъяснение методов, которые здесь не особо уместны. Эти переопределяемые методыdidUpdateWidget, деактивировать и собрать заново. О них пойдет речь в следующей статье.

class MyStatefulWidget extends StatefulWidget {

	MyStatefulWidget({
		Key key,
		this.parameter,
	}): super(key: key);

	final parameter;

	@override
	_MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

	@override
	void initState(){
		super.initState();

		// Additional initialization of the State
	}

	@override
	void didChangeDependencies(){
		super.didChangeDependencies();

		// Additional code
	}

	@override
	void dispose(){
		// Additional disposal code

		super.dispose();
	}

	@override
	Widget build(BuildContext context){
		return new ...
	}
}

На следующей диаграмме показана последовательность операций/вызовов, связанных с созданием виджета с отслеживанием состояния (упрощенная версия). В правой части диаграммы вы заметите, что в потоке данныхStateВнутреннее состояние объекта. Вы также увидите, что контекст и состояние теперь связаны, и, таким образом, контекст становится доступным (mounted).

state diagram

Далее давайте объясним это некоторыми дополнительными деталями:

initState()

После создания объекта StateinitState()method — это первый вызываемый метод (после конструктора). Этот метод будет переопределен, когда вам потребуется выполнить дополнительную инициализацию. Общие инициализации связаны с анимацией, контроллерами и т.д. Если вы переопределяете этот метод, вы должны сначала вызватьsuper.initState().

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

однаждыinitState()Когда выполнение метода завершается, объект State инициализируется, и контекст становится доступным.

Этот метод больше не будет вызываться в течение всего времени существования этого объекта State.

didChangeDependencies()

didChangeDependencies()method является вторым вызываемым методом.

На данном этапе из-заcontextдоступен, так что вы можете использовать его.

Если ваш виджет связан сInheritedWidgetи/или вам нужно инициализировать некоторыеlisteners(на основеcontext), который обычно переопределяет этот метод.

Обратите внимание, что если ваш виджет связан сInheritedWidget, который вызывается каждый раз, когда виджет перестраивается.

Если вы переопределяете этот метод, вы должны сначала вызватьsuper.didChangeDependencies().

build()

build(BuildContext context)метод вdidChangeDependencies()didUpdateWidget) называется после.

Здесь вы строите свой виджет (и, возможно, любые поддеревья).

Каждый раз, когда объект State обновляется(или когда InheritedWidget требует уведомления"зарегистрирован"widget) вызовет этот метод!!

Чтобы принудительно выполнить перестроение, вам может потребоваться вызватьsetState((){…})метод.

dispose()

dispose()Метод вызывается, когда виджет устарел.

Если вам нужно выполнить некоторые операции очистки (например, прослушиватели), переопределите этот метод и вызовите его сразу послеsuper.dispose().

Виджет без сохранения состояния или с сохранением состояния?

Это вопрос, который многие разработчики должны задать себе:Нужно ли, чтобы виджет был без сохранения состояния или с сохранением состояния?

Чтобы ответить на этот вопрос, спросите себя:

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

Если ответ на вопросyes, то вам нуженStatefulВиджет, в противном случае вам нуженStatelessВиджет.

Некоторые примеры:

  • Виджет для отображения списка флажков. Для отображения флажков необходимо рассмотреть ряд пунктов. Каждый элемент представляет собой объект, содержащий заголовок и статус. Если вы установите флажок, соответствующий item.status будет переключаться;

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

  • Экран со столом. Виджет, который позволяет пользователю заполнить форму и отправить форму на сервер.

    при этих обстоятельствах,Если вы не хотите проверить форму или сделать что-то еще перед отправкой,ОдинStatelessВиджета может быть достаточно.


Stateful Widget состоит из 2 частей

все еще помнюStatefulструктура виджета? Есть 2 части:

Определение виджета

class MyStatefulWidget extends StatefulWidget {
    MyStatefulWidget({
		Key key,
		this.color,
	}): super(key: key);

	final Color color;

	@override
	_MyStatefulWidgetState createState() => new _MyStatefulWidgetState();
}

первая часть "MyStatefulWidget” обычноpublicчасть. Вы можете создать его экземпляр, когда вам нужно добавить его в дерево виджетов. Этот раздел не меняется в течение срока службы виджета, но может приниматьStateПараметры, используемые при создании экземпляра.

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

Определение состояния виджета

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
    ...
	@override
	Widget build(BuildContext context){
	    ...
	}
}

Вторая часть "_MyStatefulWidgetStat» Управление виджетами в жизненном циклеРазнообразие, и заставлять экземпляр виджета перестраиваться каждый раз, когда применяется модификация. имя, начинающееся с '_’ делает этот класс полезным для файлов .dartчастный.

Если вам нужно сослаться на этот класс вне файла .dart, не используйте '_' префикс.

_MyStatefulWidgetStateдоступ к классам можно получить с помощьювиджет.{имя переменной}к доступу хранится вMyStatefulWidgetлюбая переменная в . В этом примере:widget.color.


Уникальный идентификатор виджета - Ключ

В Fultter каждый виджет однозначно идентифицируется. этот уникальный идентификаторна этапе сборки/рендерингаОпределяется фреймворком.

Уникальный идентификатор соответствует необязательномуKeyпараметр. Если вы опустите этот параметр, Flutter создаст его для вас.

В некоторых случаях может потребоваться форсировать этоkey, чтобы к виджету можно было получить доступ по его ключу.

Для этого можно использовать любой из следующих способов:GlobalKey,LocalKey,UniqueKeyилиObjectKey.

GlobalKeyУбедитесь, что сгенерированный ключ уникален для всего приложения.

Заставить виджеты использовать уникальные идентификаторы:

GlobalKey myKey = new GlobalKey();
...
@override
Widget build(BuildContext context){
    return new MyWidget(
        key: myKey
    );
}

Часть 2: Как получить доступ к состоянию?

Как упоминалось ранее,Stateсвязано сконтекст, иContextсвязан с виджетомпример.

1. Сам виджет

Теоретически, единственный, кто может получить доступStateдаСамо состояние виджета.

В этом случае нет никаких сложностей. Класс Widget State может получить доступ к любой внутренней переменной.

2. Прямой дочерний виджет

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

В этом случае, чтобы получить доступ к этим прямым дочерним элементамState,тебе надоучитьсяOни.

Самый простой способ позвонить кому-либо черезназвание. Во Flutter каждый виджет имеет уникальный идентификатор, который определяется фреймворком вПри построении/рендерингеКонечно. Как показано ранее, вы можете использоватьkeyПараметр устанавливает идентификатор для виджета.

...
GlobalKey<MyStatefulWidgetState> myWidgetStateKey = new GlobalKey<MyStatefulWidgetState>();
...
@override
Widget build(BuildContext context){
    return new MyStatefulWidget(
        key: myWidgetStateKey,
        color: Colors.blue,
    );
}

Однажды определив,отецВиджет может получить доступ к своим дочерним узлам.State:

myWidgetStateKey.currentState

Давайте рассмотрим базовый пример отображения SnackBar, когда пользователь нажимает кнопку. Поскольку SnackBar является дочерним виджетом Scaffold, никакие другие дочерние узлы внутри Scaffold не могут получить к нему прямой доступ (Помните концепцию контекста и его иерархическую/древовидную структуру?). Таким образом, единственный способ получить к нему доступ - черезScaffoldState, который предоставляет общедоступный метод для отображения SnackBar.

class _MyScreenState extends State<MyScreen> {
    /// the unique identity of the Scaffold
    final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

    @override
    Widget build(BuildContext context){
        return new Scaffold(
            key: _scaffoldKey,
            appBar: new AppBar(
                title: new Text('My Screen'),
            ),
            body: new Center(
                new RaiseButton(
                    child: new Text('Hit me'),
                    onPressed: (){
                        _scaffoldKey.currentState.showSnackBar(
                            new SnackBar(
                                content: new Text('This is the Snackbar...'),
                            )
                        );
                    }
                ),
            ),
        );
    }
}

3. Виджет предков

Предположим, у вас есть виджет, принадлежащий поддереву другого виджета, как показано на изображении ниже.

state child get state

Для этого необходимо выполнить 3 условия:

1."Виджет с состоянием』 (красный) должен раскрыть свойState

занезащищенныйТотState, виджет должен записать его при создании, например:

class MyExposingWidget extends StatefulWidget {

   MyExposingWidgetState myState;

   @override
   MyExposingWidgetState createState(){
      myState = new MyExposingWidgetState();
      return myState;
   }
}

2."Widget State» необходимо раскрыть некоторые методы получения/установки

чтобы"другие" чтобы установить/получить свойства в состоянии,Widget StateДоступ должен быть авторизован:

  • общественная собственность (не рекомендуется)
  • getter / setter

пример:

class MyExposingWidgetState extends State<MyExposingWidget>{
   Color _color;

   Color get color => _color;
   ...
}

3."Виджеты, заинтересованные в получении состояния"(синий) должен получитьStateцитаты

class MyChildWidget extends StatelessWidget {
   @override
   Widget build(BuildContext context){
      final MyExposingWidget widget = context.ancestorWidgetOfExactType(MyExposingWidget);
      final MyExposingWidgetState state = widget?.myState;

      return new Container(
         color: state == null ? Colors.blue : state.color,
      );
   }
}

Это решение легко реализовать, но как дочерний виджет узнает, когда его нужно перестроить?

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

В следующем разделе речь пойдетInherited Widgetконцепция, она может решить эту проблему.


InheritedWidget

короче,InheritedWidgetразрешено вwidgetЭффективно распространяйте (и делитесь) информацией по дереву.

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

База

Чтобы объяснить это, давайте рассмотрим следующий фрагмент кода:

class MyInheritedWidget extends InheritedWidget {
   MyInheritedWidget({
      Key key,
      @required Widget child,
      this.data,
   }): super(key: key, child: child);

   final data;

   static MyInheritedWidget of(BuildContext context) {
      return context.inheritFromWidgetOfExactType(MyInheritedWidget);
   }

   @override
   bool updateShouldNotify(MyInheritedWidget oldWidget) => data != oldWidget.data;
}

Приведенный выше код определяет файл с именем "MyInheritedWidget" Виджет, цель состоит в том, чтобы предоставить некоторые "общий"данные.

Как упоминалось ранее, для того, чтобы иметь возможность распространять/совместно использовать некоторые данные, они должны бытьInheritedWidgetрасположен в верхней части дерева виджетов, что объясняет@required Widget childпараметр.

static MyInheritedWidget of(BuildContext context)метод позволяет всем дочерним виджетам получить ближайшийMyInheritedWidgetПримеры (см. ниже).

последний переписатьupdateShouldNotifyспособ сказатьInheritedWidgetесли правильноданныеИзменено, должны ли уведомления доставляться всем дочерним виджетам (зарегистрированным/подписанным) (см. ниже).

Поэтому нам нужно поместить его на уровень узла дерева следующим образом:

class MyParentWidget... {
   ...
   @override
   Widget build(BuildContext context){
      return new MyInheritedWidget(
         data: counter,
         child: new Row(
            children: <Widget>[
               ...
            ],
         ),
      );
   }
}

Как дочерние узлы получают доступ к данным InheritedWidget?

Последний получит ссылку на InheritedWidget при создании дочернего элемента, например:

class MyChildWidget... {
   ...

   @override
   Widget build(BuildContext context){
      final MyInheritedWidget inheritedWidget = MyInheritedWidget.of(context);

      ///
      /// 此刻,该 widget 可使用 MyInheritedWidget 暴露的数据
      /// 通过调用:inheritedWidget.data
      ///
      return new Container(
         color: inheritedWidget.data.color,
      );
   }
}

Как взаимодействовать между виджетами?

Рассмотрим структуру дерева виджетов, показанную на рисунке ниже.

inheritedwidget tree

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

  • «Виджет А» — это кнопка, которая добавляет товар в корзину;
  • «Виджет B» — это текст, показывающий количество товаров в корзине;
  • «Виджет C» находится рядом с виджетом B и представляет собой текст со встроенным произвольным текстом;
  • Мы хотим, чтобы «Виджет А» автоматически отображал правильное количество товаров в корзине при нажатии «Виджет Б», но мы не хотим перестраивать «Виджет С».

Для этого сценарияInheritedWidgetединственный правильный вариант виджета!

образец кода

Давайте сначала напишем код, а затем объясним его:

class Item {
   String reference;

   Item(this.reference);
}

class _MyInherited extends InheritedWidget {
  _MyInherited({
    Key key,
    @required Widget child,
    @required this.data,
  }) : super(key: key, child: child);

  final MyInheritedWidgetState data;

  @override
  bool updateShouldNotify(_MyInherited oldWidget) {
    return true;
  }
}

class MyInheritedWidget extends StatefulWidget {
  MyInheritedWidget({
    Key key,
    this.child,
  }): super(key: key);

  final Widget child;

  @override
  MyInheritedWidgetState createState() => new MyInheritedWidgetState();

  static MyInheritedWidgetState of(BuildContext context){
    return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
  }
}

class MyInheritedWidgetState extends State<MyInheritedWidget>{
  /// List of Items
  List<Item> _items = <Item>[];

  /// Getter (number of items)
  int get itemsCount => _items.length;

  /// Helper method to add an Item
  void addItem(String reference){
    setState((){
      _items.add(new Item(reference));
    });
  }

  @override
  Widget build(BuildContext context){
    return new _MyInherited(
      data: this,
      child: widget.child,
    );
  }
}

class MyTree extends StatefulWidget {
  @override
  _MyTreeState createState() => new _MyTreeState();
}

class _MyTreeState extends State<MyTree> {
  @override
  Widget build(BuildContext context) {
    return new MyInheritedWidget(
      child: new Scaffold(
        appBar: new AppBar(
          title: new Text('Title'),
        ),
        body: new Column(
          children: <Widget>[
            new WidgetA(),
            new Container(
              child: new Row(
                children: <Widget>[
                  new Icon(Icons.shopping_cart),
                  new WidgetB(),
                  new WidgetC(),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return new Container(
      child: new RaisedButton(
        child: new Text('Add Item'),
        onPressed: () {
          state.addItem('new item');
        },
      ),
    );
  }
}

class WidgetB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context);
    return new Text('${state.itemsCount}');
  }
}

class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Text('I am Widget C');
  }
}

инструкция

В этом очень простом примере:

  • _MyInheritedЯвляетсяInheritedWidget, он создается заново каждый раз, когда мы добавляем элемент с помощью кнопки «Виджет A».
  • MyInheritedWidgetЯвляетсяStateВиджет, содержащий список элементов. в состоянии пройтиstatic MyInheritedWidgetState of(BuildContext context)посетитьState
  • MyInheritedWidgetStateраскрывает приобретениеitemsCountгеттерный метод иaddItemметоды, чтобы их могли использовать виджеты, которыесынчасть дерева виджетов
  • Каждый раз, когда мы добавляем элемент в State,MyInheritedWidgetStateвосстановит
  • MyTreeКласс только строит дерево виджетов и будетMyInheritedWidgetкак корневой узел дерева
  • WidgetAпростойRaisedButton, когда она нажата, она изменится снедавнийизMyInheritedWidgetвызов в случаеaddItemметод
  • WidgetBпростойText, для отображениянедавнийуровеньMyInheritedWidgetКоличество предметов

Как все это работает?

Зарегистрируйте виджет для последующих уведомлений

Когда вызывается дочерний виджетMyInheritedWidget.of(context), он проходит самостоятельноcontextи вызовите следующий метод MyInheritedWidget.

static MyInheritedWidgetState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited).data;
}

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

За кулисами простой вызов этого статического метода на самом деле делает две вещи:

  • потребительвиджет автоматически добавляется вподписчиксписок, чтобы приInheritedWidget(вот_MyInherited) при применении модификаций виджет можетреконструкция
  • _MyInheritedвиджет (он жеMyInheritedWidgetState), указанные в данных, будут возвращены впотребитель

Процесс

Поскольку используются «Виджет А» и «Виджет Б»InheritedWidgetподписался, так что если_MyInheritedМодификация применяется, затем при нажатии виджета ARaisedButton, процесс работы выглядит следующим образом (упрощенный вариант):

  1. перечислитьMyInheritedWidgetStateизaddItemметод
  2. _MyInheritedWidgetState.addItemметод добавления нового элемента в список
  3. перечислитьsetState()перестроитьMyInheritedWidget
  4. Создано из нового контента в списке_MyInheritedновый экземпляр
  5. _MyInheritedзапись через параметр (data) прошел новыйState
  6. в видеInheritedWidget, он проверяет,Уведомить потребителей(ответ верный)
  7. пройти весьпотребительсписок (здесь виджет A и виджет B) и запросить их перестроение
  8. Поскольку Wiget C непотребитель, поэтому он не будет восстанавливаться.

Пока это работает!

Однако и виджет A, и виджет B перестраиваются, но, поскольку в виджете A ничего не изменилось, перестраивать не нужно. Так как же предотвратить это?

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

Причина, по которой виджет A перестраивается одновременно, заключается в том, что он обращается кMyInheritedWidgetStateПуть.

Как прежде звонилcontext.inheritFromWidgetOfExactType()фактически автоматически подпишет виджет напотребительСписок.

Избегайте автоматических подписок, но при этом разрешайте доступ к виджету A.MyInheritedWidgetStateРешение состоит в том, чтобы переоборудоватьMyInheritedWidgetстатический метод:

static MyInheritedWidgetState of([BuildContext context, bool rebuild = true]){
    return (rebuild ? context.inheritFromWidgetOfExactType(_MyInherited) as _MyInherited
                    : context.ancestorWidgetOfExactType(_MyInherited) as _MyInherited).data;
}

Добавив дополнительный параметр типа boolean...

  • еслиrebuildпараметр true (по умолчанию), используем обычный метод (и добавляем виджет в список подписчиков)
  • еслиrebuildпараметр ложный, мы все еще можем получить доступ к данным,ноНе используйInheritedWidgetизВнутренняя реализация

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

class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final MyInheritedWidgetState state = MyInheritedWidget.of(context, false);
    return new Container(
      child: new RaisedButton(
        child: new Text('Add Item'),
        onPressed: () {
          state.addItem('new item');
        },
      ),
    );
  }
}

Вот и все, когда мы нажимаем виджет A, он больше не перестраивается.

Некоторые специальные инструкции для маршрутов, диалогов...

Маршруты, контекст диалогов иApplicationсвязывать.

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

Единственный способ, которым экран B хочет знать контекст экрана A, — это получить его через экран A и передать его в качестве параметра в Navigator.of(context).push(….)

интересная ссылка

в заключении

На эти темы еще многое предстоит сказать… особенно вInheritedWidgetначальство.

В следующей статье я представлюNotifiers / Listenersпонятия, они используютStateТакже очень интересно то, как передаются данные.

Итак, следите за обновлениями и удачного кодирования.

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


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.