Riverpod StateProvider 在列表上不起作用

Riverpod StateProvider doesn't work on list

我正在尝试创建一个复选框列表,selectedProvider 包含一个长度为 choices.lenght 的布尔列表。假设 choices.length 是 4,初始列表应该是 [false, false, false, false],假设用户点击最后一个,列表应该变成 [false, false, false, true],但是弹框没有更新.我做错了什么吗?

Widget getCheckboxList(WidgetRef ref, int start, int end) {
  List<Widget> content = [];

  final selectedProvider = StateProvider<List<bool>>(
      (ref) => List.filled(choices.length, false));

  for (int i = start; i <= end; i++) {
    content.add(
      Theme(
        data: ThemeData(
          splashColor: Colors.transparent,
          highlightColor: Colors.transparent,
        ),
        child: CheckboxListTile(
          controlAffinity: ListTileControlAffinity.leading,
          contentPadding: const EdgeInsets.symmetric(horizontal: 4),
          visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
          title: Row(
            children: [
              const SizedBox(width: 5),
              Text(choices[i][0]),
              const Spacer(),
              Text(choices[i][1] == 0 ? '免費' : '+ HK$ ${choices[i][1]}'),
              const SizedBox(width: 16),
            ],
          ),
          activeColor: primaryColor,
          value:
              ref.watch(selectedProvider.select((selected) => selected[i])),
          onChanged: (bool? picked) {
            ref.read(selectedProvider.notifier).state[i] = picked!;
          },
        ),
      ),
    );
  }
  return Column(children: [...content]);
}

这样做的原因是您已将提供程序定义放在 getCheckboxList 方法中,我认为(没有看到其余代码)它在另一个小部件的 [=13] 中使用=] 方法。这将导致每次重建小部件时调用该方法(和 re-executed)。

如果您将 selectedProvider 定义移动到任何 class 之外(根据需要),它应该适合您。

一般来说,您应该避免使用方法来构建小部件。这些都是非常低效的,并且在应用程序运行时不会被平台“缓存”。相反,您可以(并且应该)将此类逻辑打包到自定义小部件中。

比如上面的可以改成这样:

// content_widget.dart
final selectedProvider = StateProvider<List<bool>>(
  (ref) => List.filled(4, false),
);

class ContentWidget extends ConsumerWidget {
  ...
  @override
  Widget build(BuildContext build, WidgetRef ref) {
    final List<bool> selectedValues = ref.watch(selectedProvider);

    return Theme(
      data: ThemeData(),
      child: Column(
        ...
        children: List.generate(
          selectedValues.length,
          (int i) => CheckboxListTile(
            ...
            value: selectedValues[i],
            onChanged: (bool? selected) {
              ref.read(selectedProvider.notifier).state[i] = picked!;
            },
          ),
        ),
      ),
    );
  }
}

如果您希望您的提供商根据任意 choices 对象更改其大小,您只需创建一个 choicesProvider 然后您的 selectProvider 就可以像这样使用它:

final selectedProvider = StateProvider<List<bool>>(
  (ref) => List.filled(ref.watch(choicesProvider).length, false),
);

由于状态是不可变的,我们需要做这样的事情:

onChanged: (_) {
  ref.read(selectedProvider.notifier).update(
        (state) => [
          for (var j = 0; j < state.length; j++)
            if (j == i) !state[j] else state[j],
        ],
      );
},

如果您使用 StateNotifierStateNotifierProvider 将更具可读性。然后您可以定义一个 toggle() 函数。

class Selected extends StateNotifier<List> {
  Selected(List initial) : super(initial);

  void toggle(index) {
    state = [
      for (var i = 0; i < state.length; i++)
        if (i == index) !state[i] else state[i],
    ];
  }
}

然后定义提供者

final selectedProvider = StateNotifierProvider<Selected, List>(
    (ref) => Selected(List.filled(8, false)));

当您必须更改值时,

onChanged: (_) {
  ref.read(selectedProvider.notifier).toggle(i);
},