Irbisa · Шпаргалка
Базовое управление состоянием
setState, InheritedWidget, ValueNotifier
Зачем вообще нужен подход к управлению состоянием, если есть
setState?Какие подходы к управлению состоянием во Flutter вы знаете и как выбираете подход для проекта?
Как работает
setState(), что происходит при его вызове и почему его нельзя вызывать внутриbuild()?class Counter extends StatefulWidget { const Counter({super.key}); @override State<Counter> createState() => _CounterState();}…Нажатие на кнопку вызывает
setState, но счётчик всегда показывает 0. Почему, и из-за чего ещёsetStateможет «ничего не делать»?class _CounterState extends State<Counter> { @override Widget build(BuildContext context) { int count = 0; return TextButton( onPressed: () => setState(() => count++), child: Text('$count'), ); }}Что значит «поднять состояние» (lift state up) во Flutter и когда это нужно делать?
class FilterBar extends StatefulWidget { const FilterBar({super.key}); @override State<FilterBar> createState() => _FilterBarState();}…Почему колбэк, который вы передаёте в
setState, должен быть синхронным и что будет, если сделать егоasync?// wrong: debug throws "setState() callback argument returned a Future"void _loadWrong() { setState(() async { _user = await api.fetchUser(); // assigned after the rebuild already ran });}…Что нужно освободить в
dispose()уStateи что ломается, если про это забыть?class _EditorState extends State<Editor> with SingleTickerProviderStateMixin { final _text = TextEditingController(); final _focus = FocusNode(); late final AnimationController _anim; StreamSubscription<Draft>? _sub; Timer? _autosave;…Что напечатает этот код после одного нажатия на кнопку (не считая начального
build: 0) и сколько раз выполнитсяbuild?class _DemoState extends State<Demo> { int _count = 0; void _tap() { setState(() => _count++); setState(() => _count++); setState(() => _count++); print('after: $_count'); } @override Widget build(BuildContext context) { print('build: $_count'); return TextButton(onPressed: _tap, child: Text('$_count')); }}Пользователь дважды нажимает «Сохранить», и создаются два заказа. Как остановить второе нажатие локальным состоянием?
class _SaveButtonState extends State<SaveButton> { bool _saving = false; Future<void> _save() async { if (_saving) return; // the second tap lands here setState(() => _saving = true); // same synchronous slice: no race…Что будет, если вызвать
setStateпослеdispose(), и зачем проверятьmountedпосле каждогоawait?class _ProfilePageState extends State<ProfilePage> { Profile? _profile; Future<void> _refresh() async { final messenger = ScaffoldMessenger.of(context); // capture before the gap try {…Чем
initStateотличается отdidChangeDependencies?class _PostFeedState extends State<PostFeed> { FeedRepository? _repo; StreamSubscription<List<Post>>? _sub; List<Post> _posts = const []; @override…Найдите баг: родитель сбрасывает количество в строке корзины на 1, а этот степпер продолжает показывать старое число.
class _QuantityStepperState extends State<QuantityStepper> { late int _qty; @override void initState() { super.initState(); _qty = widget.quantity; } void _inc() { setState(() => _qty++); widget.onChanged(_qty); } @override Widget build(BuildContext context) => TextButton(onPressed: _inc, child: Text('$_qty'));}Из-за чего возникает ошибка «setState() or markNeedsBuild() called during build» и как её исправить?
// wrong: the parent's setState runs while the tree is being builtclass _ItemsPanelState extends State<ItemsPanel> { @override Widget build(BuildContext context) { if (widget.items.isEmpty) widget.onEmpty(); // parent callback calls its setState return ItemList(widget.items);…Чекбокс внутри диалога не обновляется при вызове
setState. Почему и как это исправить?final accepted = await showDialog<bool>( context: context, builder: (dialogContext) { var agreed = false; // lives as long as this builder's closure return StatefulBuilder( builder: (context, setLocalState) => AlertDialog(…Что такое
ValueNotifier, чем он отличается отChangeNotifierи когда он лучшеsetState?class _FavoriteButtonState extends State<FavoriteButton> { final _liked = ValueNotifier<bool>(false); @override void dispose() { _liked.dispose();…Тяжёлый график внутри
ValueListenableBuilderперестраивается на каждом тике. Зачем у билдера параметрchild?// wrong: a fresh ExpensiveChart instance on every tick, so it rebuildsValueListenableBuilder<int>( valueListenable: _ticks, builder: (context, ticks, _) => Column(children: [ Text('$ticks'), ExpensiveChart(series: widget.series),…Форма перестраивает всю страницу на каждое нажатие клавиши, потому что
onChangedвызываетsetState. Где на самом деле должен жить этот текст?// wrong: every keystroke rebuilds the page, and _text duplicates the truthTextField(onChanged: (v) => setState(() => _text = v)); // right: the controller is the source of truth; only the button listensclass _ComposerState extends State<Composer> { final _controller = TextEditingController();…Какую проблему решает
InheritedWidgetи как работаетupdateShouldNotify?class AppColors extends InheritedWidget { const AppColors({super.key, required this.primary, required super.child}); final Color primary; static AppColors of(BuildContext context) {…Чем
InheritedWidgetотличается от Provider?Какие главные претензии к GetX и почему многие команды его избегают?
Найдите баг в том, как этот
CartBadgeподписывается на корзину, и исправьте его.class _CartBadgeState extends State<CartBadge> { @override void initState() { super.initState(); widget.cart.addListener(() => setState(() {})); } @override void dispose() { widget.cart.removeListener(() => setState(() {})); super.dispose(); } @override Widget build(BuildContext context) => Text('${widget.cart.count}');}Чем
dependOnInheritedWidgetOfExactTypeотличается отgetInheritedWidgetOfExactTypeи почему читатель иногда так и не перестраивается?class CartScope extends InheritedWidget { const CartScope({super.key, required this.items, required super.child}); final List<Item> items; // registers this context as a dependent static CartScope of(BuildContext context) =>…Каждый виджет, читающий ваш
InheritedWidget, перестраивается при изменении любого поля. Как сделать так, чтобы он зависел только от одного?enum PrefAspect { currency, locale } class Prefs extends InheritedModel<PrefAspect> { const Prefs( {super.key, required this.currency, required this.locale, required super.child});…