[障害対応]Provider/Consumerを使っていなかった為に発生した障害(3つのボタンのうち1つ目のボタンでしか状態が反映されなかった、という事例)

1.事象

Providerで3つのボタンごとに、MultiProvider内で、ChangeNotifierProvider指定で、3つの処理(クラス)を登録していたのですが、動作させると、1つ目のボタンを押した時だけ、(ほかのボタンを押した結果も、まとめて)一括で反映される、という事象が発生しました。

なぜ、同じように登録したはずの、3つの処理の結果が、異なるタイミング(今から思えば、1つ目のボタンだけが状態管理を変更するトリガー設定になっていた、ということなのですが)で表示される、おかしいなぁ、という状態でした。

2.原因

結果表示用画面(ListView.builder)で、itemCount、itemBuilder に、1つ目の処理(1つ目のボタン)だけしか、初期設定していなかった、というのが原因でした。
つまり、3つの処理(3つのボタン)の処理結果を、画面に反映するためには、Consumer によって、必要な処理を全て、設定しなくてはいけませんでした。

3.修正内容

ListView.builder の処理を、各々の処理の分だけ、Consumer<各々の処理クラス>としたものを、設定し、それらをラップして表示する、という修正を行いました。

(修正前)問題個所:以下のtaskMode1の部分(これしか初期設定していなかった!)
    return Scaffold(
      appBar: AppBar(title: const Text('Task List')),
      body: ListView.builder(
        itemCount: taskModel1.tasks.length,
        itemBuilder: (context, index) {
          final task = taskModel1.tasks[index];
          return ListTile(
            title: Text(task['task']),
            trailing: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                IconButton(
                  icon: const Icon(Icons.edit),
                  onPressed: () {
                    _editTaskDialog(context, taskModel1, task['id'], task['task']);
                  },
                ),
                IconButton(
                  icon: const Icon(Icons.delete),
                  onPressed: () {
                    taskModel1.removeTask(task['id']);
                  },
                ),
              ],
            ),
          );
        },
      ),

(修正内容)

上記で書いた様に、ListView.builder の処理を、各々の処理の分だけ、Consumer<各々の処理クラス>としたものを、設定し、それらをラップしました。

(修正後)3~34行目(後は、5~34行目を、元処理分だけ設定する:
    return Scaffold(
      appBar: AppBar(title: const Text('Task List')),
      body: Column(
        children: [
          Expanded(
            child: Consumer<TaskModel1>(
              builder: (context, taskModel1, child) =>  ListView.builder(
                itemCount: taskModel1.tasks.length,
                itemBuilder: (context, index) {
                  final task = taskModel1.tasks[index];
                  return ListTile(
                    title: Text(task['task']),
                    trailing: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        IconButton(
                          icon: const Icon(Icons.edit),
                          onPressed: () {
                            _editTaskDialog1(context, taskModel1, task['id'], task['task']);
                          },
                        ),
                        IconButton(
                          icon: const Icon(Icons.delete),
                          onPressed: () {
                            taskModel1.removeTask(task['id']);
                          },
                        ),
                      ],
                    ),
                  );
                },
              ),
            ),
          ),
          Expanded(
            child: Consumer<TaskModel2>(
              builder: (context, taskModel2, child) =>  ListView.builder(
                itemCount: taskModel2.tasks.length,
                itemBuilder: (context, index) {
                  final task = taskModel2.tasks[index];
                  return ListTile(
                    title: Text(task['task']),
                    trailing: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        IconButton(
                          icon: const Icon(Icons.edit),
                          onPressed: () {
                            _editTaskDialog2(context, TaskModel2(), task['id'], task['task']);
                          },
                        ),
                        IconButton(
                          icon: const Icon(Icons.delete),
                          onPressed: () {
                            taskModel2.removeTask(task['id']);
                          },
                        ),
                      ],
                    ),
                  );
                },
              ),
            ),
          ),
          Expanded(
            child: Consumer<TaskModel3>(
              builder: (context, taskModel3, child) =>  ListView.builder(
                itemCount: taskModel3.tasks.length,
                itemBuilder: (context, index) {
                  final task = taskModel3.tasks[index];
                  return ListTile(
                    title: Text(task['task']),
                    trailing: Row(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        IconButton(
                          icon: const Icon(Icons.edit),
                          onPressed: () {
                            _editTaskDialog3(context, TaskModel3(), task['id'], task['task']);
                          },
                        ),
                        IconButton(
                          icon: const Icon(Icons.delete),
                          onPressed: () {
                            taskModel2.removeTask(task['id']);
                          },
                        ),
                      ],
                    ),
                  );
                },
              ),
            ),
          ),
        ],
      ),

4.結果

上記の修正(Consumer による設定により、元処理毎に、結果反映範囲を、個別に設定したので)一応、予定通りの内容になりました。(※ただ途中、DB操作メソッドについては、処理をいじったので、冒頭のメッセージとは変わっています)

Consumerの部分だけ、参考にして下さい。(DB操作部分の説明は省略してあります)

コメントを残す