Flutter Setstate inside Listview.builder's builditem function not reloading

牧云@^-^@ 提交于 2020-06-09 05:22:48

问题


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
    );
  }
}

回答1:


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,
                    ),

                ),


来源:https://stackoverflow.com/questions/61142775/flutter-setstate-inside-listview-builders-builditem-function-not-reloading

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!