Примеры собственного кода
Собственный код видит сгенерированные классы проекта и может менять глобальные значения — состояние приложения, тему. Ниже собраны типовые случаи.
Собственные функции не умеют импортировать ничего сверх стандартного набора. Поэтому всё, что ниже требует нового импорта, работает только в собственных виджетах и действиях.
Вот что доступно собственной функции — этим набором и приходится обходиться:
import 'dart:convert';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'lat_lng.dart';
import 'place.dart';
import 'uploaded_file.dart';
import '/backend/backend.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '/backend/schema/structs/index.dart';
import '/backend/schema/enums/enums.dart';
import '/auth/firebase_auth/auth_util.dart';Сгенерированные классы FlutterFlow
Пока вы собираете приложение, FlutterFlow генерирует полноценный проект на Flutter. В нём есть свои классы, закрывающие типовые задачи:
- Кнопки —
FFButtonс готовым оформлением и поведением. - Google Places —
FFPlaceс названием, адресом и координатами места. - Загруженные файлы —
FFUploadedFileс именем файла, байтами и ссылкой.
Класс — чертёж объекта: он описывает свойства (данные) и методы (действия над ними).
Например:
- у класса
Carмогут быть свойстваcolorиspeedи методыdrive()иstop(); - у класса
FFPlace— свойстваaddressиlatLngи методы для работы с ними.
Такие классы почти всегда начинаются с FF или FlutterFlow. Наберите эти буквы в редакторе — подсказка покажет доступные.

Компонент внутри собственного виджета
Так стоит делать, если компонент фиксирован. Если содержимое зависит от выбора пользователя, передавайте его параметром.
В собственном виджете можно вернуть готовый компонент FlutterFlow — не переписывая его содержимое кодом. Скажем, вы делаете диалог на пакете с pub.dev: его содержимым может стать компонент, собранный на холсте.
Импорт класса компонента FlutterFlow добавляет сам.

