INDEX / JOURNAL / LOG-010
VOL. 2026 · ENGINEERING
SERIES 3 / 8
LOG-010 · HOOKS

StatefulWidget を Hooks で書き換える

PUBLISHED
2026 · 08 · 14
AUTHOR
MIGIWAYA
READ
約 5 分
TAGS
#flutter#dart
lib/screens/profile_edit_screen.dartDART
class ProfileEditScreen extends StatefulWidget {
const ProfileEditScreen({super.key});
 
@override
State<ProfileEditScreen> createState() => _ProfileEditScreenState();
}
 
class _ProfileEditScreenState extends State<ProfileEditScreen> {
final _nameController = TextEditingController();
 
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
 
@override
Widget build(BuildContext context) => TextField(controller: _nameController);
}

§ 01WHY

lib/screens/profile_edit_screen.dartDART
class ProfileEditScreen extends HookWidget {
const ProfileEditScreen({super.key});
 
@override
Widget build(BuildContext context) {
final nameController = useTextEditingController();
return TextField(controller: nameController);
}
}

§ 02USESTATE

lib/screens/counter_screen.dartDART
class CounterScreen extends HookWidget {
const CounterScreen({super.key});
 
@override
Widget build(BuildContext context) {
final count = useState(0);
 
return Column(
children: [
Text('${count.value}'),
ElevatedButton(
onPressed: () => count.value++, // setState 相当
child: const Text('+1'),
),
],
);
}
}

§ 03USEEFFECT

DART
useEffect(() {
final subscription = itemStream.listen(_handleEvent);
return subscription.cancel; // 返した関数が「後始末」になる
}, const []); // 第 2 引数が実行タイミングを決める
keysいつ実行されるかState での対応
毎回の buildbuild のたびに走る処理
初回のみinitState / dispose
userId が変わったときdidUpdateWidget での差分処理
DART
final formatter = useMemoized(() => NumberFormat.decimalPattern(), const []);

§ 04RULES

DART
// すべて壊れる書き方
if (isEditing) {
final controller = useTextEditingController(); // 条件分岐の中
}
for (final item in items) {
useState(item); // ループの中
}
if (user == null) return const SizedBox(); // hook より前の早期 return
final name = useState(user.name);
DART
final controller = useTextEditingController(); // 先に全部呼ぶ
final isEditing = useState(false);
 
if (user == null) return const SizedBox(); // 分岐はその後

§ 05RIVERPOD

Widgetref(共有状態)hooks(ローカル)
lib/screens/item_search_screen.dartDART
class ItemSearchScreen extends HookConsumerWidget {
const ItemSearchScreen({super.key});
 
@override
Widget build(BuildContext context, WidgetRef ref) {
final searchController = useTextEditingController(); // ローカルな入れ物
final items = ref.watch(itemListProvider); // 共有状態
 
return Column(
children: [
TextField(controller: searchController),
Expanded(child: ItemList(items: items)),
],
);
}
}

§ 06BOUNDARY

§ 07SUMMARY

脚注 · NOTES
  1. [1]
  2. [2]
  3. [3]
  4. [4]
  5. [5]
同シリーズ · Flutter の基礎を実コードで学ぶ II
§ + RELATED すべて見る →
INQUIRY
CONTACT
LOCATION
STATUS
Active / Open
ESTABLISHED
2024 · 5 · 5
FORM