🌊

【Riverpod】Listの値を更新したのにref.watchで再描画されない

2022/04/18に公開
2

事象

watch している状態が更新されているはずなのに、再描画されないことがありました。

事象が起こったコードを単純化すると以下のような感じです。

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

class Hoge extends ConsumerWidget {
  Hoge({Key? key}) : super(key: key);

  final StateProvider<List<String>> _hogeListProvider =
      StateProvider((ref) => ['hoge', 'fuga']);

  
  Widget build(BuildContext context, WidgetRef ref) {
    final hogeList = ref.watch(_hogeListProvider);
    return Column(
      children: [
        Column(
          children: List.generate(
            hogeList.length,
            (index) {
              return Text(hogeList[index]);
            },
          ),
        ),
        ElevatedButton(
          child: Container(
            color: Colors.blue,
          ),
          onPressed: () {
            // リストに'fuga'がなければ追加、あれば削除
            if (hogeList.contains('fuga')) {
              hogeList.remove('fuga');
            } else {
              hogeList.add('fuga');
            }
            // 削除or追加したリストをstateに代入する
            ref.read(_hogeListProvider.notifier).state = hogeList;
          },
        )
      ],
    );
  }
}

何らかのメソッドで、状態管理の対象となっているhogeListの操作を行い、状態を更新。
一見正しく状態の更新が行われているように見えたのですが、再描画されませんでした。

解決策

状態の更新を以下のように書き換えると再描画されました

- ref.read(_hogeListProvider.notifier).state = hogeList;
+ ref.read(_hogeListProvider.notifier).state = [...hogeList];

原因

原因ははっきりとは分かりませんが、List の中身を変えただけでは List 自体の参照(メモリ番地)が変わらないため、変化があったとみなされないのでしょうか?
詳しい方いらっしゃったらコメント頂けると助かります 🙇🏻‍♂️

Discussion

r-toyr-toy

List の中身を変えただけでは List 自体の参照(メモリ番地)が変わらないため、変化があったとみなされないのでしょうか?

Dartは原則参照渡しですが、Spread Operatorを使いListオブジェクト自体を再生成したことで異なる値として認識されたからですね。

illyilly

なるほど、ぼんやりと参照渡しのあたりが原因かとは思っていたのですが、やはり再生成されなければ異なる値と認識されないのですね。
ありがとうございます!