Тема оформления в своём виджете
Цвета в собственном виджете лучше брать из темы, а не прописывать значениями: тогда виджет не выпадет из общего оформления и переживёт смену палитры.
Доступ к теме даёт FlutterFlowTheme.of(context): оттуда берутся и цвета (primary, primaryBackground, ваши собственные), и текстовые стили (bodyLarge, bodyMedium).
Для доступа к FlutterFlowTheme нужен импорт import '../flutter_flow/flutter_flow_theme.dart';.
class CustomButton extends StatefulWidget {
final String label;
CustomButton({required this.label});
@override
_CustomButtonState createState() => _CustomButtonState();
}
class _CustomButtonState extends State<CustomButton> {
bool isPressed = false;
void toggleButton() {
setState(() {
isPressed = !isPressed;
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: isPressed
? FlutterFlowTheme.of(context).primary // цвет при нажатии
: FlutterFlowTheme.of(context).secondaryBackground, // обычный цвет
foregroundColor: FlutterFlowTheme.of(context).secondaryText, // цвет текста
),
onPressed: toggleButton,
child: Text(
widget.label,
style: FlutterFlowTheme.of(context).bodyText1, // стиль текста из темы
),
);
}
}
Работа с App State из кода
Состояние приложения обычно читают и меняют действиями, но иногда это нужно прямо в коде — когда важен порядок операций. Класс FFAppState даёт и доступ к значениям, и вспомогательные методы.
Для доступа к FFAppState нужен импорт import '../../flutter_flow/flutter_flow_util.dart';.
- Чтение значения
Future getCartItems() async {
// Читаем текущее содержимое корзины из AppState
final currentCartItems = FFAppState().cartItems;
print('Current Cart Items: $currentCartItems');
}
- Запись значения
Future enableDarkMode() async {
// Включаем тёмную тему
FFAppState().update(() {
FFAppState().enableDarkMode = true;
});
print('Dark mode enabled');
}
- Работа со списками
У FFAppState есть готовые методы для списков — подробнее о сгенерированном классе см. здесь.
Future addLocation(LatLng value) async {
// Добавить точку в список LatLng
FFAppState().addToLatLngList(value);
}
Future removeLocation(LatLng value) async {
// Удалить конкретную точку из списка
FFAppState().removeFromLatLngList(value);
}
Future removeLocationAtIndex(int index) async {
// Удалить точку по индексу
FFAppState().removeAtIndexFromLatLngList(index);
}
Future updateLocationAtIndex(int index, LatLng Function(LatLng) updateFn) async {
// Изменить точку по индексу
FFAppState().updateLatLngListAtIndex(index, updateFn);
}
Future insertLocationAtIndex(int index, LatLng value) async {
// Вставить точку по индексу
FFAppState().insertAtIndexInLatLngList(index, value);
}
Свои типы данных
Для каждого собственного типа данных FlutterFlow генерирует класс <Имя>Struct. В коде такие объекты создают, читают, меняют и возвращают обратно в действие. Разберём на примере ProductStruct.
Пример 1: создание объекта
// Новый экземпляр ProductStruct
final newProduct = ProductStruct(
productId: '123',
name: 'Example Product',
description: 'A sample product description.',
category: 'Electronics',
subCategory: 'Mobile Phones',
price: PriceStruct(amount: 299.99, currency: 'USD'),
sizes: ['Small', 'Medium', 'Large'],
colors: [ColorsStruct(colorName: 'Red', colorHex: '#FF0000')],
images: [ImagesStruct(thumbnail: 'https://example.com/image.jpg')],
stockStatus: StockStatusStruct(xs: 0, small: 2),
reviews: [ReviewsStruct(rating: 4, comment: 'Great product!')],
);
Пример 2: чтение полей
Из готового объекта можно достать значение и вернуть его в вызывающее действие.
- Одно поле
Тип возврата String?, потому что значение может отсутствовать.
// Вернуть название товара
String? getProductName(ProductStruct product) {
return product.name;
}
- Проверка на наличие значения
// Есть ли у товара описание
bool hasDescription(ProductStruct product) {
return product.description != null;
}
- Список из вложенных объектов
// Собрать тексты всех отзывов
List<String> getProductReviewComments(ProductStruct product) {
return product.reviews?.map((review) => review.comment ?? '').toList() ?? [];
}
Пример 3: изменение полей
Объект можно править перед записью в базу или передачей в действие.
- Простое поле
// Обновить название товара
Future updateProductName(ProductStruct product, String newProductName) {
product.productName = newProductName;
}
- Вложенный объект
Здесь меняется цена, которая сама является объектом PriceStruct.
// Обновить цену товара
Future updateProductPrice(ProductStruct product, double newAmount, String currency) {
if (product.price != null) {
// Меняем только сумму
product.price!.amount = newAmount;
} else {
// Если цены нет — создаём объект целиком
product.price = PriceStruct(
amount: newAmount,
currency: currency,
);
}
}
- Список вложенных объектов
Future addNewReviews(ProductStruct product) {
product.reviews ??= []; // создаём список, если его ещё нет
product.reviews!.addAll([
ReviewStruct(rating: 5, comment: 'Excellent product!'),
ReviewStruct(rating: 4, comment: 'Good quality, but a bit expensive.'),
ReviewStruct(rating: 3, comment: 'Satisfactory, meets expectations.'),
]);
}
Или если список отзывов приходит в действие параметром:
Future addDynamicReviews(ProductStruct product, List<ReviewStruct> newReviews) {
product.reviews ??= []; // создаём список, если его ещё нет
product.reviews!.addAll(newReviews);
}
Данные авторизации Firebase
При работе с Firebase Authentication в коде доступны сведения о текущем пользователе.
Проверка подтверждённой почты:
if (currentUserEmailVerified) {
// Действие для подтверждённых пользователей
}
Путь к папке пользователя:
String directoryPath = '/users/' + currentUserUid + '/files';
Что ещё доступно:
currentUserEmail— адрес почты;currentUserUid— уникальный идентификатор;currentUserDisplayName— отображаемое имя;currentUserPhoto— ссылка на аватар;currentPhoneNumber— телефон, если он есть;currentJwtToken— JWT-токен для защищённых запросов;currentUserEmailVerified— подтверждена ли почта.
Значения окружения разработки
Если в проекте используются окружения разработки, FlutterFlow генерирует класс FFDevEnvironmentValues — по тому окружению, которое выбрано на момент генерации кода.
Future getWebhookId() async {
// Add your function code here!
return FFDevEnvironmentValues().webhookId;
}
Библиотеки в собственном коде
Из проекта, подключившего библиотеку, доступны её состояние, значения и виджеты.
Значения библиотеки
Для библиотек генерируется класс FFLibraryValues — по аналогии с FFAppState и FFDevEnvironmentValues. Через него читаются Library Values.
Future getSchema(StateStruct? syncStatus) async {
print(FFLibraryValues().schema);
}
Собственный код библиотеки
При подключении библиотеки FlutterFlow сам добавляет нужные импорты, и её код становится доступен вашему.
Для библиотеки с идентификатором проекта library_hybw3o добавится такой импорт:
import 'package:library_hybw3o/flutter_flow/custom_functions.dart' as library_hybw3o_functions;
После этого её функции вызываются из ваших:
int getRandomIndex(List<int> indexList) {
final item = library_hybw3o_functions.getRandomItem(); // функция из библиотеки
// получаем случайный индекс
final randomNumber = math.Random();
return ...
}
Импорт вручную
Если импорт не появился сам, добавьте его сами и задайте псевдоним.
Пример: подключаем собственные действия библиотеки к своему виджету.
// Свой импорт
import 'package:library_hybw3o/custom_code/actions/index.dart' as library_hybw3o_actions; // задаём псевдоним
// Код виджета
class CustomDialog extends StatefulWidget {
const CustomDialog({
super.key,
this.width,
this.height,
});
final double? width;
final double? height;
@override
State<CustomDialog> createState() => _CustomDialogState();
}
class _CustomDialogState extends State<CustomDialog> {
@override
void initState() {
library_hybw3o_actions.getSchema(StateStruct()); // вызываем действие библиотеки
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(height: 50, width: 50);
}
}