Flutter中FutureBuilder结合DropdownButton调用setState报错问题
Flutter问题解决:Dropdown重复值错误与JSON重复加载
问题分析
- getJsonData重复调用:FutureBuilder的
future参数直接传入getJsonData(),每次Widget rebuild(比如调用setState)都会重新执行该方法,导致JSON重复加载。 - DropdownButton重复值错误:
Students类未正确实现相等性判断(==和hashCode),DropdownButton无法识别选项的唯一性;或因重复加载导致列表出现重复项,选中时触发校验错误。
解决方案
1. 修复JSON重复加载问题
将Future对象存储在State中,仅在initState中初始化一次,避免每次build重新调用:
class TestPage extends StatefulWidget { const TestPage({super.key}); @override State<TestPage> createState() => TestPageState(); } class TestPageState extends State<TestPage> { // 声明Future变量,存储数据加载任务 late Future<List<Students>> _studentDataFuture; @override void initState() { super.initState(); // 仅在初始化时执行一次数据加载 _studentDataFuture = getJsonData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: ..., bottomNavigationBar: ..., body: FutureBuilder<List<Students>>( // 使用存储的Future,而非直接调用方法 future: _studentDataFuture, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return const Center(child: Text("加载中...")); default: if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { // 将加载完成的数据传递给BuildPage return BuildPage(students: snapshot.data!); } else { return const Center(child: Text('暂无数据')); } } }, ), ); } }
2. 修复DropdownButton重复值错误
确保Students类重写==运算符和hashCode,基于唯一标识(如学生ID)判断相等性:
class Students { final String id; // 学生唯一标识 final String name; // 其他字段 Students({required this.id, required this.name}); factory Students.fromJson(Map<String, dynamic> json) { return Students( id: json['id'], name: json['name'], // 解析其他字段 ); } // 重写==运算符,基于id判断相等 @override bool operator ==(Object other) => identical(this, other) || other is Students && runtimeType == other.runtimeType && id == other.id; // 重写hashCode,基于id生成 @override int get hashCode => id.hashCode; }
3. 优化BuildPage的DropdownButton体验
在BuildPage中,根据数据状态控制Dropdown的可用性,避免真机加载延迟导致的交互问题:
class BuildPage extends StatefulWidget { final List<Students> students; const BuildPage({super.key, required this.students}); @override State<BuildPage> createState() => _BuildPageState(); } class _BuildPageState extends State<BuildPage> { Students? selectedStudent; final TextEditingController _textController = TextEditingController(); @override Widget build(BuildContext context) { return Column( children: [ DropdownButton<Students>( value: selectedStudent, // 数据为空时禁用Dropdown enabled: widget.students.isNotEmpty, items: widget.students.map((student) { return DropdownMenuItem<Students>( value: student, child: Text(student.name), ); }).toList(), onChanged: (Students? newValue) { setState(() { selectedStudent = newValue; // 更新文本控制器内容 _textController.text = newValue?.name ?? ''; }); }, ), TextField(controller: _textController), ], ); } }
关键说明
- 存储Future到State变量:保证数据仅加载一次,避免rebuild时重复执行异步任务。
- 重写相等性方法:让DropdownButton能正确识别每个选项的唯一性,解决选中时的重复值校验错误。
- 控制Dropdown可用性:在数据加载完成前禁用控件,避免用户操作空列表,同时提升真机交互体验。
内容的提问来源于stack exchange,提问作者guguCara
相关产品推荐
相关产品推荐

