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

时间:2019-08-06 13:39:25

标签: flutter flutter-layout statelesswidget

所以我试图重构listView逻辑。基本上,我的ListView变得不方便使用UI逻辑,所以我决定为什么不将UI逻辑的某些部分移至另一个类

这是我的代码 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;
  }
}

错误如下: enter image description here

我正在尝试执行的操作(或之前在ListPage.dart中包含整个代码时所做的操作)是在listView中动态计算的单元格,每个单元格都响应显示更多数据的轻击动作。我根本不明白我在这里做错了什么。

我在初始化中但在回调函数内部调用了setState。无状态小部件ListTextArea根本不会处理状态,但会将tapAction返回给StateFulWidget ListPage.dart。

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

3 个答案:

答案 0 :(得分:1)

对于我来说,该错误是在构建完成之前设置状态时发生的,因此,我将其推迟到下一个刻度,并且可以正常工作。

以前

myFunction()

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

答案 1 :(得分:0)

问题出在ListTextArea.dart行

# define a check function (a converter from string to bool):
def check_quotes(val):
    stripped= val.strip()
    return stripped.startswith('"') & stripped.endswith('"')

# create a converter dict (just use a dict comprehension 
# if you don't know the column names, just make sure you
# chose a range at least as large as you have columns in
# your files (if your range is larger, it doesn't hurt)
conv_dict= {i: check_quotes for i in range(100)}
df= pd.read_csv('yourfile.csv', sep=';', index_col=[0], converters=conv_dict, quoting= csv.QUOTE_NONE)

# if the file is consistently quoted, the following line prints True
df.any().any()

您在构建方法中调用函数onTap: didTapOnTextArea(msg), ,而不是将其作为Tap监听器传递。您必须将其替换为

didTapOnTextArea

答案 2 :(得分:0)

InkWell(
    onTap: () => Navigator.push(context,
        MaterialPageRoute(builder: (context) =>
            ChangeNotifierProvider<AlertHistoryProvider>(
                create: (_) => AlertHistoryProvider(),
                child: AlertsHistory()
            ),
        )
    ),
    child : Text('Alerts History')),

我如上所述尝试过,但仍然收到SetState错误。具有讽刺意味的是,它只会在此流程中引起问题,并且在所有其他流程中都可以正常工作...困惑