Сгенерированный код страницы
Для каждой страницы FlutterFlow создаёт два файла: класс Widget и класс Model. Для страницы ProductListPage это ProductListPageWidget и ProductListPageModel.
Разбор построен на сгенерированном коде демонстрационного приложения EcommerceFlow. Сам код лежит в репозитории на GitHub.
Класс PageModel
PageModel хранит состояние страницы и поднимает модели компонентов, которые на ней используются. Все такие классы наследуют FlutterFlowModel — общий каркас для всех моделей.

Про вспомогательные классы и методы, которые FlutterFlow генерирует для всех страниц и компонентов, — на странице FlutterFlowModel.
Локальное состояние
В PageModel лежат поля, соответствующие переменным состояния страницы.
Скажем, на ProductListPage завели переменную searchString. В файле product_list_page_model.dart ей соответствует приватное поле _searchString с геттером и сеттером, который заодно пишет изменения в журнал.
String? _searchString;
set searchString(String? value) {
_searchString = value;
debugLogWidgetClass(rootModel);
}
String? get searchString => _searchString;
Переменные, имя которых начинается с подчёркивания (_searchString), доступны только внутри своего класса.
Там же хранится состояние виджетов страницы. Например, _dropDownValue — текущее значение выпадающего списка:
String? _dropDownValue;
set dropDownValue(String? value) {
_dropDownValue = value;
debugLogWidgetClass(rootModel);
}
String? get dropDownValue => _dropDownValue;
Модели дочерних компонентов
PageModel поднимает модели компонентов, которые есть на странице. Если на ней стоит компонент CartCounter, его модель создаётся здесь же:
// модель компонента CartCounter
late CartCounterModel cartCounterModel;
@override
void initState(BuildContext context) {
cartCounterModel = createModel(context, () => CartCounterModel()..parentModel = this);
}
Внутри модели страницы поднимаются только модели дочерних компонентов. Сама модель страницы создаётся в классе состояния виджета — см. класс PageWidget.
Для динамических списков компонентов — в ListView, Row или Column — заводится Map<String, FlutterFlowModel>, по одной модели на экземпляр:
// модели динамического компонента CategoryAvatar
Map<String, FlutterFlowModel> categoryAvatarModels = {};
dispose()
dispose освобождает то, что больше не нужно, — без этого утекает память. Здесь освобождаются cartCounterModel, searchQueryFocusNode и searchQueryTextController:
@override
void dispose() {
cartCounterModel.dispose();
searchQueryFocusNode?.dispose();
searchQueryTextController?.dispose();
}
Класс PageWidget
PageWidget строит интерфейс страницы и хранит дерево виджетов, собранное на холсте. Он всегда наследует StatefulWidget, то есть обновляется через setState и живёт по обычному жизненному циклу Flutter.
class ProductListPageWidget extends StatefulWidget {
const ProductListPageWidget({super.key});
@override
State<ProductListPageWidget> createState() => _ProductListPageWidgetState();
}
Создание модели
Модель страницы создаётся в классе состояния. Она отвечает за состояние страницы, логику и работу с данными.
class _ProductListPageWidgetState extends State<ProductListPageWidget> {
late ProductListPageModel _model;
@override
void initState() {
super.initState();
_model = createModel(context, () => ProductDetailPageModel());
}
Освобождение модели
dispose модели вызывается из переопределённого dispose класса состояния виджета — так слушатели и контроллеры освобождаются, когда виджет уходит из дерева.
@override
void dispose() {
_model.dispose();
super.dispose();
}
Ключ Scaffold
У каждой страницы есть GlobalKey для Scaffold — через него из кода открывают и закрывают боковые панели и показывают уведомления.
final scaffoldKey = GlobalKey<ScaffoldState>();
return Scaffold(
key: scaffoldKey,
...)
Скрытие клавиатуры
Корневой виджет каждой страницы — GestureDetector, снимающий фокус с поля ввода по нажатию. Благодаря этому клавиатура закрывается при нажатии в любом месте вне поля.
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
FocusManager.instance.primaryFocus?.unfocus();
},
...)
Обе эти вещи FlutterFlow добавляет сам.
Действия onPageLoad
Действия из триггера onPageLoad попадают в addPostFrameCallback внутри initState. Так они выполняются после того, как виджет полностью построен и отрисован, — иначе интерфейс пришлось бы обновлять до того, как он готов.
@override
void initState() {
super.initState();
_model = createModel(context, () => ProductListPageModel());
// действия при загрузке страницы
SchedulerBinding.instance.addPostFrameCallback((_) async {
_model.searchString = null;
safeSetState(() {});
... // остальные действия
});
}
safeSetState — надстройка над setState из Flutter: она вызывает его, только если виджет ещё в дереве. Это спасает от ошибок во время работы приложения.