在 ListView 上构建期间调用 setState() 或 markNeedsBuild()

setState() or markNeedsBuild() called during build on ListView

所以我正在尝试重构我的 listView 逻辑。基本上我的 ListView 已经变得笨重 UI 逻辑,所以我决定,为什么不将 UI 逻辑的某些部分移动到另一个 class

这是我的代码 ListPage.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/ListTextArea.dart';
import 'package:sample_flutter_works/Model.dart';
import 'dart:convert';
import 'package:sample_flutter_works/RefreshTableContainer.dart';

class ListPage extends StatefulWidget {
  @override
  MyListPage createState() => MyListPage();
}

class MyListPage extends State<ListPage> {
  MessageList messageList;
  List<int> viewTimeInfo;
  ScrollController _controller;

  _scrollListener() {

  }

  @override
  void initState() {
     super.initState();
    SystemChrome.setPreferredOrientations(
        [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);

    _controller = ScrollController();
    _controller.addListener(_scrollListener);

    loadMessages(
        completionBlock: (dataSet) => {
              setState(() {
                messageList = dataSet;
              })
            });
  }

  void loadMessages({completionBlock}) async {
    var jsonString = await rootBundle.loadString('assets/Chat.json');
    final jsonResponse = json.decode(jsonString);
    if (jsonResponse != null) {
      completionBlock(MessageList.fromJSON(jsonResponse));
    } else {
      completionBlock(null);
    }
  }

  Widget listLayout() {
    return ListView.separated(
        padding: const EdgeInsets.all(8.0),
        itemCount: (messageList != null && messageList.msgList != null)
            ? messageList.msgList.length
            : 0,
        separatorBuilder: (context, index) => Divider(
              color: Colors.black,
              height: 4.0,
            ),
        itemBuilder: (BuildContext context, int index) {
          var msgValToSend =
              (messageList != null && messageList.msgList != null)
                  ? messageList.msgList[index]
                  : null;

          return Stack(
            children: <Widget>[
              IntrinsicHeight(
                child: Row(
                  children: <Widget>[
                    getTheImageLayout(msgValToSend),
                    new ListTextArea(
                        msg: msgValToSend,
                        didTapOnTextArea: tappedOnTextArea,
                        visibilityCheck: checkForVisibility)
                  ],
                ),
              )
            ],
          );
        });
  }


  tappedOnTextArea(Message msg) {
    var viewedInfo = this.viewTimeInfo;
    if (viewedInfo != null) {
      var indexOfTappedElement = viewedInfo.indexOf(msg.messageID);

      if (indexOfTappedElement != null && indexOfTappedElement != -1) {
        viewedInfo.removeAt(indexOfTappedElement);
      } else {
        viewedInfo.add(msg.messageID);
      }
    } else {
      viewedInfo = [msg.messageID];
    }

    setState(() {
              viewTimeInfo = viewedInfo;
            });
  }

  checkForVisibility(bool _visible, Message msg) {
    if (msg != null && this.viewTimeInfo != null) {
      var checkForIndex = this.viewTimeInfo.indexOf(msg.messageID);
      if (checkForIndex != null && checkForIndex != -1) {
        _visible = true;
      }
    }
  }


  Widget getTheImageLayout(Message msg) {
    return Expanded(
        flex: 2,
        child: Align(
            alignment: Alignment.topLeft,
            child: Padding(
              padding: EdgeInsets.fromLTRB(5, 2.5, 0, 0),
              child: Container(
                  color: Colors.red,
                  height: 50,
                  child: Row(
                    children: <Widget>[
                      userImageView(msg),
                    ],
                  )),
            )));
  }

  Widget userImageView(Message msg) {
    return Expanded(
        flex: 8,
        child: Align(
            alignment: Alignment.centerLeft,
            child: Container(
                width: 40.0,
                height: 40.0,
                decoration:
                    BoxDecoration(shape: BoxShape.circle, color: Colors.green),
                child: ClipOval(
                  child: Image.network(
                    (msg.msgUser.userPicUrl != null)
                        ? msg.msgUser.userPicUrl
                        : 'https://picsum.photos/250?image=9',
                    fit: BoxFit.fill,
                  ),
                ))));
  }

  Future<void> refreshTheChatTable() async {
    print(" This is where the logic of pulll 2 refresh must be written ");

    loadMessages(
        completionBlock: (dataSet) => {
              setState(() {
                messageList = dataSet;
              })
            });
  }

  @override
  Widget build(BuildContext context) {
    return new RefreshTableContainer(
      listLayout: listLayout(),
      pull2RefreshAction: refreshTheChatTable,
    );
  }
}

ListTextArea.dart

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:sample_flutter_works/Model.dart';

class ListTextArea extends StatelessWidget {

  Message msg;
  Function didTapOnTextArea;
  Function visibilityCheck;

  ListTextArea({
    this.msg,
    this.didTapOnTextArea,
    this.visibilityCheck
  });

  @override
  Widget build(BuildContext context) {
    return Expanded(
        flex: 8,
        child: GestureDetector(
          onTap: didTapOnTextArea(msg),
          child: Padding(
            padding: EdgeInsets.fromLTRB(0, 2.5, 10, 0),
            child: Column(
              children: getChildWidgetArray(msg) ,
            ),
          ),
        ));
  }

  List<Widget> getChildWidgetArray(Message msg) {

    var elementalArray = [
      Align(
        alignment: Alignment.topLeft,
        child: Text(
          (msg != null) ? msg.msgContent.content : "Data Loading",
          style: TextStyle(
            background: Paint()..color = Colors.orange,
          ),
        ),
      ),
      Spacer(), // Defaults to a flex of one.
      Align(
        alignment: Alignment.bottomRight,
        child: Text(
          'Date of sending',
          textDirection: TextDirection.rtl,
          style: TextStyle(
            background: Paint()..color = Colors.blue,
          ),
        ),
      )
    ];

    var _visible = false;
    visibilityCheck(_visible,msg);

    var timeInfo = AnimatedOpacity (
          opacity: _visible ? 1.0 : 0.0,
          duration: Duration(milliseconds: 500),
          child: Align(
            child: _visible ? (Align(alignment: Alignment.topLeft,child:Column(children: <Widget>[Text("Last Read :" + (msg.msgTimeInfo.lastReadInfo)),
                                      Text("Delievered :" + (msg.msgTimeInfo.deliveredInfo))],))): null));
        elementalArray.add(timeInfo);

    return elementalArray;
  }
}

错误如下:

我正在尝试做的(或者当整个代码在 ListPage.dart 中时我已经做过)是在 listView 中动态计算的单元格,每个单元格响应在更多数据中显示的点击操作。我完全不明白我在这里做错了什么。

我在 init 中调用了 setState,但在回调函数中。 statelesswidget ListTextArea 根本不会处理状态,但是 returns tapAction 到 StateFulWidget ListPage.dart.

那么为什么我会收到此错误。任何见解都会有所帮助。

问题出在 ListTextArea.dart 行

onTap: didTapOnTextArea(msg),

您正在构建方法中调用函数 didTapOnTextArea,而不是将其作为点击侦听器传递。您必须将其替换为

onTap: (){
  didTapOnTextArea(msg);
},

在我的例子中,错误发生在我在构建完成之前设置状态时,所以,我将它推迟到下一个 tick 并且它起作用了。

以前

myFunction()

新建

Future.delayed(Duration.zero, () async {
  myFunction();
});