需求
有一个可输入的列表,输入列表会根据选择的选项不同,而输入不同内容。
比如:
选择 Vip客户时,需要输入:姓名,手机号,住址,Vip时长,付款方式
选择普通用户时,需要输入:姓名,手机号,住址。
实现
使用ListView展示所有的输入项,因为输入框具有通用性。所以做了一个简单的封装。
左侧为标题,右侧为输入内容,空内容时,展示hint提示请输入
。
大概是这样的样式
-----------------------------
客户类型 ⊙Vip ⊙普通
-----------------------------
姓名 请输入
-----------------------------
手机号 请输入
-----------------------------
住址 请输入
-----------------------------
当客户类型改变时,下方数据需要联动改变。
问题
切换客户类型时,会有数据错乱的情况(比如手机号显示到了姓名那里)。
追查问题:
由于数据共享使用的Provider+ChangeNotifier的形式。所以,数据变化时,实际上,引用到了值得地方会进行更新的。
所以第一步判断,值是否传递到了自己封装的组件中。
自己封装的输入框代码(简陋版,主要为了说明问题):
// CustomInputTextField
class CustomInputTextField extends StatefulWidget {
// 提示
final String hint;
// 输入变化监听
final ValueChanged<String>? onValueChanged;
// 初始值
final String? value;
const CustomInputTextField(
{Key? key,
required this.hint,
this.onValueChanged,
this.value,})
: super(key: key);
@override
State<StatefulWidget> createState() {
return CustomInputTextFieldState();
}
}
class CustomInputTextFieldState extends State<CustomInputTextField> {
late TextEditingController _controller;
@override
void initState() {
_controller = TextEditingController(text : widget.value??"");
}
@override
Widget build(BuildContext context) {
return TextField(
controller: _controller,
onChanged: onInputChanged,
);
}
}
发现问题原因
其实,当数据更新时,每次Build时拿到的值是正确的值,那为什么,显示出来的值确实错乱的呢?
因为,TextField中的值,显示的其实是_controller
中的Text,而因为使用了State,而initState
方法只会执行一次,所以当组件中value
变化时候,实际上,_controller
中的数据并没有变化。所以显示的会错乱。
解决方案
粗暴版
直接每次都创建新的controller,因为这个controller在整个组件中,没有主动用到。
controller: TextEditingController.fromValue(
TextEditingValue(
text: widget.value??'',
selection: TextSelection.fromPosition(
TextPosition(offset: widget.value?.length ?? 0),
),
),
),
善良版
在每次组件调用didChangeDependence时,重新取widget的值给_controller赋值上。
或者在didUpdateWidget时,将新的值拿到,与旧的值对比,并替换。