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],
],
);
},
如果您使用 StateNotifier
和 StateNotifierProvider
将更具可读性。然后您可以定义一个 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);
},
我正在尝试创建一个复选框列表,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],
],
);
},
如果您使用 StateNotifier
和 StateNotifierProvider
将更具可读性。然后您可以定义一个 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);
},