为什么FutureBuilder包裹的DropdownButtonFormField重建时会追加相同数据
问题根源
- Future实例在build方法中重复创建:你当前将
getPetTypes()的调用放在无状态组件的build方法内,每次组件触发重建时都会生成新的Future实例,导致FutureBuilder反复执行异步加载流程。如果你的Cubit中getPetTypes方法内部是将请求到的数据追加到缓存列表而非返回全新的独立列表,每次加载返回的列表就会叠加旧数据,出现重复。 - 固定Key导致组件状态复用:你给
DropdownButtonFormField设置了固定的常量Key,Flutter在组件树更新时会复用相同Key的组件状态,而非销毁重建旧组件,旧的选项状态不会被清空,新返回的重复数据就会叠加展示。 - 额外问题:你当前写死了
value: 'Dog',会导致用户选择其他选项后,组件重建时选中值自动回退到Dog,建议把选中值存在Cubit状态中动态读取。
解决方案
1. 调整组件结构,缓存Future实例
改用StatefulWidget,在initState中仅调用一次getPetTypes()生成Future实例,复用给FutureBuilder,避免每次build重复触发请求。
2. 修正Cubit的数据返回逻辑
确保getPetTypes每次返回的都是全新的、未叠加旧缓存的列表,不要在方法内部对全局存储的列表做追加操作。
3. 调整组件Key配置
要么去掉Dropdown的固定Key,要么将Key和当前下拉列表数据绑定,数据变化时Key同步更新,触发Flutter直接重建下拉组件,不复用旧状态。
修改后代码示例
// 改用StatefulWidget缓存Future实例 class PetTypeInput extends StatefulWidget { const PetTypeInput({super.key}); @override State<PetTypeInput> createState() => _PetTypeInputState(); } class _PetTypeInputState extends State<PetTypeInput> { late Future<List<PetType>> _petTypesFuture; @override void initState() { super.initState(); // 初始化阶段仅调用一次获取数据的方法 _petTypesFuture = context.read<RegisterPetProfileCubit>().getPetTypes(); } @override Widget build(BuildContext context) { return FutureBuilder<List<PetType>>( future: _petTypesFuture, // 复用同一个Future实例 builder: (context, snapshot) { if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { final petKinds = snapshot.data!; return DropdownButtonFormField<String>( // Key和数据绑定,数据变化时自动重建组件 key: Key('registerForm_petKindInput_${petKinds.hashCode}'), decoration: const InputDecoration( labelText: 'pet kind', helperText: '', ), // 从Cubit状态中动态读取当前选中值,不要写死 value: context.watch<RegisterPetProfileCubit>().state.selectedPetKind, onChanged: (petKindValue) => context .read<RegisterPetProfileCubit>() .petKindChanged(petKindValue!), items: _buildItems(petKinds), ); } return const TextField( enabled: false, decoration: InputDecoration( labelText: 'pet kind', helperText: '', ), ); }, ); } List<DropdownMenuItem<String>> _buildItems(List<PetType> petKinds) { final petTypesMap = <String, String>{}; for (var petType in petKinds) { petTypesMap[petType.id] = petType.label; } return petTypesMap.entries.map((entry) { return DropdownMenuItem<String>( key: Key(entry.key), child: Text(entry.value), value: entry.key, ); }).toList(); } }
Cubit方法修正示例
// 错误写法:每次调用都往同一个列表追加数据,导致重复 List<PetType> _cachedPetTypes = []; Future<List<PetType>> getPetTypes() async { final res = await api.requestPetTypes(); _cachedPetTypes.addAll(res); return _cachedPetTypes; } // 正确写法:每次返回全新的独立列表 Future<List<PetType>> getPetTypes() async { final res = await api.requestPetTypes(); return List.unmodifiable(res); }
内容的提问来源于stack exchange,提问作者Ariel Magbanua
相关产品推荐
相关产品推荐

