Menu
CoddyTech

Riverpod: Notifier

CoddyのFlutterジャーニー「状態管理」セクションの一部。レッスン 19/27。

StateProviderは数値ひとつなら問題ありません。追加や削除のメソッドを持つリストのように、状態にルールが伴う場合は、状態とそれを変更するメソッドを保持するクラスであるNotifierを書きます。

class GuestList extends Notifier<List<String>> {
  @override
  List<String> build() => [];

  void add(String name) {
    state = [...state, name];
  }
}

final guestsProvider =
    NotifierProvider<GuestList, List<String>>(GuestList.new);

buildは最初の状態である空のリストを返します。クラス内では、stateが現在の値です。GuestList.newはクラスのコンストラクターで、プロバイダーがNotifierを作成できるように渡します。

重要なルールは、stateには新しい値を設定し、古い値は決して変更しないことです。Riverpodは、stateに別のオブジェクトが設定されたときにだけウィジェットに通知します。state.add(name)はリストをその場で変更するため、stateは同じリストのままで、誰にも通知されません。[...state, name]は新しいリストを作ります。3つのドットは古い名前をすべて新しいリストに展開し、その後、新しい名前を末尾に追加します。

ウィジェットはNotifierを介してメソッドを呼び出します。ref.read(guestsProvider.notifier).add('Ada')。このページはConsumerStatefulWidget、つまりステートフルな種類です。テキストコントローラーも保持するためです。そのStateはフィールドとしてrefを持ちます。

challenge icon

チャレンジ

難しい

ゲストリストにはguestsProvider内の名前が表示されますが、Addを押しても入力欄がクリアされるだけです。

  • GuestListにメソッドadd(String name)を追加し、名前を末尾に追加した新しいリストをstateに設定してください。
  • 入力欄をクリアする前に、Addから入力欄のテキストを渡してそのメソッドを呼び出すようにしてください。

自分で試してみよう

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class GuestList extends Notifier<List<String>> {
  @override
  List<String> build() => [];
}

final guestsProvider = NotifierProvider<GuestList, List<String>>(GuestList.new);

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: PartyPage(),
    );
  }
}

class PartyPage extends ConsumerStatefulWidget {
  const PartyPage({super.key});

  @override
  ConsumerState<PartyPage> createState() => _PartyPageState();
}

class _PartyPageState extends ConsumerState<PartyPage> {
  final controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    final guests = ref.watch(guestsProvider);
    return Scaffold(
      appBar: AppBar(title: Text('Guests: ${guests.length}')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: controller,
                    decoration: const InputDecoration(labelText: 'Guest name'),
                  ),
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: () {
                    controller.clear();
                  },
                  child: const Text('Add'),
                ),
              ],
            ),
          ),
          Expanded(
            child: ListView(
              children: [
                for (final name in guests) ListTile(leading: const Icon(Icons.person), title: Text(name)),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

状態管理のすべてのレッスン

自分で練習してみよう: Flutterオンラインコンパイラ