setState() or markNeedsBuild called during build

161,693

Solution 1

In my case I was calling the setState method before the build method had completed the process of building the widgets.

You can face this error if you are showing a snackBar or an alertDialog before the completion of the build method, as well as in many other cases. So, in such cases you should use a call back function as shown below:

WidgetsBinding.instance.addPostFrameCallback((_){

  // Add Your Code here.

});

or you can also use SchedulerBinding which does the same:

SchedulerBinding.instance.addPostFrameCallback((_) {

  // add your code here.

  Navigator.push(
        context,
        new MaterialPageRoute(
            builder: (context) => NextPage()));
});

Solution 2

Your code

onPressed: buildlist('button'+index.toString()),

executes buildlist() and passes the result to onPressed, but that is not the desired behavior.

It should be

onPressed: () => buildlist('button'+index.toString()),

This way a function (closure) is passed to onPressed, that when executed, calls buildlist()

Solution 3

I was also setting the state during build, so, I deferred it to the next tick and it worked.

previously

myFunction()

New

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

Solution 4

The problem with WidgetsBinding.instance.addPostFrameCallback is that, it isn't an all encompassing solution.

As per the contract of addPostFrameCallback -

Schedule a callback for the end of this frame. [...] This callback is run during a frame, just after the persistent frame callbacks [...]. If a frame is in progress and post-frame callbacks haven't been executed yet, then the registered callback is still executed during the frame. Otherwise, the registered callback is executed during the next frame.

That last line sounds like a deal-breaker to me.

This method isn't equipped to handle the case where there is no "current frame", and the flutter engine is idle. Of course, in that case, one can invoke setState() directly, but again, that won't always work - sometimes there just is a current frame.


Thankfully, in SchedulerBinding, there also exists a solution to this little wart - SchedulerPhase

Let's build a better setState, one that doesn't complain.

(endOfFrame does basically the same thing as addPostFrameCallback, except it tries to schedule a new frame at SchedulerPhase.idle, and it uses async-await instead of callbacks)

Future<bool> rebuild() async {
  if (!mounted) return false;

  // if there's a current frame,
  if (SchedulerBinding.instance.schedulerPhase != SchedulerPhase.idle) {
    // wait for the end of that frame.
    await SchedulerBinding.instance.endOfFrame;
    if (!mounted) return false;
  }

  setState(() {});
  return true;
}

This also makes for nicer control flows, that frankly, just work.

await someTask();

if (!await rebuild()) return;

await someOtherTask();

Solution 5

I recieved this error due to a pretty dumb mistake, but maybe someone is here because he did the exact same thing...

In my code i have two classes. One class (B) is just there to create me a special widget with a onTap function. The function that should get triggered by the user tap was in the other class (A). So i was trying to pass the function of class A to the constructor of class B, so that i could assign it to the onTap.

Unfortunatly, instead of passing the functions i called them. This is what it looked like:

ClassB classBInstance = ClassB(...,myFunction());

Obviously, this is the correct way:

ClassB classBInstance = classB(...,myFunction);

This solved my error message. The error makes sense, because in order for myFunction to work the instance of class B and so my build had to finish first (I am showing a snackbar when calling my function).

Hope this helps someone one day!

Share:
161,693

Related videos on Youtube

Divyang Shah
Author by

Divyang Shah

I am passionate about coding and all technologies in general . I believe from my experience I can optimise anything and everything over a period of time!!

Updated on December 25, 2021

