I have a streambuilder that loads information from firestore. Then I use a listview.builder to display the information. The displayed information is being called by the function firstBuildItem or buildItem.
The functionality that I want is that when the user clicks on the container that is wrapped with a gesturedetector, I want another widget to be expanded below the main widget. I'm not sure how to achieve this with flutter because since each message is an item insider listview.builder, the boolean value needs to be local inside the function since every function has its own boolean that is checking for if the list item was clicked. I would like to find out how to fix my code so that each item responds when clicked inside the listview by expanding.
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,
),
then here is the builditem function:
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,
),
],
),
),
);
}
and then the expanded widget:
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
);
}
}
I solved the issue by turning firstBuildItem widget function into a stateful widget of it's own class. And then passing a key to the function. With this, setState worked without rebuilding the entire 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,
),
),