Flutter:如何单独控制列表中单个Widget的可见性?
问题描述
从Firebase读取数据生成Widget列表,点击某一项时希望仅切换该Widget的内容可见性,但当前用Visibility绑定全局的valueVisible变量,导致所有项的可见性同步变化,如何实现单独控制?
用户当前代码:
body: StreamBuilder<QuerySnapshot>( stream: _keyService.getKeys(), builder: (context, snapshot) { return !snapshot.hasData ? const Center(child: CircularProgressIndicator()) : ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context, index) { DocumentSnapshot mypost = snapshot.data!.docs[index]; return Padding( padding: EdgeInsets.all(size * 0.3), child: InkWell( onTap: () { valueVisible = !valueVisible; }, child: Container( decoration: BoxDecoration( color: ColorItems.mainColor, border: Border.all(width: 5, color: Colors.grey), borderRadius: BorderRadius.all(Radius.circular(20))), child: Padding( padding: EdgeInsets.all(size), child: Container( child: Row( children: [ Expanded( child: Text( "${mypost['key']}", style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), ), const Text( ": ", style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox( width: 20, ), Expanded( child: Visibility( visible: valueVisible, child: Text( "${mypost['value']}", style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), )) ], ), ), ), ), ), ); }, ); })
相关截图:

解决方案
方法一:用列表维护每个项的可见状态
核心思路是给每个列表项分配独立的状态变量,用布尔列表记录每个项的可见性:
- 在页面的State类中定义状态列表:
List<bool> _itemVisibility = [];
- 在StreamBuilder的builder中,数据加载完成后初始化列表(确保长度与文档数量一致,初始值可按需设置):
if (snapshot.hasData) { // 仅在数据变化时初始化,避免重复创建 if (_itemVisibility.length != snapshot.data!.docs.length) { _itemVisibility = List.filled(snapshot.data!.docs.length, false); } }
- 修改点击事件,切换对应索引的状态:
onTap: () { setState(() { _itemVisibility[index] = !_itemVisibility[index]; }); },
- 更新Visibility组件的visible属性:
Visibility( visible: _itemVisibility[index], child: Text( "${mypost['value']}", style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), )
修改后的完整代码片段:
// 页面State类中添加 List<bool> _itemVisibility = []; // ... body: StreamBuilder<QuerySnapshot>( stream: _keyService.getKeys(), builder: (context, snapshot) { if (snapshot.hasData) { if (_itemVisibility.length != snapshot.data!.docs.length) { _itemVisibility = List.filled(snapshot.data!.docs.length, false); } } return !snapshot.hasData ? const Center(child: CircularProgressIndicator()) : ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context, index) { DocumentSnapshot mypost = snapshot.data!.docs[index]; return Padding( padding: EdgeInsets.all(size * 0.3), child: InkWell( onTap: () { setState(() { _itemVisibility[index] = !_itemVisibility[index]; }); }, child: Container( decoration: BoxDecoration( color: ColorItems.mainColor, border: Border.all(width: 5, color: Colors.grey), borderRadius: BorderRadius.all(Radius.circular(20))), child: Padding( padding: EdgeInsets.all(size), child: Container( child: Row( children: [ Expanded( child: Text( "${mypost['key']}", style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), ), const Text( ": ", style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox( width: 20, ), Expanded( child: Visibility( visible: _itemVisibility[index], child: Text( "${mypost['value']}", style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), )) ], ), ), ), ), ), ); }, ); })
方法二:将列表项封装为独立的StatefulWidget
把每个列表项做成独立的有状态组件,让每个组件自己维护可见状态,代码耦合度更低:
- 创建独立的列表项组件:
class KeyItemWidget extends StatefulWidget { final String keyText; final String valueText; final double size; const KeyItemWidget({ super.key, required this.keyText, required this.valueText, required this.size, }); @override State<KeyItemWidget> createState() => _KeyItemWidgetState(); } class _KeyItemWidgetState extends State<KeyItemWidget> { bool _isVisible = false; @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.all(widget.size * 0.3), child: InkWell( onTap: () { setState(() { _isVisible = !_isVisible; }); }, child: Container( decoration: BoxDecoration( color: ColorItems.mainColor, border: Border.all(width: 5, color: Colors.grey), borderRadius: const BorderRadius.all(Radius.circular(20))), child: Padding( padding: EdgeInsets.all(widget.size), child: Row( children: [ Expanded( child: Text( widget.keyText, style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), ), const Text( ": ", style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(width: 20), Expanded( child: Visibility( visible: _isVisible, child: Text( widget.valueText, style: const TextStyle( color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), ), )) ], ), ), ), ), ); } }
- 修改原页面的ListView.builder部分:
ListView.builder( itemCount: snapshot.data!.docs.length, itemBuilder: (context, index) { DocumentSnapshot mypost = snapshot.data!.docs[index]; return KeyItemWidget( keyText: "${mypost['key']}", valueText: "${mypost['value']}", size: size, ); }, )
内容的提问来源于stack exchange,提问作者erdemsaray
相关产品推荐
相关产品推荐

