一尘不染

Listview.builder的builditem函数内的Flutter Setstate无法重新加载

flutter

我有一个从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
    );
  }
}

阅读 508

收藏
2020-08-13

共1个答案

一尘不染

我通过将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,
                    ),

                ),
2020-08-13