我有一个从firestore加载信息的streambuilder。然后,我使用listview.builder来显示信息。显示的信息由函数firstBuildItem或buildItem调用。
我想要的功能是,当用户单击用手势检测器包装的容器时,我希望另一个小部件在主小部件下方展开。我不确定如何用flutter实现此目的,因为由于每个消息都是listview.builder内部的项目,因此布尔值必须是函数内部的局部值,因为每个函数都有自己的布尔值,用于检查是否单击了列表项。我想了解如何修复我的代码,以便在列表视图中通过展开单击时每个项目都能响应。
ListView.builder( padding: EdgeInsets.all(10.0), itemBuilder: (context, index) => index == 0 ? firstBuildItem(index, snapshot.data.documents[index]) : buildItem(index, snapshot.data.documents[index], snapshot.data.documents[index-1]), itemCount: snapshot.data.documents.length, reverse: true, controller: listScrollController, ),
然后是builditem函数:
Widget firstBuildItem(int index, DocumentSnapshot mainDocument) { ///If the box is expanded bool _isExpanded = false; ///Toggle the box to expand or collapse void _toggleExpand() { setState(() { _isExpanded = !_isExpanded; }); } return GestureDetector( onTap: _toggleExpand, child: Container( color: _isExpanded ? Colors.blueGrey : null, child: Column( children: <Widget>[ Column( children: <Widget>[ SizedBox(height: 20.0), Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ ChatDateHeader(timestamp: mainDocument['timestamp']), ], ), ], ), Column( children: <Widget>[ Row( children: <Widget>[ Container( child: Text( mainDocument['content'], style: TextStyle(color: primaryColor), ), padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0), ), ], mainAxisAlignment: MainAxisAlignment.end, ), getTime(mainDocument['timestamp'], true), ExpandedSection( expand: _isExpanded, child: getDate(mainDocument['timestamp'], true), ), SizedBox(height: 20.0), ], crossAxisAlignment: CrossAxisAlignment.end, ), ], ), ), ); }
然后展开的小部件:
class ExpandedSection extends StatefulWidget { final Widget child; final bool expand; ExpandedSection({this.expand = false, this.child}); @override _ExpandedSectionState createState() => _ExpandedSectionState(); } class _ExpandedSectionState extends State<ExpandedSection> with SingleTickerProviderStateMixin { AnimationController expandController; Animation<double> animation; @override void initState() { super.initState(); prepareAnimations(); } ///Setting up the animation void prepareAnimations() { expandController = AnimationController( vsync: this, duration: Duration(milliseconds: 500) ); Animation curve = CurvedAnimation( parent: expandController, curve: Curves.fastOutSlowIn, ); animation = Tween(begin: 0.0, end: 1.0).animate(curve) ..addListener(() { setState(() { }); } ); } @override void didUpdateWidget(ExpandedSection oldWidget) { super.didUpdateWidget(oldWidget); if(widget.expand) { expandController.forward(); } else { expandController.reverse(); } } @override void dispose() { expandController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SizeTransition( axisAlignment: 1.0, sizeFactor: animation, child: widget.child ); } }
我通过将firstBuildItem窗口小部件功能转换为它自己的类的有状态窗口小部件来解决了该问题。然后将一个键传递给该函数。这样,setState可以工作,而无需重建整个streambuilder。
ListView.builder( padding: EdgeInsets.all(10.0), itemCount: snapshot.data.documents.length, reverse: true, controller: listScrollController, itemBuilder: (context, index) => index == snapLength ? FirstChatBuildMessageItem( key: Key('counter-${index}'), id: id, peerAvatar: peerAvatar, index: index, mainDocument: snapshot.data.documents[index], listMessage: listMessage, ) : //index == 0 ? ChatBuildMessageItem( key: Key('counter-${index}'), id: id, peerAvatar: peerAvatar, index: index, mainDocument: snapshot.data.documents[index], previousDocument: snapshot.data.documents[index + 1], listMessage: listMessage, ), ),