状態を共通の祖先に置き、子ウィジェットを、コールバックを通じて変化を報告する制御されたウィジェットに変えましょう。
このレッスンを Kodokon で開くよくある状況です。数量セレクターと、別の場所に表示される合計が、同じデータを共有する必要があります。黄金律はこうです。状態はそれに依存するウィジェットの最も近い共通の祖先に置かれます。親がデータを所有し、それをパラメーターとして下へ渡し、上へと流れ戻るコールバックを通じて変化を受け取ります。これが"状態のリフトアップ"です。
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のときにマイナスボタンが無効になっていることに注目してください。onPressedにnullを渡すだけで十分であり、このロジックは受け取った値から直接導かれます。
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)です。ツリーが深くなり、コールバックを五段階も通し続けるのが面倒になったら、状態管理の解決策を知る時が来ます。それが次のモジュールのテーマです。