【问题标题】:setState() or markNeedsBuild called during build在构建期间调用 setState() 或 markNeedsBuild
【发布时间】:2018-05-15 11:52:23
【问题描述】:
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

我有两个错误:

1] 水平视口被赋予了无限的高度。 水平视口被赋予了无限量的垂直空间来展开。

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

请帮帮我。我正在创建这个应用程序,在每个图像上单击一个列表,如下所示。图片将排成一行,列表应显示在行下方。

谢谢。

【问题讨论】:

    标签: listview dart size flutter setstate


    【解决方案1】:

    在我的例子中,我在 build 方法完成构建小部件的过程之前调用了 setState 方法。

    如果您在完成build 方法之前显示snackBaralertDialog,以及在许多其他情况下,您可能会遇到此错误。因此,在这种情况下,您应该使用如下所示的回调函数:

    WidgetsBinding.instance.addPostFrameCallback((_){
    
      // Add Your Code here.
    
    });
    

    或者你也可以使用SchedulerBinding 这样做:

    SchedulerBinding.instance.addPostFrameCallback((_) {
    
      // add your code here.
    
      Navigator.push(
            context,
            new MaterialPageRoute(
                builder: (context) => NextPage()));
    });
    

    【讨论】:

    • 这应该是公认的答案。当我在 FutureBuilder(登录成功屏幕)中创建新的 MaterialPageRoute 时,我发生了这个错误。
    • 这行得通,谢谢!我在调用Navigator.of(context).popUntil 方法时遇到了同样的问题
    【解决方案2】:

    你的代码

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

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

    应该是

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

    这样一个函数(闭包)被传递给onPressed,执行时调用buildlist()

    【讨论】:

    • 我仍然收到错误:Horizo​​ntal viewport was given unbounded height 。这是什么意思?
    • 我想我必须把那个留给@Darky 或其他人。
    • @DivyangShah 您正在使用 Container 作为 Flexible 的父级,应该反过来。
    • 解决我的错误,不明白这个区别。也许有更多详细信息的链接?
    • @chengxcv5 查找“闭包”。不同之处在于,如果您将一个函数的引用分配给onPressed,则该函数可以由小部件在识别到点击时调用。问题中代码的问题在于,它没有传递函数,而是立即调用该函数,并将该函数的返回值分配给onPressed() =&gt; ... 使其成为没有名称(闭包)且此处没有参数的内联函数,调用时执行 buildlist(...)
    【解决方案3】:

    我也在构建过程中设置了状态,所以我将它推迟到下一个滴答声并且它起作用了。

    以前

    myFunction()
    

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

    【讨论】:

    • 谢谢,这对我有用。在我的情况下,我正在做 Infinite gridview,我试图在底部显示加载视图并多次调用 onBuild。但是使用这个解决方案,谢谢再次..
    • 最简单的方法。谢谢
    【解决方案4】:

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

    在我的代码中,我有两个类。一类 (B) 只是为我创建一个具有 onTap 功能的特殊小部件。应该由用户点击触发的功能在另一个类 (A) 中。所以我试图将A类的函数传递给B类的构造函数,以便我可以将它分配给onTap。

    不幸的是,我没有传递我调用它们的函数。 这是它的样子:

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

    显然,这是正确的方法:

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

    这解决了我的错误消息。 这个错误是有道理的,因为为了让 myFunction 能够工作 B 类的实例,所以我的构建必须首先完成(我在调用我的函数时显示了一个快餐栏)。

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

    【讨论】:

    • :) 哈哈哈第一行
    • 哈哈非常感谢,原来如此:D
    • 非常感谢。我没有意识到我犯了这个错误......整天都在寻找解决方案
    【解决方案5】:

    WidgetsBinding.instance.addPostFrameCallback 的问题在于,它不是一个包罗万象的解决方案。

    根据addPostFrameCallback的合同-

    为此帧结束安排回调。 [...] 此回调在一帧期间运行,就在持久帧之后 回调 [...]。如果一个帧正在进行中并且帧后回调没有 还没有执行,那么注册的回调仍然执行 帧期间。 否则执行注册的回调 在下一帧。

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

    此方法不适用于处理没有“当前帧”且颤振引擎空闲的情况。当然,在这种情况下,可以直接调用setState(),但同样,这也不会总是起作用 - 有时只有一个当前帧。


    谢天谢地,在 SchedulerBinding 中,也有解决这个小疣的方法 - SchedulerPhase

    让我们建立一个更好的setState,一个不会抱怨的。

    endOfFrameaddPostFrameCallback 的作用基本相同,只是它尝试在SchedulerPhase.idle 安排一个新帧,并且它使用异步等待而不是回调)

    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();
    

    【讨论】:

    • 哇,非常感谢!你的回答让我很开心。这正是我一直在寻找的:)
    【解决方案6】:

    当你使用 onWillpop 方法返回时会出现这种类型的错误

    onWillPop: onBackPressedWillPopeback());
    

    用这个替换

    onWillPop: () => onBackPressedWillPopeback());
    

    我相信你的错误会得到解决...如果有任何其他问题随时问我...

    【讨论】:

    【解决方案7】:

    如果是 InkWell,只需从 onTap() 中删除大括号 {},如果是 GestureDetector 或按钮等,只需从 onPressed() 中删除。

    【讨论】:

      【解决方案8】:

      就我而言,我使用的是 GetX 和 Slider。对于我的滑块,我直接使用了 Rx 变量而不是实际值。

      我以前的代码。

      RxDouble sliderValue = 1.0.obs;
      
      MySlider(
         value: this.sliderValue
      )
      

      我将代码更改为:

      MySlider(
         value: this.sliderValue.value
      )
      

      现在错误消失了。

      【讨论】:

        【解决方案9】:

        为避免此错误,请使用Navigator API 将对话框作为路由触发。在下面的示例中,有两个页面。第二页有一个在输入时显示的对话框。当用户通过单击第一页上的按钮请求第二页时,Navigator 会连续推送两条路由——一条用于第二页,另一条用于对话框。

        class FirstScreen extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                title: Text('First Screen'),
              ),
              body: Center(
                child: RaisedButton(
                  child: Text('Launch screen'),
                  onPressed: () {
                    // Navigate to the second screen using a named route.
                    Navigator.pushNamed(context, '/second');
                    // Immediately show a dialog upon loading the second screen.
                    Navigator.push(
                      context,
                      PageRouteBuilder(
                        barrierDismissible: true,
                        opaque: false,
                        pageBuilder: (_, anim1, anim2) => MyDialog(),
                      ),
                    );
                  },
                ),
              ),
            );
          }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-10-05
          • 2020-12-12
          • 2020-12-28
          • 2021-01-06
          • 2021-06-06
          • 2021-05-31
          • 2021-08-24
          相关资源
          最近更新 更多