Flutter中StatefulWidget下ListView点击图标不更新问题排查
问题:Flutter ListView点击ListTile后,收藏图标状态不更新
我正在制作一个学习用Demo,在Demo中添加了ListView,希望点击ListTile时切换Icons.favorite的激活与未激活状态。以下是我的代码:
import 'package:flutter/material.dart'; void main() => runApp(new RandomWordApp()); class RandomWordApp extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new MaterialApp( title: "Random App", home: new RandomWordHome(), ); } } class RandomWordHome extends StatefulWidget { @override createState() => new RandomState(); } class RandomState extends State<RandomWordHome> { @override Widget build(BuildContext context) { // TODO: implement build return new Scaffold( appBar: new AppBar( title: new Text("Random Word"), ), body: setupListView(), ); } Widget setupListView() { final _arrayOfData = [ { "name": "Amit Patel", "address": "Junagadh" }, {"name": "Arjun Jain", "address": "Madhya Pradesh"}, {"name": "Ajay Shah", "address": "Limbadi"}, {"name": "Ankur Patel", "address": "Visanagar"}, {"name": "Soheb Ansari", "address": "Ahmedabad"} ]; final _arraySelectedRowStatus = List.filled(5, 0); return new ListView.builder( padding: new EdgeInsets.all(10.0), itemCount: _arrayOfData.length, itemBuilder: (context, i) { return new ListTile( title: new Text(_arrayOfData[i]["name"]), subtitle: new Text(_arrayOfData[i]["address"]), leading: new CircleAvatar( backgroundColor: Colors.blue, child: new Text(_arrayOfData[i]["name"][0])), trailing: new Icon( (_arraySelectedRowStatus[i] == 0 ? Icons.favorite_border : Icons.favorite), color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red), ), onTap: () { setState( () { _arraySelectedRowStatus[i] = (_arraySelectedRowStatus[i] == 0 ? 1 : 0); Scaffold.of(context).showSnackBar(new SnackBar( content: new Text("You are selecting at " + i.toString()))); }, ); }, ); }); } }
执行print(_arrayOfData[i]["name"] + " - " + _arraySelectedRowStatus[i].toString());能得到预期输出,但Icons.favorite图标并未改变,怀疑是ListView未重载,请问我哪里出错了?请指引正确方向。
问题分析与解决方案
核心问题
你遇到的问题根源在于状态变量的作用域和生命周期:
_arraySelectedRowStatus是在setupListView()方法内部定义的局部变量,每次调用setState()触发build()方法重新执行时,setupListView()会被再次调用,这个列表会被重新初始化为List.filled(5, 0)——也就是说,你每次点击修改的状态都会被重置,界面自然不会显示更新后的图标。- 同理,
_arrayOfData作为静态数据,虽然不会影响状态,但放在方法内部每次build都会重新创建,也会造成不必要的性能损耗。
修复方案
把_arrayOfData和_arraySelectedRowStatus提升为RandomState类的成员变量,这样它们的生命周期就和State实例绑定,不会在每次build时被重置:
修改后的完整代码:
import 'package:flutter/material.dart'; void main() => runApp(new RandomWordApp()); class RandomWordApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: "Random App", home: new RandomWordHome(), ); } } class RandomWordHome extends StatefulWidget { @override createState() => new RandomState(); } class RandomState extends State<RandomWordHome> { // 把数据和状态变量移到State类的成员位置 final _arrayOfData = [ { "name": "Amit Patel", "address": "Junagadh" }, {"name": "Arjun Jain", "address": "Madhya Pradesh"}, {"name": "Ajay Shah", "address": "Limbadi"}, {"name": "Ankur Patel", "address": "Visanagar"}, {"name": "Soheb Ansari", "address": "Ahmedabad"} ]; late List<int> _arraySelectedRowStatus; // 在initState中初始化状态变量,确保只执行一次 @override void initState() { super.initState(); _arraySelectedRowStatus = List.filled(_arrayOfData.length, 0); } @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("Random Word"), ), body: setupListView(), ); } Widget setupListView() { return new ListView.builder( padding: new EdgeInsets.all(10.0), itemCount: _arrayOfData.length, itemBuilder: (context, i) { return new ListTile( title: new Text(_arrayOfData[i]["name"]!), subtitle: new Text(_arrayOfData[i]["address"]!), leading: new CircleAvatar( backgroundColor: Colors.blue, child: new Text(_arrayOfData[i]["name"]![0])), trailing: new Icon( (_arraySelectedRowStatus[i] == 0 ? Icons.favorite_border : Icons.favorite), color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red), ), onTap: () { setState( () { _arraySelectedRowStatus[i] = (_arraySelectedRowStatus[i] == 0 ? 1 : 0); ScaffoldMessenger.of(context).showSnackBar(new SnackBar( content: new Text("You are selecting at " + i.toString()))); }, ); }, ); }); } }
额外优化点
- 添加了
!来处理空安全(Dart 2.12+的要求),避免编译警告。 - 把
Scaffold.of(context)替换成了ScaffoldMessenger.of(context)——这是Flutter新版本中推荐的SnackBar调用方式,旧的Scaffold.of已经被标记为过时。 - 使用
initState()初始化_arraySelectedRowStatus,确保它只在State创建时初始化一次,而非依赖硬编码的长度,更灵活。
这样修改后,点击ListTile时,状态会被正确保存,setState()触发build后,ListView会根据更新后的_arraySelectedRowStatus渲染对应的图标状态。
内容的提问来源于stack exchange,提问作者Govaadiyo
相关产品推荐
相关产品推荐

