Quick Tips: initState and didChangeDependencies methods in Flutter Explained!
So, a lot of new to Flutter developers are facing problems understanding initState() and didChangeDependencies() method, Though Flutter has got it covered in official docs but newbie developers are finding it hard to understand them.
initState()
According to Flutter official docs,Called when this object is inserted into the tree.
The framework will call this method exactly once for each State object it creates.
Override this method to perform initialization that depends on the location at which this object was inserted into the tree (i.e., context) or on the widget used to configure this object (i.e., widget).
Whenever we start making a widget i.e a class and start building the layers or tree of widgets using Build Context(), we can initialize any variable, call any function or method before building the widget tree i.e before building the widget anything inside initState() function will be called or initialized first and the widgets are built later. Let me explain using a simple example:
As you can see in the code above, before building the widget tree initState() is initialized and called where print statement is used to print ‘open’, you can use initState() to set values to any variable,list or even use it to call any function.
didChangeDependencies()
According to Flutter official docs,Called when a dependency of this State object changes.
For example, if the previous call to build referenced an InheritedWidget that later changed, the framework would call this method to notify this object about the change.
This method is also called immediately after initState. It is safe to call BuildContext.dependOnInheritedWidgetOfExactType from this method.
Subclasses rarely override this method because the framework always calls build after a dependency changes. Some subclasses do override this method because they need to do some expensive work (e.g., network fetches) when their dependencies change, and that work would be too expensive to do for every build.
Called when a dependency of this State object changes.
For example, if the previous call to build referenced an InheritedWidget that later changed, the framework would call this method to notify this object about the change.
This method is also called immediately after initState. It is safe to call BuildContext.dependOnInheritedWidgetOfExactType from this method.
its called before build after an InheritedWidget notifies you that it updated
its useful because it gives you an opportunity to compare and set state outside of build.
Lets take an example:
Theme.of(context) for example is glue for dependOnInheritedWidgetOfExactType,when that InheritedWidget updates e.g. from the theme changing, it will rebuild your widget.
Why is it useful?
its useful because it gives you an opportunity to compare and set state outside of build.
#a possible condition for didChangeDependencies in the above snippet to be called here would be that your GreatPlaces provider updated.
Here’s a book you can use to master Flutter:
Что такое initState и super.initState во флаттере?
В документации это написано, но я не могу понять.
Вызывается, когда этот объект вставляется в дерево.
Платформа вызовет этот метод ровно один раз для каждого создаваемого им объекта State.
Переопределите этот метод, чтобы выполнить инициализацию, которая зависит от местоположения, в котором этот объект был вставлен в дерево (т. Е. Контекст) или от виджета, используемого для настройки этого объекта (т. Е. Виджет).
Если метод сборки состояния зависит от объекта, который может сам изменять состояние, например ChangeNotifier или Stream, или какого-либо другого объекта, на который можно подписаться для получения уведомлений, то состояние должно подписаться на этот объект во время initState, отказаться от подписки на старый object и подписаться на новый объект при его изменении в didUpdateWidget, а затем отказаться от подписки на объект в удалении.
Вы не можете использовать BuildContext.inheritFromWidgetOfExactType из этого метода. Однако didChangeDependencies будет вызываться сразу после этого метода, и там можно использовать BuildContext.inheritFromWidgetOfExactType.
Если вы переопределите это, убедитесь, что ваш метод начинается с вызова super.initState ().
Но я не уверен в его значении. Вы можете это объяснить?
2 ответа
Обычно мы переопределяем этот метод, если нам нужно выполнить какую-то работу по инициализации, например, зарегистрировать слушателя, потому что в отличие от build() этот метод вызывается один раз.
Подкласс State может переопределить initState для выполнения работы, которая должна выполняться только один раз. Например, переопределите initState, чтобы настроить анимацию или подписаться на службы платформы. Реализации initState необходимо запускать с вызова super.initState.
Когда объект состояния больше не нужен, платформа вызывает dispose () для объекта состояния. Переопределите функцию удаления, чтобы выполнить очистку. Например, переопределите команду отмены таймеров или отказа от подписки на службы платформы. Реализации dispose обычно заканчиваются вызовом super.dispose
Использование initState ()
Пример.
Управление состоянием в приложениях на Flutter
Общие принципы
Flutter — реактивный фреймворк, и для разработчика, специализирующегося на нативной разработке, его философия может быть непривычна. Поэтому начнём с небольшого обзора.
Пользовательский интерфейс на Flutter, как и в большинстве современных фреймворков, состоит из дерева компонентов (виджетов). При изменении какого-либо компонента, происходит перерендеринг этого и всех его дочерних компонентов (с внутренними оптимизациями, о которых ниже). При глобальном изменении отображения (например, повороте экрана), перерисовывается всё дерево виджетов.
Этот подход может показаться неэффективным, но на самом деле он передаёт программисту контроль за скоростью работы. Если производить обновление интерфейса на самом верхнем уровне без необходимости — всё будет работать медленно, но при правильной компоновке виджетов, приложения на Flutter могут быть очень быстрыми.
Во Flutter существует два типа виджетов — Stateless и Stateful. Первые (аналог Pure Components в React) не имеют состояния и полностью описываются своими параметрами. Если не меняются условия отображения (скажем, размер области, в которой должен показываться виджет) и его параметры, система переиспользует ранее созданное визуальное представление виджета, поэтому использование Stateless виджетов хорошо сказывается на производительности. При этом всё равно при каждой перерисовке виджета формально создаётся новый объект и запускается конструктор.
Stateful виджеты сохраняют некоторое состояние между рендерингами. Для этого они описываются двумя классами. Первый из классов, собственно виджет, описывает объекты, которые создаются при каждой отрисовке. Второй класс, описывает состояние виджета и его объекты передаются в создаваемые объекты виджета. Изменение состояния Stateful виджетов является основным источником перерисовки интерфейсов. Для этого нужно изменить его свойства внутри вызова метода SetState. Таким образом, в отличие от многих других фреймворков, во Flutter нет неявного отслеживания состояния — любое изменение свойств виджета вне метода SetState не приводит к перерисовке интерфейса.
Теперь, после описания основ, можно начать с простого приложения, использующего Stateless и Stateful виджеты:
Если нужны более живучие состояния
Идём дальше. Состояние Stateful виджетов сохраняется между перерисовками интерфейсов, но только до тех пор, пока виджет нужен, т.е. реально находится на экране. Проведём простой эксперимент — разместим наш список на вкладке:
При запуске можно увидеть, что при переключении между вкладками, состояние удаляется (вызывается метод dispose()), при возврате создаётся снова (метод initState()). Это разумно, так как хранение состояния неотображаемых виджетов будет отнимать ресурсы системы. В том случае, когда состояние виджета должно переживать его полное скрытие, возможны несколько подходов:
Во-первых, можно использовать отдельные объекты (ViewModel) для хранения состояния. Dart на уровне языка поддерживает фабричные конструкторы, которые можно использовать для создания фабрик и синглтонов, хранящих необходимые данные.
Мне больше нравится этот подход, т.к. он позволяет изолировать бизнес-логику от пользовательского интерфейса. Это особенно актуально в связи с тем, что Flutter Release Preview 2 добавил возможность создавать pixel-perfect интерфейсы для iOS, но делать это нужно, разумеется, на соответствующих виджетах.
Во-вторых, можно использовать знакомый программистам React подход поднятия состояния, когда данные хранятся в компонентах, расположенных выше по дереву. Поскольку Flutter перерисовывает интерфейс только при вызове метода setState(), эти данные можно менять и использовать без рендеринга. Такой подход несколько более сложен и повышает связность виджетов в структуре, но позволяет точечно задавать уровень хранения данных.
Наконец существуют библиотеки хранения состояния, например flutter_redux.
Для простоты используем первый подход. Сделаем отдельный класс ListData, синглтон, хранящий значения для нашего списка. При отображении будем использовать этот класс.
Сохранение позиции скролла
Если скрутить список из предыдущего примера вниз, потом перейти между вкладками, нетрудно заметить, что позиция прокрутки не сохраняется. Это логично, так как в нашем классе ListData она не хранится, а собственное состояние виджета не переживает переключение между табами. Реализуем хранение состояния прокрутки вручную, но для интереса сложим её не в отдельный класс и не в ListData, а в состояние более высокого уровня, чтобы показать, как с этим работать.
Обратите внимание на виджеты ScrollController и NotificationListener (а также ранее использованный DefaultTabController). Концепция виджетов, не имеющих своего отображения должна быть знакома разработчикам, работающим с React/Redux — в этой связке активно используются компоненты-контейнеры. Во Flutter виджеты без отображения обычно используются для добавления функциональности к дочерним виджетам. Это позволяет оставить сами визуальные виджеты легковесными и не обрабатывать системные события там, где они не нужны.
Переживаем выключение приложения
Сохранение информации на время работы приложения — это хорошо, но часто хочется сохранять её и между сеансами, особенно учитывая привычку операционных систем закрывать фоновые приложения при нехватке памяти. Основные варианты постоянного хранения данных во Flutter это:
Для демонстрации сделаем сохранение состояния прокрутки в Shared preferences. Для этого добавим восстановление позиции скролла при инициализации состояния _MyHomePageState и сохранение при прокрутке.
Здесь нужно немного остановиться на асинхронной модели Flutter/Dart, поскольку все внешние службы работают на асинхронных вызовах. Принцип работы этой модели сходен с node.js — есть один основной поток выполнения (thread), который прерывается на асинхронные вызовы. На каждом следующем прерывании (а UI делает их постоянно) обрабатываются результаты завершённых асинхронных операций.При этом есть возможность запускать тяжеловесные вычисления в фоновых threads (через функцию compute).
Итак, запись и чтение в SharedPreferences делаются асинхронно (хотя библиотека позволяет синхронное чтение из кэша). Для начала разберёмся с чтением. Стандартный подход к асинхронному получению данных выглядит так — запустить асинхронный процесс, по его завершению выполнить SetState, записав полученные значения. В результате пользовательский интерфейс будет обновлён с использованием полученных данных. Однако в данном случае мы работаем не с данными, а с положением прокрутки. Нам не нужно обновлять интерфейс, нужно только вызвать метод jumpTo у ScrollController. Проблема в том, что результат обработки асинхронного запроса может вернуться в любой момент и совсем не обязательно будет что и куда прокручивать. Чтобы гарантированно выполнить операцию на полностью инициализированном интерфейсе, нам нужно … всё-таки выполнить прокрутку внутри setState.
Получаем примерно такой код:
С записью всё интереснее. Дело в том, что в процессе прокрутки, сообщающие об этом события приходят постоянно. Запуск асинхронной записи при каждом изменении значения может привести к ошибкам приложения. Нам нужно обрабатывать только последнее событие из цепочки. В терминах реактивного программирования это называется debounce и его мы и будем использовать. Dart поддерживает основные возможности реактивного программирования через потоки данных (stream), соответственно нам нужно будет создать поток из обновлений позиции прокрутки и подписаться на него, преобразуя его с помощью Debounce. Для преобразования нам потребуется библиотека stream_transform. В качестве альтернативного подхода, можно использовать RxDart и работать в терминах ReactiveX.
Init state flutter что это
Класс StatefulWidget предназначен для создания виджетов, которые хранят состояние. При этом несмотря на то, что объекты класса StatefulWidget являются неизменяемыми (immutable), их состояние является изменяемым (mutable).
Например, определим простейший виджет StatefulWidget:
Итак,здесь определен класс Counter, который наследуется от StatefulWidget.
Затем виджет Counter встравивается в приложение в элемент Scaffold:
В итоге при запуске приложения мы увидим текст со значением переменной value
Но ключевая идея StatefulWidget состоит в том, что мы можем менять его состояние. Поэтому добавим изменение переменной value по нажатию на кнопку:
В итоге по нажатию на кнопку увеличится значение переменной value:
Передача данных в State
При необходимости в объект State можно передавать данные извне. В этом случае передача осуществляется через StatefulWidget. Например, изменим выше приведенный пример таким образом, чтобы State принимал начальные данные извне:
При создании объекта State виджет Counter передает в конструктор соответствующие данные:
При этом эти значения виджет Counter сам принимает извне с помощью своего конструктора:
В итоге при применении виджета мы можем передавать в него различные данные:
Получение виджета
С помощью свойства widget внутри State можно обращаться к виджету StatefulWidget, к которому привязан объект State:
Вынос логики изменения состояния в метод
Если метод, в который вынесены все действия, ничего не возвращает и не принимает никаких параметров, то есть соответствует определению onPressed (как в данном случае), то можно напрямую присвоить функцию параметру onPressed:
Управление состоянием приложения в Flutter
Привет, Хабр! Представляю перевод статьи, Let me help you to understand and choose a state management solution for your app, которая попалась мне и заинтересовала в процессе изучения азов управления состоянием во Flutter. Буду рад услышать любую критику касательно данного перевода. В обратных кавычках («) будут написаны мои личные мысли и пояснения.
Управление состоянием во Flutter — горячая тема. Возможных вариантов решения задачи много и запутаться в них, выбирая наиболее подходящий под ваши потребности — крайне просто. Я сам путался, но нашел подходящее решение. Позвольте поделиться им с вами.
Чтобы найти решение, подходящее к вашим потребностям, необходимо определить сами потребности. В моем случае это:
Разница между локальным и глобальным состоянием
Перед тем, как погрузиться в анализ отобранных решений, необходимо понять разницу между локальным и глобальным состоянием. Для этого подойдет практический пример:
Представим форму авторизации, где пользователю предлагается ввести логин и пароль и получить объект «личности пользователя» после отправки формы. В этом примере любая проверка данных, вводимых в поля формы — будет являться частью локального состояния `виджета формы авторизации`, и остальная часть приложения не должна знать об этом. А возвращаемый `сервером авторизации` объект «личности» — частью глобального состояния. Так, как от этого объекта зависят другие компоненты, меняющие поведение в зависимости от того, авторизован ли пользователь.
Если вы не хотите ждать, или не заинтересованы в моих исследованиях, то вот краткий обзор полученных результатов:
Моя рекомендация — использовать BLoC для управления локальным состоянием и Redux для глобального состояния, особенно, если вы создаете сложное приложение, которое будет расти со временем.
Почему не стоит использовать setState()
Использование setState() внутри ваших виджетов отлично подходит для быстрого создания прототипов, и получения обратной связи на эти вносимые изменения, но данный путь не помогает нам достичь поставленных целей, потому логика отображения смешивается с бизнес логикой, чем нарушается принцип чистоты и качества кода. Сопровождение подобного кода будет сложным в будущем, поэтому кроме как для создания прототипов, данный подход не рекомендуется.
ScopedModel — шаг в верном направлении
В наших виджетах мы сможем реагировать на изменения в модели с помощью `предоставляемого данной библиотекой` виджета ScopedModelDescendant :
Мощное решение — BLoC
Данный паттерн был придуман в Google и там же его используют. Он поможет нам достичь следующих целей:
Я и сам очень рекомендую использовать данный подход для управления локальным состоянием, однако он подойдет даже и для управления глобальным состоянием. Однако в последнем случае вы столкнетесь с проблемой — где и как правильно внедрять BLoC, чтобы к нему имели доступ разные компоненты, и тут на сцену выходит Redux.
Redux и BLoC — идеальный микс для меня
Одной из целей, которые я описывал в начале статьи был поиск чего-то, широко используемого и предсказуемого и это Redux — паттерн и набор инструментов, которые вместе помогают нам управлять глобальным состоянием. Он имеет три базовых принципа в основе:
Единственный источник истины — все состояние ` state ` вашего приложения хранится в древовидном объекте в единственном хранилище ` store `
Данный подход к управлению состоянием широко принят web-разработчиками, и его появление на мобильных устройствах поможет получить преимущества web и mobile-application разработчикам.
Brian Egan разрабатывает как оригинальный Redux, так и flutter_redux, а также он сделал потрясающее Todo приложение, в котором он применил множество архитектурных паттернов, включая Redux.
Учитывая все качества Redux, я очень сильно советую использовать его для управления глобальным состоянием, но вы должны быть уверены, что не используете его для управления локальным состоянием, если хотите масштабировать свое приложение.










