在构建过程中调用setState()或markNeedsBuild


85
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),
    );
  }
}

由DartPad格式化;-)

我有两个错误:

1]为水平视口赋予了无限的高度。在水平视口中可以无限扩展垂直空间。

2] setState()或markNeedsBuild在构建期间调用垂直的renderflex溢出了99488个像素。

请帮我。我正在创建这个应用程序,在每个图像上单击一个列表,应在下面显示。图像将在一行中,并且列表应显示在该行的下方。

谢谢。

Answers:


-30

我已经用两个Refreshment项目重新创建了您要尝试执行的操作,您可能想重构内容并以更好的方式处理布局,但是为了简单起见,请尝试遵循此示例并将其与您正在尝试实现:

在此处输入图片说明

import "package:flutter/material.dart";

class LetsParty extends StatefulWidget {
  @override
  _LetsPartyState createState() => new _LetsPartyState();
}

class _LetsPartyState extends State<LetsParty> {
  Image _partyImage = new Image.network(
      "http://www.freshcardsgifts.co.uk/images/_lib/animal-party-greetings-card-3003237-0-1344698261000.jpg");

  final GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();

  List<List> list = [
    ["Pina Colada",
    "Bloody Mary",
    "Strawberry Flush",
    "Mango Diver",
    "Peach Delight"
    ],
    [
      "Absolute",
      "Smirnoff",
      "White Mischief",
      "Romanov",
      "Blender's Pride"
    ]
  ];

  List<String> visibleList = null;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      key: scaffoldKey,
      appBar: new AppBar(title: new Text("Let's Party"),),
      body: new ListView(
        // shrinkWrap: true,
        children: <Widget>[


          new Container (
              height: 150.0,
              child: new ListView.builder(
                  shrinkWrap: true,
                  scrollDirection: Axis.horizontal,
                  itemCount: 2, //add the length of your list here
                  itemBuilder: (BuildContext context, int index) {
                    return new Column(
                      children: <Widget>[

                        ///Flexible let's your widget flex in  the given space, dealing with the overflow you have
                        //  new Flexible(child: new FlatButton(onPressed: ()=>print("You pressed Image No.$index"), child: _partyImage)),
                        new Flexible (child: new Container(
                            child: new FlatButton(onPressed: () {
                              scaffoldKey.currentState.showSnackBar(
                                  new SnackBar(
                                      content: new Text(
                                          "You pressed Image No.$index")));

                              setState(() {
                                visibleList = list[index];
                              });
                            },
                                child: _partyImage),
                            width: 100.0,
                            height: 100.0
                        ),),
                        //Exact width and height, consider adding Flexible as a parent to the Container
                        new Text("Text$index"),
                      ],
                    );
                  })),

          new Column(children: visibleList==null?new List():new List.generate(5, (int i) {
            new ListTile(title: new Text(visibleList[i]),
              onTap: () =>
                  scaffoldKey.currentState.showSnackBar(new SnackBar(
                    content: new Text("Your choise is ${visibleList[i]}"),)),
            );
          }))
        ],),);
  }
}

88
这不应该是选择的答案。它所做的只是重写OP的代码以修复错误,而没有解决错误的原因或发生的原因,因此对于其他访问此页面具有相同或相似错误的人来说,这是无济于事的。
Abion47年

2
@ Abion47实际上,大多数颤抖的答案只是没有解释的代码
Peter Haddad

178

就我而言,在构建小部件的构建方法完成过程之前,我正在调用setState方法。

如果在完成构建方法之前显示小吃店或警报对话框,并且在许多其他情况下,则可能会遇到此错误。因此在这种情况下,请使用以下回调函数。

WidgetsBinding.instance.addPostFrameCallback((_){

  // Add Your Code here.

});

或者您也可以使用SchedulerBinding进行相同的操作。

SchedulerBinding.instance.addPostFrameCallback((_) {

  // add your code here.

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

29
thaaaaaaaaaaaaaaaaaaaaaananks maaan
Richard Willian

9
这应该是公认的答案。当我MaterialPageRouteFutureBuilder(登录成功屏幕)中创建新文件时,发生了此错误。
Phani Rithvij

感谢@Developine提供此解决方案。我用了您解决方案的第一部分。
Nilesh

工作正常,谢谢!调用Navigator.of(context).popUntil方法时,我遇到了同样的问题
damato

非常感谢您
N Sivaram

134

您的密码

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

执行buildlist()并将结果传递给onPressed,但这不是所需的行为。

它应该是

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

这样,将函数(关闭)传递给onPressed,执行时调用buildlist()


我仍然遇到错误:水平视口被赋予了无限的高度。这意味着什么 ?
Divyang Shah

我想我得把那个留给@Darky或其他人。
君特Zöchbauer

1
@DivyangShah,您正在使用Container作为Flexible的父级,否则应该相反。
aziza

1
我喜欢这个,仅重构了3个小时,就得到了您为我修复的此行错误,现在一切正常!
朱利安·奥托

29

我还在构建过程中设置了状态,因此,我将其推迟到下一个刻度,并且它起作用了。

先前

myFunction()

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

1
感谢这又是为我工作。在我的情况我在做无限GridView和我试图显示在底部加载视图和onBuild叫很多次。但随着该解决方案的工作,谢谢..
Tejas的特里维迪

2
最简单的方法。谢谢
Dani

2

由于一个非常愚蠢的错误,我收到了此错误,但也许有人在这里,因为他做了完全相同的事情...

在我的代码中,我有两个类。一个类(B)在那里为我创建了一个带有onTap函数的特殊小部件。用户轻触应触发的功能在另一类(A)中。因此,我试图将类A的功能传递给类B的构造函数,以便可以将其分配给onTap。

不幸的是,我没有传递函数,而是调用了它们。看起来是这样的:

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

显然,这是正确的方法:

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

这解决了我的错误信息。该错误是有道理的,因为为了使myFunction能够使用类B的实例,因此必须首先完成构建(调用函数时,我将显示一个小吃栏)。

希望有一天能对某人有所帮助!


:) haahaha第一线
Divyang Shah

0

问题WidgetsBinding.instance.addPostFrameCallback在于,这不是一个全面的解决方案。

根据addPostFrameCallback的合同-

在此帧结束时安排一个回调。[...]该回调在框架中,在持久性框架回调[...]之后运行。如果框架正在进行中,并且尚未执行框架后回调,则在框架期间仍将执行已注册的回调。否则,在下一帧期间执行注册的回调。

最后一行对我来说听起来像是一笔破坏交易。

该方法无法处理没有“当前帧”且颤振引擎处于空闲状态的情况。当然,在这种情况下,一个可以调用setState()直接,但同样,这不会永远工作-有时仅仅当前帧。


值得庆幸的是,在SchedulerBinding,也存在解决这个小疣- SchedulerPhase

让我们构建一个更好的setState,没有抱怨的东西。

endOfFrame与基本上具有相同的作用addPostFrameCallback,除了它尝试在处调度新帧SchedulerPhase.idle,并且它使用async-await而不是回调)

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;
}

坦白说,这也使控制流程更好,那就可以了

await someTask();

if (!await rebuild()) return;

await someOtherTask();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.