Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

So I am trying to refactor my listView logic. Basically my ListView has become cumbersome with the UI logic , so I decided, why not move certain parts of the UI logic to another class

This is my code 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;
  }
}

The error is as follows: enter image description here

What I am trying to do ( or had done earlier on when the entire code was in ListPage.dart ) was dynamically calculated cells in a listView, each cell responding to a tap action that shows in more data. I don't understand what I did wrong here at all.

I called the setState in init but inside a callback function. The statelesswidget ListTextArea will not handle the state at all, but returns the tapAction to the StateFulWidget ListPage.dart.

So why am I getting this error. Any insights would be helpful.

like image 482
Debanjan Chakraborty Avatar asked Aug 06 '19 13:08

Debanjan Chakraborty


People also ask

Was thrown setState () or markNeedsBuild () called during build?

To Solve setState() or markNeedsBuild called during build A vertical renderflex overflowed Error You just need to use a call back function. Because Should be setState method call before the build method had completed the process of building the widgets and thats why you are facing this error.

How do you change the state in flutter?

setState method Null safety Notify the framework that the internal state of this object has changed. Whenever you change the internal state of a State object, make the change in a function that you pass to setState: setState(() { _myState = newValue; }); The provided callback is immediately called synchronously.


1 Answers

In my case, the error occurred when I was setting the state before build was complete, so, I deferred it to the next tick and it worked.

previously

myFunction()

New

Future.delayed(Duration.zero, () async {
  myFunction();
});
like image 173
alfiepoleon Avatar answered Sep 24 '22 07:09

alfiepoleon