FFAppState
Разбор построен на сгенерированном коде демонстрационного приложения EcommerceFlow. Сам код лежит в репозитории на GitHub.
FFAppState — единая точка хранения глобального состояния приложения. Это синглтон: за всё время работы приложения существует ровно один его экземпляр. Класс наследует ChangeNotifier, поэтому виджеты могут подписываться на изменения и перестраиваться.
Внутри — методы инициализации и обновления сохранённого состояния, а также переменные с парами геттеров и сеттеров.
Каркас класса из демонстрационного магазина:
class FFAppState extends ChangeNotifier {
static FFAppState _instance = FFAppState._internal();
factory FFAppState() {
return _instance;
}
FFAppState._internal();
static void reset() {
_instance = FFAppState._internal();
}
void update(VoidCallback callback) {
callback();
notifyListeners();
}
// переменная состояния приложения с геттером и сеттером
bool _enableDarkMode = false;
bool get enableDarkMode => _enableDarkMode;
set enableDarkMode(bool value) {
_enableDarkMode = value;
}
}
_enableDarkMode здесь — переменная, заведённая разработчиком; геттер и сеттер к ней создаются сами.
Перестроение при обновлении
При обновлении переменной состояния из редактора действий выбирается режим перестроения: Rebuild All Pages, Rebuild Current Page или No Rebuild. Вот что получается в коде.
Rebuild Current Page
Вызывается сеттер, сразу за ним setState(() {}); — перестраивается только текущая страница.
Пример: обновление enableDarkMode в триггере onInitialization страницы ProductListPage.
SchedulerBinding.instance.addPostFrameCallback((_) async {
FFAppState().enableDarkMode = !(FFAppState().enableDarkMode ?? true);
setState(() {});
});
Rebuild All Pages
Вызывается сеттер, затем метод update(), который внутри дёргает notifyListeners() — перестраиваются все виджеты, зависящие от этой переменной.
SchedulerBinding.instance.addPostFrameCallback((_) async {
FFAppState().enableDarkMode = !(FFAppState().enableDarkMode ?? true);
FFAppState().update(() {});
});
В собственных действиях обязательно вызывайте update(), иначе изменения не дойдут до других страниц:
FFAppState().update(() => FFAppState().enableDarkMode = !(FFAppState().enableDarkMode ?? true));No Rebuild
Вызывается только сеттер: значение меняется, но ничего не перестраивается.
watch<FFAppState>
Когда через редактор действий добавляется Update App State, в метод build соответствующих страниц попадает строка:
@override
Widget build(BuildContext context) {
context.watch<FFAppState>();
...
context.watch<FFAppState>() подписывает виджет на изменения FFAppState: при любом изменении виджет перестраивается и показывает актуальные данные.
Списки в состоянии приложения
Для переменной типа List FlutterFlow генерирует набор вспомогательных методов: геттер, сеттер и операции добавления, удаления и правки элементов. Разберём на примере LatLngList.
late LoggableList<LatLng> _LatLngList =
LoggableList([LatLng(37.4071594, -122.0775312), LatLng(40.7358633, -73.9910835)]);
List<LatLng> get LatLngList => _LatLngList?..logger = () => debugLogAppState(this);
set LatLngList(List<LatLng> value) {
if (value != null) {
_LatLngList = LoggableList(value);
}
debugLogAppState(this);
}
void addToLatLngList(LatLng value) {
LatLngList.add(value);
}
void removeFromLatLngList(LatLng value) {
LatLngList.remove(value);
}
void removeAtIndexFromLatLngList(int index) {
LatLngList.removeAt(index);
}
void updateLatLngListAtIndex(
int index,
LatLng Function(LatLng) updateFn,
) {
LatLngList[index] = updateFn(_LatLngList[index]);
}
void insertAtIndexInLatLngList(int index, LatLng value) {
LatLngList.insert(index, value);
}
Что здесь что:
- список хранится в приватном поле
_LatLngListтипаLoggableList— обёртке, добавляющей журналирование; - геттер
LatLngListотдаёт список остальному приложению; - сеттер заменяет список целиком и записывает изменение через
debugLogAppState; addToLatLngListдобавляет элемент в конец;removeFromLatLngListудаляет конкретный элемент;removeAtIndexFromLatLngListудаляет элемент по позиции;updateLatLngListAtIndexменяет элемент по позиции, применяя к нему функциюupdateFn;insertAtIndexInLatLngListвставляет элемент на нужную позицию, сдвигая остальные.
Про работу с состоянием приложения в редакторе — на странице App State.