Какая связь между виджетами с сохранением состояния и без состояния во Flutter?


105

Виджет с отслеживанием состояния определяется как любой виджет, который меняет свое состояние в течение своего времени существования. Но это очень распространенная практика, когда a StatelessWidgetявляется StatefulWidgetодним из его дочерних элементов. Не StatelessWidgetпереходит в состояние, если у него есть StatefulWidgetодин из его дочерних элементов?

Я попытался заглянуть в документацию как часть кода StatelessWidget, но не мог понять, как StatelessWidgetможет иметь Statefulwidgetдочерние элементы и при этом оставаться StatelessWidget.

В чем связь и разница между виджетами с сохранением состояния и без состояния во Flutter?


2
Вы можете составить свой макет из различных типов виджетов, однако это не означает, что вы наследуете характеристики композиции, влияющие на каждый виджет. Я имею в виду, что у вас может быть Контейнер без состояния, у которого есть дочерний элемент другого Контейнера, который объявлен как StatefulWidget где-то еще, состояние контейнера будет влиять только на этот один компонент. Итак, все дело в композиции из разных типов виджетов, каждая функция которой вам нужна.
aziza

1
Для того, чтобы натворить еще больше, есть третий тип виджета: InheritedWidget; Который может сделать StatelessWidgetобновление.
Реми Русселе

Ответы:


103

StatelessWidget никогда не будет перестраивать само по себе (но может от внешних событий). StatefulWidget может. Это золотое правило.

НО любой виджет можно перекрашивать в любой момент.

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

Но вам не важно, какого типа ваши дети. На вас это никак не влияет.


11
(Относительно новый для фреймворка). В чем разница между rebuildиrepaint
user462455

Также из комментариев в коде фреймворка очевидно, что StateFulWidgets тоже неизменяемы.
user462455

3
Построение виджета - это, по сути, вызов метода "build" с последующим созданием / обновлением соответствующего рендеринга; за которым следует процесс покраски. Что напечатает эти рендеры на экране.
Реми Русселе

классы, наследующие StatefulWidget, неизменяемы. Но само состояние (State <YourWidget>) изменчиво.
Реми Русселе

1
@ RémiRousselet Виджеты с сохранением состояния и без состояния перестраивают каждый кадр, согласно flutter.dev/docs/get-started/flutter-for/…
Мэтт

82

StatefulWidget против StatelessWidget.

введите описание изображения здесь

StatelessWidget - виджет, не требующий изменяемого состояния.

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

  • statelessВиджет полезен , когда часть пользовательского интерфейса вы описываете не зависит ни от чего, кроме информации о конфигурации в самом объекте и BuildContext , в котором раздут виджет. Для композиций, которые могут динамически изменяться, например, из-за наличия внутреннего состояния, управляемого часами, или в зависимости от некоторого состояния системы, рассмотрите возможность использования StatefulWidget.

class GreenFrog extends StatelessWidget {
  const GreenFrog({ Key key }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(color: const Color(0xFF2DBD3A));
  }
}

StatefulWidget - виджет с изменяемым состоянием.

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

Когда Flutter строит StatefulWidget, он создает объект State. В этом объекте хранится все изменяемое состояние для этого виджета.

Понятие состояния определяется двумя вещами:

1) Данные, используемые виджетом, могут измениться.

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

Жизненный цикл StatefulWidget

Жизненный цикл состоит из следующих упрощенных этапов:

  1. createState () - когда Flutter получает указание создать StatefulWidget, он немедленно вызывает createState().
  • Создает изменяемое состояние для этого виджета в заданном месте дерева.

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

@override
_MyState createState() => _MyState();
  1. mount == true - все виджеты имеют this.mountedсвойство bool . Он становится истинным, когда buildContextприсваивается. Вызов, setStateкогда виджет отключен, является ошибкой . Находится ли данный объект State в данный момент в дереве.
  • После создания объекта State и перед вызовом initStateфреймворк «монтирует» объект State, связывая его с файлом
    BuildContext. Государство остается объект не установлено до каркасных
    звонков dispose(), после чего рамка никогда не запрашивает у
    объекта государственного строить заново.

  • Вызов setState является ошибкой, если не установлено значение true.

