Храни состояние в общем предке и превращай дочерние виджеты в контролируемые виджеты, которые сообщают об изменениях через колбэки.
Открыть этот урок в KodokonКлассическая ситуация: выбор количества и итоговая сумма, показанная в другом месте, должны использовать одни и те же данные. Золотое правило: состояние живёт в ближайшем общем предке виджетов, которые от него зависят. Родитель владеет данными, передаёт их вниз параметром и получает изменения через колбэк, который поднимается обратно наверх. Это и есть «подъём состояния» (lifting state up).
import 'package:flutter/material.dart';
class CartScreen extends StatefulWidget {
const CartScreen({super.key});
@override
State<CartScreen> createState() => _CartScreenState();
}
class _CartScreenState extends State<CartScreen> {
int _quantity = 1;
void _updateQuantity(int value) {
setState(() => _quantity = value);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Cart')),
body: Column(
children: [
QuantitySelector(
quantity: _quantity,
onChanged: _updateQuantity,
),
Text('Total: ${_quantity * 20} €'),
],
),
);
}
}Дочерний виджет становится контролируемым виджетом: это StatelessWidget, он ничего не хранит, показывает полученное значение и сообщает о каждом намерении через onChanged. ValueChanged<int> - это просто псевдоним для void Function(int). Обрати внимание на кнопку «минус», отключённую при количестве 1: достаточно передать null в onPressed, и эта логика напрямую следует из полученного значения.
import 'package:flutter/material.dart';
class QuantitySelector extends StatelessWidget {
const QuantitySelector({
super.key,
required this.quantity,
required this.onChanged,
});
final int quantity;
final ValueChanged<int> onChanged;
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: const Icon(Icons.remove),
onPressed: quantity > 1
? () => onChanged(quantity - 1)
: null,
),
Text('$quantity'),
IconButton(
icon: const Icon(Icons.add),
onPressed: () => onChanged(quantity + 1),
),
],
);
}
}Ты пользуешься этим контрактом постоянно, сам того не замечая: Checkbox, Slider, Switch - все они контролируемые виджеты (value + onChanged). Когда дерево станет глубже и протаскивать колбэки через пять уровней надоест, придёт время познакомиться с решениями для управления состоянием - это тема следующего модуля.