Поднятие состояния и передача данных
Когда несколько виджетов делят одни данные, состояние «поднимают» к их общему предку.
Суть: данные хранятся в общем родителе и передаются вниз через параметры, а изменения «всплывают» вверх через колбэки-функции. Это базовый паттерн без сторонних библиотек.
Паттерн «данные вниз, события вверх» — это не прихоть Flutter, а проверенный принцип однонаправленного потока данных, лежащий в основе многих современных фреймворков. Его сила в предсказуемости: чтобы понять, почему на экране именно эти данные, достаточно посмотреть на одно место — источник правды в родителе. Не нужно гадать, какой из десяти виджетов втихую поменял значение, ведь менять его имеет право только владелец.
Представьте: кнопка «+» в одном виджете, а число-счётчик в другом. Если состояние спрятано внутри кнопки, счётчик о нём не узнает. Решение — lifting state up: вынести общие данные в ближайшего общего родителя. Родитель хранит состояние, передаёт значение вниз дочерним виджетам и даёт им колбэк, чтобы сообщать о действиях.
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int count = 0; // состояние живёт у общего предка
@override
Widget build(BuildContext context) {
return Column(children: [
CounterDisplay(count: count), // данные вниз
AddButton(onPressed: () => setState(() => count++)), // событие вверх
]);
}
}
class CounterDisplay extends StatelessWidget {
final int count;
const CounterDisplay({super.key, required this.count});
@override
Widget build(BuildContext context) => Text('Счёт: $count');
}
class AddButton extends StatelessWidget {
final VoidCallback onPressed;
const AddButton({super.key, required this.onPressed});
@override
Widget build(BuildContext context) =>
ElevatedButton(onPressed: onPressed, child: const Text('+'));
}
Как работает поток данных под капотом
Во Flutter данные текут вниз (от родителя к детям через параметры), а события — вверх (от детей к родителю через колбэки). Дочерний виджет не меняет состояние сам; он лишь вызывает переданную функцию onPressed, а родитель решает, что делать, и вызывает setState. Это делает поток предсказуемым: единственный источник правды — родитель.
CounterPage (хранит count = 0)
| данные вниз ^ события вверх
| (count: 0) | (onPressed колбэк)
v |
CounterDisplay AddButton
показывает 0 нажатие -> вызывает onPressed()
-> родитель: setState count++
-> новые данные текут вниз снова
# Модель lifting state up: родитель — источник правды
class CounterPage:
def __init__(self):
self.count = 0
def display(self): # данные вниз
print(f'Счёт: {self.count}')
def on_add_pressed(self): # событие вверх -> меняет родитель
self.count += 1
self.display() # перерисовка после изменения
page = CounterPage()
page.display() # Счёт: 0
page.on_add_pressed() # ребёнок "нажал" -> родитель обновил -> Счёт: 1
page.on_add_pressed() # Счёт: 2
Частые ошибки
- Дублировать состояние в нескольких виджетах — они рассинхронизируются. Источник правды должен быть один.
- Менять данные прямо в дочернем виджете вместо вызова колбэка — нарушает однонаправленный поток.
- Поднимать состояние слишком высоко — перестраивается лишний поддеревья. Поднимайте до ближайшего общего предка, не выше.
Best practices
- Храните общие данные у ближайшего общего родителя — не выше, чем нужно.
- Передавайте данные вниз параметрами, события — вверх колбэками (
VoidCallback,ValueChanged). - Когда дерево становится глубоким и колбэков много — пора смотреть на стейт-менеджеры (следующий урок).
За колбэками во Flutter стоят удобные типы: VoidCallback для действий без аргументов, ValueChanged<T> для передачи изменённого значения наверх (например, новый текст из поля ввода). Используя эти стандартные типы вместо самодельных, вы делаете интерфейс своих виджетов узнаваемым: любой Flutter-разработчик с ходу поймёт, что onChanged: ValueChanged<String> сообщает родителю новое строковое значение.
Итог: поднятие состояния — фундаментальный паттерн Flutter: данные вниз, события вверх, один источник правды. Он отлично работает для небольших экранов, но в крупных приложениях колбэки множатся — тогда на сцену выходят инструменты управления состоянием.