bool get mounted => _element != null;
  1. initState () - это первый метод, вызываемый при создании виджета (конечно, после конструктора класса).

initStateвызывается один раз и только один раз. Он должен позвонитьsuper.initState().

  • Инициализируйте данные, которые основаны на конкретном BuildContext для созданного экземпляра виджета.

  • Инициализируйте свойства, которые полагаются на эти «родительские» виджеты в дереве.

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

@override
initState() {
  super.initState();
  // Add listeners to this class
  cartItemStream.listen((data) {
    _updateWidget(data);
  });
}
  1. didChangeDependencies () - вызывается при изменении зависимости этого объекта State.
  • Этот метод также вызывается сразу после initState. Вызов BuildContext.inheritFromWidgetOfExactTypeиз этого метода безопасен .

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

@protected
@mustCallSuper
void didChangeDependencies() { }
  1. build () - описывает часть пользовательского интерфейса, представленную виджетом.

Платформа вызывает этот метод в различных ситуациях:

  • После звонка initState.
  • После звонка didUpdateWidget.
  • После звонка на setState.
  • После изменения зависимости этого объекта State (например, InheritedWidget, на который ссылаются предыдущие изменения сборки).
  • После вызова деактивируйте, а затем повторно вставьте объект State в дерево в другом месте.
  • Платформа заменяет поддерево под этим виджетом виджетом, возвращаемым этим методом, либо путем обновления существующего поддерева, либо путем удаления поддерева и расширения нового поддерева, в зависимости от того, может ли виджет, возвращаемый этим методом, обновить корень существующего поддерева. , как определено при звонке Widget.canUpdate.

  • Обычно реализации возвращают вновь созданное созвездие виджетов, которые настроены с использованием информации из конструктора этого виджета, заданного BuildContext и внутреннего состояния этого объекта State.

@override
  Widget build(BuildContext context, MyButtonState state) {
    ... () { print("color: $color"); } ...
  }
  1. didUpdateWidget () - вызывается при изменении конфигурации виджета.
  • Если родительский виджет перестраивается и запрашивает обновление этого места в дереве для отображения нового виджета с тем же типом среды выполнения и Widget.key, инфраструктура обновит свойство виджета этого объекта State, чтобы оно ссылалось на новый виджет, а затем вызовет это с предыдущим виджетом в качестве аргумента.

  • Переопределите этот метод, чтобы он отвечал при изменении виджета (например, для запуска неявной анимации).

  • Платформа всегда вызывает build после вызова didUpdateWidget, что означает, что любые вызовы setState в didUpdateWidget являются избыточными.

@mustCallSuper
@protected
void didUpdateWidget(covariant T oldWidget) { }
  1. setState () - всякий раз, когда вы меняете внутреннее состояние объекта State, вносите изменения в функцию, которую вы передаете setState:
  • Вызов setState уведомляет платформу о том, что внутреннее состояние этого объекта изменилось таким образом, что это может повлиять на пользовательский интерфейс в этом поддереве, что заставляет платформу планировать сборку для
    этого объекта State.

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

setState(() { _myState = newValue });
  1. deactivate () - Deactivate вызывается, когда State удаляется из дерева, но он может быть повторно вставлен до завершения изменения текущего кадра. Этот метод существует в основном потому, что объекты состояния можно перемещать из одной точки дерева в другую.
  • Платформа вызывает этот метод всякий раз, когда удаляет этот объект State из дерева. В некоторых случаях структура повторно вставляет объект State в другую часть дерева (например, если поддерево, содержащее этот объект State, перенесено из одного места в дереве в другое). Если это произойдет, фреймворк гарантирует, что вызовет build, чтобы дать объекту State возможность адаптироваться к его новому положению в дереве. Если фреймворк повторно вставляет это поддерево, он сделает это до конца кадра анимации, в котором поддерево было удалено из дерева. По этой причине объекты состояния могут отложить высвобождение большинства ресурсов до тех пор, пока платформа не вызовет их метод удаления.

Это редко используется.

