Flutter中带Radio按钮的ListView点击卡顿性能优化问题
问题根因
当前写法直接在父组件调用setState,会触发父组件全量build,所有列表项无论是否修改都会重新走构建逻辑,100个列表项每次更新都重复构建,自然出现卡顿。
优化方案
方案1:将单个列表项抽为独立StatefulWidget
把每个问题对应的ListTile抽成独立的带状态组件,点击单选时只调用子组件自己的setState,仅刷新当前单个列表项,不会触发父页面全量重建。
示例代码:
// 独立的列表项组件 class QuestionItem extends StatefulWidget { final ItemModel question; final int index; const QuestionItem({super.key, required this.question, required this.index}); @override State<QuestionItem> createState() => _QuestionItemState(); } class _QuestionItemState extends State<QuestionItem> { @override Widget build(BuildContext context) { return ListTile( title: Text("${widget.index + 1}. ${widget.question.name}"), subtitle: Column( children: [ ListTile( onTap: () { setState(() { widget.question.checked = 'yes'; }); }, title: const Text("Yes"), leading: Radio( value: 'yes', groupValue: widget.question.checked, onChanged: (val) { setState(() { widget.question.checked = 'yes'; }); }), ), ListTile( title: const Text("No"), onTap: () { setState(() { widget.question.checked = 'no'; }); }, leading: Radio( value: 'no', groupValue: widget.question.checked, onChanged: (val) { setState(() { widget.question.checked = 'no'; }); }), ) ], ), ); } } // 父组件中使用ListView.builder加载 ListView.builder( itemCount: _questions.length, itemBuilder: (context, index) => QuestionItem( question: _questions[index], index: index, ), )
方案2:使用ValueNotifier + ValueListenableBuilder实现局部刷新(无需抽离组件)
如果不想抽离组件,可以修改ItemModel的checked属性为ValueNotifier类型,仅监听单个选项的状态变化,更新时只会刷新被包裹的Radio区域,完全不需要调用父组件的setState。
示例修改:
- 修改ItemModel定义:
class ItemModel { final String name; final int id; // 把checked改成ValueNotifier final ValueNotifier<String> checked; ItemModel({required this.name, required this.id, required String checked}) : checked = ValueNotifier(checked); }
- 列表项中使用ValueListenableBuilder包裹需要刷新的区域:
ListView.builder( itemCount: _questions.length, itemBuilder: (context, index) { final question = _questions[index]; return ListTile( title: Text("${index + 1}. ${question.name}"), subtitle: Column( children: [ ListTile( onTap: () => question.checked.value = 'yes', title: const Text("Yes"), leading: ValueListenableBuilder<String>( valueListenable: question.checked, builder: (context, value, child) => Radio( value: 'yes', groupValue: value, onChanged: (val) => question.checked.value = 'yes', ), ), ), ListTile( onTap: () => question.checked.value = 'no', title: const Text("No"), leading: ValueListenableBuilder<String>( valueListenable: question.checked, builder: (context, value, child) => Radio( value: 'no', groupValue: value, onChanged: (val) => question.checked.value = 'no', ), ), ) ], ), ); } )
通用优化项
- 必须使用
ListView.builder构建列表,不要直接把所有100个列表项提前生成塞进ListView的children属性,builder会自动懒加载,仅渲染当前屏幕可见的列表项,大幅降低初始渲染和更新的性能消耗。 - 所有静态不变的Widget都添加
const修饰,比如const Text("Yes"),避免不必要的重复构建。 - 合并重复逻辑:ListTile的onTap和Radio的onChanged功能完全一致,保留其中一个即可,减少冗余执行代码。
内容的提问来源于stack exchange,提问作者Geoff
相关产品推荐
相关产品推荐

