Сгенерированный код страницы

Для каждой страницы FlutterFlow создаёт два файла: класс Widget и класс Model. Для страницы ProductListPage это ProductListPageWidget и ProductListPageModel.

инфо
Про пример

Разбор построен на сгенерированном коде демонстрационного приложения EcommerceFlow. Сам код лежит в репозитории на GitHub.

Класс PageModel

PageModel хранит состояние страницы и поднимает модели компонентов, которые на ней используются. Все такие классы наследуют FlutterFlowModel — общий каркас для всех моделей.

Схема генерации классов Model и Widget при создании страницы

совет
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;
совет
Приватные переменные в Dart

Переменные, имя которых начинается с подчёркивания (_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

safeSetState — надстройка над setState из Flutter: она вызывает его, только если виджет ещё в дереве. Это спасает от ошибок во время работы приложения.

перевод официальной документации FlutterFlow

обновлено

ESC