@protected
@mustCallSuper
void deactivate() { }
  1. dispose () - вызывается, когда этот объект навсегда удаляется из дерева.
  • Платформа вызывает этот метод, когда этот объект State больше никогда не будет построен. После вызовов инфраструктуры dispose()объект State считается отключенным, а установленное свойство имеет значение false. На этом этапе вызывать setState является ошибкой. Эта стадия жизненного цикла является завершающей: нет возможности перемонтировать объект State, который был удален.

  • Подклассы должны переопределить этот метод, чтобы освободить любые ресурсы, удерживаемые этим объектом (например, остановить любую активную анимацию).

@protected
@mustCallSuper
void dispose() {
  assert(_debugLifecycleState == _StateLifecycle.ready);
  assert(() { _debugLifecycleState = _StateLifecycle.defunct; return true; }());
}

введите описание изображения здесь

Для получения дополнительной информации перейдите здесь здесь , здесь


26

Из документации на flutter.io :

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

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


14

Как упоминается в документации по флаттеру

В чем смысл?

Некоторые виджеты сохраняют состояние, а некоторые - без него. Если виджет изменяется - например, пользователь взаимодействует с ним - он сохраняет состояние. Состояние виджета состоит из значений, которые могут изменяться, например, текущего значения ползунка или того, установлен ли флажок. Состояние виджета хранится в объекте State, отделяя состояние виджета от его внешнего вида. Когда состояние виджета изменяется, объект состояния вызывает setState (), сообщая фреймворку перерисовать виджет.

Виджет без гражданства не имеет внутреннего состояния для управления. Icon, IconButton и Text являются примерами виджетов без сохранения состояния, которые являются подклассом StatelessWidget.

С сохранением состояния виджета является динамическим. Пользователь может взаимодействовать с виджетом с отслеживанием состояния (например, вводя текст в форму или перемещая ползунок), или он изменяется со временем (возможно, поток данных вызывает обновление пользовательского интерфейса). Checkbox, Radio, Slider, InkWell, Form и TextField являются примерами виджетов с отслеживанием состояния, которые являются подклассом StatefulWidget.

https://flutter.io/tutorials/interactive/#stateful-stateless


10

Состояние - это информация, которая (1) может быть прочитана синхронно при построении виджета, а (2) может измениться в течение срока службы виджета. Разработчик виджета несет ответственность за то, чтобы состояние было оперативно уведомлено при изменении такого состояния с помощью State.setState.

StatefulWidget :

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

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

Сами экземпляры StatefulWidget являются неизменяемыми и хранят свое изменяемое состояние либо в отдельных объектах State, которые создаются методом createState, либо в объектах, на которые это State подписывается, например объектах Stream или ChangeNotifier, ссылки на которые хранятся в конечных полях в StatefulWidget. сам.

StatelessWidget :

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

Виджет без сохранения состояния полезен, когда описываемая часть пользовательского интерфейса не зависит ни от чего, кроме информации о конфигурации в самом объекте и BuildContext, в котором виджет раздувается. Для композиций, которые могут изменяться динамически, например, из-за наличия внутреннего состояния, управляемого часами, или в зависимости от некоторого состояния системы, рассмотрите возможность использования StatefulWidget.


9

Виджеты без сохранения состояния - это статические виджеты. Вам просто нужно передать несколько свойств перед инициализацией виджетов без сохранения состояния. Они не зависят от изменений данных или поведения. Например. Text, Icon, RaisedButton - это виджеты без сохранения состояния.

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

Изменить 15/11/2018

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

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

У виджетов без состояния и виджетов с отслеживанием состояния разный жизненный цикл.


Даже после передачи новых данных извне в Statelessвиджет, мы можем изменить его во время выполнения, но он не называется Statefulвиджетом (в отличие от вашей последней строки).
CopsOnRoad

Не могли бы вы объяснить, как виджет без сохранения состояния может «обновляться при изменении внешних данных»? («Внешние данные - это данные, которые передаются через конструктор».) Будет ли конструктор вызываться не только один раз? Как меняются данные, прошедшие через конструктор?
user1596274

8

