Listview.builder 的 builditem 函数中的 Flutter Setstate 未重新加载
Flutter Setstate inside Listview.builder's builditem function not reloading
我有一个从 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 小部件函数变成它自己的有状态小部件 class 解决了这个问题。然后将一个键传递给该函数。有了这个,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,
),
),
我有一个从 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 小部件函数变成它自己的有状态小部件 class 解决了这个问题。然后将一个键传递给该函数。有了这个,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,
),
),