Flutter中DropdownButtonFormField调用setState后UI不更新问题
问题根源
你的DefaultDropdown组件内部维护了_currentValue状态,但仅在initState时初始化,当父组件更新传入的initialValue(即通过CEP获取到新的UF值时),子组件没有同步这个新值,导致Dropdown的UI还是显示旧值。只有当PageView切换页面触发组件重建时,才会重新获取最新的initialValue,这就是切换页面后才显示正确值的原因。
解决方案
1. 修复DefaultDropdown组件的状态同步
在DefaultDropdownState中重写didUpdateWidget方法,当父组件传入的initialValue发生变化时,同步更新内部的_currentValue并触发UI刷新:
@override void didUpdateWidget(covariant DefaultDropdown<T> oldWidget) { super.didUpdateWidget(oldWidget); // 当初始值变化时,同步更新内部状态 if (widget.initialValue != oldWidget.initialValue) { setState(() { _currentValue = widget.initialValue; }); } }
完整修改后的DefaultDropdownState代码:
class DefaultDropdownState<T> extends State<DefaultDropdown<T>> { T? _currentValue; @override void initState() { super.initState(); _currentValue = widget.initialValue; } // 添加该方法处理父组件参数更新 @override void didUpdateWidget(covariant DefaultDropdown<T> oldWidget) { super.didUpdateWidget(oldWidget); if (widget.initialValue != oldWidget.initialValue) { setState(() { _currentValue = widget.initialValue; }); } } @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.label, style: const TextStyle(fontWeight: FontWeight.w800), ), const SizedBox(height: 8), SizedBox( height: widget.height, child: DropdownButtonFormField<T>( value: _currentValue, onChanged: (newValue) { setState(() { _currentValue = newValue; widget.onChanged(newValue); }); }, decoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), ), fillColor: widget.fillColor, filled: true, contentPadding: const EdgeInsets.symmetric(horizontal: 12.0), ), isExpanded: true, items: widget.items.map((T item) { return DropdownMenuItem<T>( value: item, child: Text( item.toString(), overflow: TextOverflow.ellipsis, ), ); }).toList(), ), ), ], ); } }
2. 确认CompanyFormThree的状态绑定正确
确保CompanyFormThree中的UF状态正确关联父组件传入的值:
- 将组件内的UF变量改为状态变量(比如
late String _uf) - 在
initState中初始化_uf = widget.uf - 在
_fetchAddressFromCEP的setState中更新_uf,同时调用widget.onUfChanged同步到父页面
修改后的CompanyFormThree关键代码示例:
class CompanyFormThree extends StatefulWidget { final ValueChanged<String> onUfChanged; final String uf; // 其他参数... const CompanyFormThree({ super.key, required this.onUfChanged, required this.uf, // 其他参数初始化... }); @override State<CompanyFormThree> createState() => _CompanyFormThreeState(); } class _CompanyFormThreeState extends State<CompanyFormThree> { late String _uf; // 其他控制器... @override void initState() { super.initState(); _uf = widget.uf; // 其他初始化代码... } void _fetchAddressFromCEP(String cep) async { final response = await http.get(Uri.parse('https://viacep.com.br/ws/$cep/json/')); if (response.statusCode == 200) { final data = jsonDecode(response.body); if (data['erro'] == null) { if(mounted){ setState(() { publicSpaceController.text = data['logradouro'] ?? ''; neighborhoodController.text = data['bairro'] ?? ''; cityController.text = data['localidade'] ?? ''; numberController.text = ''; _uf = data['uf'] ?? ''; widget.onUfChanged(_uf); }); } } else { AlertSnackBar.show(context: context, text: "CEP não encontrado."); } } else { AlertSnackBar.show(context: context, text: "Erro ao buscar CEP."); } } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ // 其他组件... DefaultDropdown( items: ufsBrasil, label: "UF", initialValue: _uf.isNotEmpty ? _uf : null, onChanged: (newUF) { setState(() { _uf = newUF!; }); widget.onUfChanged(_uf); }, ), // 其他组件... ], ), ); } }
原理说明
didUpdateWidget是StatefulWidget的生命周期方法,当父组件传入的参数变化时会被调用,通过对比新旧initialValue,可以同步更新子组件内部的状态。- 确保子组件的状态和父组件的状态双向绑定,无论是用户手动选择Dropdown还是通过API自动填充,都能实时同步并刷新UI。
内容的提问来源于stack exchange,提问作者Daniel Henrique Hartmann
相关产品推荐
相关产品推荐

