Riverpod存储字符串列表的状态为何总是重置?如何修复?
问题描述
我构建了如下Riverpod状态:
import "package:riverpod_annotation/riverpod_annotation.dart"; part "form_state.g.dart"; @riverpod class FormState extends _$FormState { @override List<String?> build() { ref.listenSelf((previous, next) { log("previous: $previous, next: $next", name: "FORMSTATE 0"); }); return List.filled(10, null); } void change(int index, String value) { log("change: $index, value: $value", name: "FORMSTATE 1"); state = [ ...state.sublist(0, index), value, ...state.sublist(index + 1), ]; } }
在Widget中使用了一组TextField:
class DummyPage extends ConsumerStatefulWidget { const DummyPage({super.key}); @override ConsumerState<DummyPage> createState() => _DummyPageState(); } class _DummyPageState extends ConsumerState<DummyPage> { final List<TextEditingController> _controllers = List.generate(10, (index) => TextEditingController()); @override void initState() { final form = ref.read(formStateProvider); for (var i = 0; i < _controllers.length; i++) { _controllers[i].text = form[i] ?? ""; } super.initState(); } @override void dispose() { for (var i = 0; i < _controllers.length; i++) { _controllers[i].dispose(); } super.dispose(); } @override Widget build(context) { return Column(children: [ ...List.generate( _controllers.length, (index) => TextField( controller: _controllers[index], onChanged: (val) { ref.read(formStateProvider.notifier).change(index, val); } ) ]) } }
当前每次调用onChanged时,状态总会重置,日志输出如下:
[FORMSTATE 0] previous: null, next: [null, null, null, null, null, null] [FORMSTATE 1] change: 0, value: 2 [FORMSTATE 0] previous: [null, null, null, null, null, null], next: [2, null, null, null, null, null] [FORMSTATE 0] previous: null, next: [null, null, null, null, null, null] [FORMSTATE 1] change: 1, value: 3 [FORMSTATE 0] previous: [null, null, null, null, null, null], next: [null, 3, null, null, null, null] [FORMSTATE 0] previous: null, next: [null, null, null, null, null, null] [FORMSTATE 1] change: 2, value: 9 [FORMSTATE 0] previous: [null, null, null, null, null, null], next: [null, null, 9, null, null, null]
请问导致该问题的原因是什么?如何修改才能实现当列表中索引i的TextField值变化时,Riverpod状态的对应索引i的值也同步更新?
问题原因
核心问题是Widget未监听Riverpod状态变化,且状态实例未被持续订阅:
build方法中没有用ref.watch订阅formStateProvider,状态更新时不会触发Widget重建,无法同步最新状态到输入框控制器。initState仅在初始化时读取一次状态,后续状态更新后不会同步到TextEditingController;同时因为没有监听状态,Provider可能因作用域变化被销毁重建,导致每次操作拿到的都是初始状态实例。
修改方案
方案1:让Widget监听状态并同步控制器
修改DummyPage代码,通过ref.watch订阅状态,在状态更新时同步输入框控制器的文本:
class DummyPage extends ConsumerStatefulWidget { const DummyPage({super.key}); @override ConsumerState<DummyPage> createState() => _DummyPageState(); } class _DummyPageState extends ConsumerState<DummyPage> { final List<TextEditingController> _controllers = List.generate(10, (index) => TextEditingController()); @override void initState() { super.initState(); // 初始化时同步初始状态到控制器 final initialState = ref.read(formStateProvider); for (var i = 0; i < _controllers.length; i++) { _controllers[i].text = initialState[i] ?? ""; } } @override void dispose() { for (var controller in _controllers) { controller.dispose(); } super.dispose(); } @override Widget build(BuildContext context) { // 监听状态变化,同步最新状态到对应控制器 final formState = ref.watch(formStateProvider); for (var i = 0; i < formState.length; i++) { // 避免不必要的文本赋值,防止光标跳动 if (_controllers[i].text != formState[i]) { _controllers[i].text = formState[i] ?? ""; } } return Column(children: [ ...List.generate( _controllers.length, (index) => TextField( controller: _controllers[index], onChanged: (val) { ref.read(formStateProvider.notifier).change(index, val); }, ), ), ]); } }
方案2:优化状态更新逻辑(可选)
可以简化FormState的change方法写法,逻辑与原代码一致但更清晰:
void change(int index, String value) { log("change: $index, value: $value", name: "FORMSTATE 1"); final newState = List<String?>.from(state); newState[index] = value; state = newState; }
关键说明
ref.watch会订阅Provider,状态更新时自动触发Widget重建,确保Widget始终持有最新状态。- 添加文本判断可以避免重复赋值导致的输入光标异常跳动问题。
内容的提问来源于stack exchange,提问作者sempraEdic
相关产品推荐
相关产品推荐