Comments

  • Divyang Shah
    Divyang Shah over 2 years
    class MyHome extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => new MyHomePage2();
    }
    
    class MyHomePage2 extends State<MyHome> {
      List items = new List();
    
      buildlist(String s) {
        setState(() {
          print("entered buildlist" + s);
          List refresh = new List();
          if (s == 'button0') {
            refresh = [
              new Refreshments("Watermelon", 250),
              new Refreshments("Orange", 275),
              new Refreshments("Pine", 300),
              new Refreshments("Papaya", 225),
              new Refreshments("Apple", 250),
            ];
          } else if (s == 'button1') {
            refresh = [
              new Refreshments("Pina Colada", 250),
              new Refreshments("Bloody Mary", 275),
              new Refreshments("Long Island Ice tea", 300),
              new Refreshments("Screwdriver", 225),
              new Refreshments("Fusion Cocktail", 250),
            ];
          } else if (s == 'button2') {
            refresh = [
              new Refreshments("Virgin Pina Colada", 250),
              new Refreshments("Virgin Mary", 275),
              new Refreshments("Strawberry Flush", 300),
              new Refreshments("Mango Diver", 225),
              new Refreshments("Peach Delight", 250),
            ];
          } else {
            refresh = [
              new Refreshments("Absolute", 250),
              new Refreshments("Smirnoff", 275),
              new Refreshments("White Mischief", 300),
              new Refreshments("Romanov", 225),
              new Refreshments("Blender's Pride", 250),
            ];
          }
    
          for (var item in refresh) {
            items.add(new ItemsList(item));
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        var abc = MediaQuery.of(context).size;
    
        print(abc.width);
    
        var width = abc.width / 4;
    
        Text text = new Text("Dev");
        Text text2 = new Text("Sneha");
        Text text3 = new Text("Prashant");
        Text text4 = new Text("Vikesh");
    
        var pad = const EdgeInsets.all(10.0);
    
        Padding pad1 = new Padding(child: text, padding: pad);
        Padding pad2 = new Padding(child: text2, padding: pad);
        Padding pad3 = new Padding(child: text3, padding: pad);
        Padding pad4 = new Padding(child: text4, padding: pad);
    
        ListView listView = new ListView(children: <Widget>[
          new Image.asset('images/party.jpg'),
          pad1,
          pad2,
          pad3,
          pad4
        ]);
    
        Drawer drawer = new Drawer(child: listView);
    
        return new Scaffold(
          drawer: drawer,
    
          appBar: new AppBar(
            title: new Text('Booze Up'),
          ),
          body: new Column(children: <Widget>[
            new ListView.builder(
              scrollDirection: Axis.horizontal,
              itemCount: 4,
              itemBuilder: (BuildContext context, int index) {
                return new Column(children: <Widget>[
                  new Container(
                    child: new Flexible(
                        child: new FlatButton(
                      child: new Image.asset('images/party.jpg',
                          width: width, height: width),
                      onPressed: buildlist('button' + index.toString()),
                    )),
                    width: width,
                    height: width,
                  )
                ]);
              },
            ),
            new Expanded(
                child: new ListView(
              padding: new EdgeInsets.fromLTRB(10.0, 10.0, 0.0, 10.0),
              children: items,
              scrollDirection: Axis.vertical,
            )),
          ]),
    
          floatingActionButton: new FloatingActionButton(
            onPressed: null,
            child: new Icon(Icons.add),
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    }
    
    class Refreshments {
      String name;
      int price;
    
      Refreshments(this.name, this.price);
    }
    
    class ItemsList extends StatelessWidget {
      final Refreshments refreshments;
    
      ItemsList(this.refreshments);
    
      @override
      Widget build(BuildContext context) {
        return new ListTile(
          onTap: null,
          title: new Text(refreshments.name),
        );
      }
    }
    

    Full code

    I am having two errors:

    1] Horizontal viewport was given unbounded height . A horizontal viewport was given an unlimited amount of vertical space in which to expand.

    2] setState() or markNeedsBuild called during build A vertical renderflex overflowed by 99488 pixels.

    Please help me with it . I am creating this app where on each image click a list should be shown below . The images will be in a row and the list should be shown below the row.

    Thank you.

  • Divyang Shah
    Divyang Shah over 6 years
    I am still getting the error: Horizontal viewport was given unbounded height . What does that mean ?
  • Günter Zöchbauer
    Günter Zöchbauer over 6 years
    I guess I have to leave that one to @Darky or someone else.
  • Shady Aziza
    Shady Aziza over 6 years
    @DivyangShah you are using a Container as the parent of Flexible, it should be the other way around.
  • Phani Rithvij
    Phani Rithvij about 4 years
    This should be the accepted answer. This error occurred to me when I was creating a new MaterialPageRoute in a FutureBuilder (a login success screen).
  • damato
    damato almost 4 years
    This is working, thanks! I was getting the same issue when calling Navigator.of(context).popUntil method
  • Tejas Trivedi
    Tejas Trivedi over 3 years
    Thanks this is working for me.. In my case I'm doing Infinite gridview and I was trying to show loading view at bottom and onBuild called to many times.. But working with this solution, Thanks again..
  • Divyang Shah
    Divyang Shah over 3 years
    :) haahaha for the first line
  • Dani
    Dani over 3 years
    easiest way. Thanks
  • DarkMath
    DarkMath about 3 years
    Wow, thank you so much! Your answer made my day. That's exactly what I was looking for :)
  • Nicolas Degen
    Nicolas Degen about 3 years
    haha thanks so much, that was exactly it:D
  • Vega
    Vega almost 3 years
    This answer was already given: stackoverflow.com/a/47592505/5468463
  • Aysha Hamna
    Aysha Hamna over 2 years
    Thank you so much. I didn't realize I had done this mistake... was searching a solution all day long
  • chengxcv5
    chengxcv5 over 2 years
    resolve my error, does't understand this difference . maybe there's link to more detail about that?
  • Günter Zöchbauer
    Günter Zöchbauer over 2 years
    @chengxcv5 look up "closures". The difference is that if you assign a reference to a function to onPressed, that function can be called by the widget when it recognizes a tap. The problem with the code in the question is, that it doesn't pass a function, instead the function is called immediately and the return value of that function is assigned to onPressed. The () => ... makes it an inline function without a name (closure) and here without parameters, that when called executes buildlist(...)
  • Naveen Katragadda
    Naveen Katragadda about 2 years
    Important distinction here is - passing the function itself versus it's return value. thank you..this worked for me. Also here's a link explaining what a function "Closure" is - Function defined inside another function (parent) often have unrestricted access to the local variables of the parent function. Some languages allow this and Dart is one of them. medium.com/flutter-community/…
  • 1housand
    1housand about 2 years
    this works also: Future(() async { myFunction(); });