Я могу придумать очень простую аналогию. У вас есть мебель с книгами, украшениями и телевизором. Мебель без гражданства, она ничего не делает, не двигается. В телевизоре, с другой стороны, вы можете включать, выключать, переключать канал, воспроизводить фильм, если к нему подключен какой-либо DVD, и т. Д. У телевизора есть внутреннее состояние, которое влияет на его поведение. В мебели у вас нет гос. Наличие телевизора в мебели не добавляет ей состояния. Надеюсь это поможет.


Это не ответ на конкретный вопрос автора.
Исайя

1
Это отличная аналогия!
Уильям Террилл

6

Ответ на вопрос о переполнении стека - состояние или отсутствие состояния .

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

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

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


5

Что такое виджеты с отслеживанием состояния и без состояния?

TL; DR: виджет, который позволяет обновлять экран, является виджетом с отслеживанием состояния. Виджет, который не имеет состояния.

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

Я нашел это относительное содержание в интересном средстве массовой информации. Пожалуйста!


4

Без сохранения состояния: состояние виджета создается ТОЛЬКО ОДИН РАЗ, затем он может обновлять значения, но не состояние явно. Это также видно из структуры. Вот почему у него есть только один класс, который расширяется с помощью StatelessWidget. Итак, если я скажу, они никогда не смогут повторно запустить build()метод снова.

С отслеживанием состояния : виджеты могут обновлять свое СОСТОЯНИЕ (локально) и значения несколько раз при срабатывании события . По этой причине реализация тоже разная. В этом, у нас есть 2 класса, один StatefulWidgetи другой его состояние реализации обработчика т.е. State<YourWidget>. Итак, если я скажу, они могут повторно запустить build()метод снова и снова в зависимости от инициированных событий.

Схема ниже поможет.

введите описание изображения здесь


1

При написании приложения вы обычно создаете новые виджеты, которые являются подклассами StatelessWidget или StatefulWidget.

Вот некоторые различия между виджетами StatelessWidgetи StatefulWidgetвиджетами:

Виджет без сохранения состояния:

  1. Виджет с неизменяемым состоянием.
  2. Виджеты без сохранения состояния - это статические виджеты.
  3. Они не зависят от изменений данных или поведения.
  4. Виджеты без сохранения состояния не имеют состояния, они будут отображаться один раз и не будут обновляться, а будут обновляться только при изменении внешних данных.
  5. Например: Text, Icon, RaisedButtonявляются Апатриды виджетов.

Виджет без сохранения состояния:

  1. Виджет с изменяемым состоянием.
  2. Виджеты с отслеживанием состояния - это динамические виджеты.
  3. Их можно обновлять во время выполнения в зависимости от действий пользователя или изменения данных.
  4. Виджеты с отслеживанием состояния имеют внутреннее состояние и могут повторно визуализироваться при изменении входных данных или при изменении состояния виджета.
  5. Для примера: Checkbox, Radio Button, Sliderявляются Stateful виджеты

1

отказ от ответственности: - начал работать над флаттером с прошлой недели :)

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


1
Я думаю, что автор имел в виду обратное: · D
Roc Boronat

0

Простыми словами:

Как мы знаем, каждый виджет - это вид во флаттере. У которого есть свои классы. Когда мы используем эти классы, мы создаем из них объект. Мы даем значения их различным переменным / свойствам. Ex. Мы создаем текстовый виджет, поэтому можем присвоить ему строку, цвет, размер шрифта, семейство шрифтов. Таким образом, давая это, мы определяем его свойства при его создании. До этого момента виджеты без сохранения состояния или с отслеживанием состояния были одинаковыми, но,

Когда мы хотим изменить / обновить его свойства (скажем, String или Color) снова и снова, тогда это должен быть виджет Stateful.

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

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

Таким образом, Stateless - это меньше данных, а Stateful - это полные данные.

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

Пожалуйста, поправьте меня, если я ошибаюсь.


0

Что такое виджеты с отслеживанием состояния и без состояния?

Виджет без сохранения состояния: виджет без состояния создается только тогда, когда он имеет родительские изменения.

Виджеты с отслеживанием состояния: виджеты с полным состоянием содержат состояние виджета и могут быть перестроены при изменении состояния.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.