当前位置: 首页 > 面试题库 >

控件树锁定时调用Flutter setState()或markNeedsBuild()

燕烨
2023-03-14
问题内容

我很难找到此异常的来源,而且该应用程序非常复杂,因此很难显示代码的任何相关部分。这是发生错误时的存储库: Github
repo

我怀疑以下代码可能是肇事者:

Widget buildResultCard(Map result, BuildContext context) {
    String name = result["value"];
    String description =
        result["label"].replaceAll(new RegExp(r"<(?:.|\n)*?>"), "");
    TextEditingController controller = new TextEditingController(text: name);

    Function onPressed = () {
      showDialog(
          context: context,
          child: new AlertDialog(
            title: new Text("Name your schedule"),
            content: new TextField(
              autofocus: true,
              controller: controller,
            ),
            actions: <Widget>[
              new FlatButton(
                  onPressed: () {
                    String givenName = controller.text;
                    ScheduleMeta schedule = new ScheduleMeta(
                        givenName: givenName,
                        name: name,
                        type: _selectedChoice.value,
                        description: description);

                    scheduleStore
                        .dispatch(new AddScheduleAction(schedule: schedule));

                    scheduleStore.dispatch(
                        new SetCurrentScheduleAction(schedule: schedule));

                    fetchAllSchedules(scheduleStore.state.schedules)
                        .then((weeks) {
                      scheduleStore.dispatch(
                          new SetWeeksForCurrentScheduleAction(weeks: weeks));
                    });

                    Scaffold.of(context).showSnackBar(new SnackBar(
                          content: new Text("Added " + givenName),
                          action: new SnackBarAction(
                              label: "Undo",
                              onPressed: () {
                                scheduleStore.dispatch(
                                    new RemoveScheduleAction(schedule: name));

                                Scaffold.of(context).showSnackBar(new SnackBar(
                                      content: new Text(
                                          "Deleted " + givenName),
                                    ));
                              }),
                        ));

                    Navigator.of(context).pop();
                  },
                  child: new Text("Add")),
            ],
          ));
    };

    return new Card(
      child: new Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          new ListTile(
            leading: const Icon(Icons.schedule),
            title: new Text(name),
            subtitle: new Text(description),
            isThreeLine: true,
            dense: true,
          ),
          new ButtonTheme.bar(
            child: new ButtonBar(
              children: <Widget>[
                new FlatButton(
                    child: const Text('Add Schedule'),
                    onPressed: scheduleStore.state.schedules
                            .any((schedule) => schedule.name == name)
                        ? null
                        : onPressed)
              ],
            ),
          ),
        ],
      ),
    );
  }

显示对话框时出现错误,并且当用户按下对话框上的按钮时,两个Redux存储调度将彼此直接发送。对话框后面的UI订阅Redux存储中的更改。

我以为Dart / Flutter是单线程的,因此不会发生这样的冲突,在这种情况下,似乎线程在小部件上调用setState(),而另一个在小部件树上锁定了。

有没有一种方法可以检查小部件树是否被锁定,从而可以避免这种情况?

堆栈提供此信息:

I/flutter (13466): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (13466): The following assertion was thrown while finalizing the widget tree:
I/flutter (13466): setState() or markNeedsBuild() called when widget tree was locked.
I/flutter (13466): This _ModalScope widget cannot be marked as needing to build because the framework is locked.
I/flutter (13466): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (13466):   _ModalScope([LabeledGlobalKey<_ModalScopeState>#cb4cc]; state: _ModalScopeState#d4c02())
I/flutter (13466): 
I/flutter (13466): When the exception was thrown, this was the stack:
I/flutter (13466): #0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3250)
I/flutter (13466): #2      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3226)
I/flutter (13466): #3      State.setState (package:flutter/src/widgets/framework.dart:1072)
I/flutter (13466): #4      _ModalScopeState._routeSetState (package:flutter/src/widgets/routes.dart:473)
I/flutter (13466): #5      ModalRoute.setState (package:flutter/src/widgets/routes.dart:552)
I/flutter (13466): #6      ModalRoute.changedInternalState (package:flutter/src/widgets/routes.dart:889)
I/flutter (13466): #7      TransitionRoute&&LocalHistoryRoute.removeLocalHistoryEntry (package:flutter/src/widgets/routes.dart:317)
I/flutter (13466): #8      LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:267)
I/flutter (13466): #9      DrawerControllerState.dispose (package:flutter/src/material/drawer.dart:147)
I/flutter (13466): #10     StatefulElement.unmount (package:flutter/src/widgets/framework.dart:3550)
I/flutter (13466): #11     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1626)
I/flutter (13466): #12     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #13     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #14     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #15     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #16     MultiChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4421)
I/flutter (13466): #17     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #18     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #19     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #20     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #21     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #22     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #23     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #24     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #25     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4321)
I/flutter (13466): #26     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #27     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #28     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #29     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #30     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #31     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4321)
I/flutter (13466): #32     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #33     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #34     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #35     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #36     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #37     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #38     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #39     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #40     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #41     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #42     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #43     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #44     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #45     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1624)
I/flutter (13466): #46     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3427)
I/flutter (13466): #47     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1622)
I/flutter (13466): #48     _InactiveElements._unmountAll (package:flutter/src/widgets/framework.dart:1636)
I/flutter (13466): #49     BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:2228)
I/flutter (13466): #50     BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2060)
I/flutter (13466): #51     BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:2227)
I/flutter (13466): #52     BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:505)
I/flutter (13466): #53     BindingBase&SchedulerBinding&GestureBinding&ServicesBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:189)
I/flutter (13466): #54     BindingBase&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:688)
I/flutter (13466): #55     BindingBase&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:636)
I/flutter (13466): #56     _drawFrame (file:///b/build/slave/Linux_Engine/build/src/flutter/lib/ui/hooks.dart:70)
I/flutter (13466): (elided one frame from class _AssertionError)
I/flutter (13466): ════════════════════════════════════════════════════════════════════════════════════════════════════

问题答案:

这不是线程问题。此错误表示您setState在构建阶段正在调用。

一个典型的例子如下:

Widget build(BuildContext context) {
    myParentWidgetState.setState(() { print("foo"); });
    return Container();
}

但是setState调用可能不太明显。例如,a Navigator.pop(context)setState内部执行。因此,以下内容:

Widget build(BuildContext context) {
    Navigator.pop(context);
    return Container();
}

也不行。

查看stacktrace,似乎与Navigator.pop(context) 模态同时尝试使用新数据进行更新。



 类似资料:
  • Tabris.js app的UI界面由原生控件组成。这些控件在原生代码里执行,并用JavaScript对象来描述。为了在屏幕上可见,每个控件都必须有一个父控件。可以使用 appendTo 方法向父控件添加控件。 UI根节点 所有控件的顶级父控件用对象 ui 表示。该对象包含一些固定的子控件,用于表示app用户界面的不同部分: ui.statusBar - 显示时间和一些系统图标 ui.naviga

  • 我有一个具有以下目录结构的S3位置,其顶部创建了一个配置单元表: 假设我有一个Spark程序,它使用下面的代码行将数据跨多个分区写入上面的表位置: spark在写入S3位置时是否锁定数据文件? 我们如何使用Spark作为ETL工具来处理这样的并发情况?

  • 用清晰的层级结构展示信息,可展开或折叠。 基础用法 基础的树形结构展示。 demo <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree> <script> export default { data() { return { data: [{

  • Tree 树形控件 用清晰的层级结构展示信息,可展开或折叠。 基础用法 基础的树形结构展示。 ::: demo 基础的树形结构展示 constructor(props) { super(props); this.state = { data: [{ label: '一级 1', children: [{ label: '二级 1-1',

  • 用清晰的层级结构展示信息,可展开或折叠。 基础用法 基础的树形结构展示。 <el-tree [model]="data"> </el-tree> <script type="text"> // in Component data: any = [{ label: '一级 1', children: [{ label: '二级 1-1', children: [{

  • Tree 树形控件 用清晰的层级结构展示信息,可展开或折叠。 基础用法 基础的树形结构展示。 demo <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree> <script> export default { data() { return {

  • 问题内容: 我们的一位客户正在使用某些Novell安全软件,有时会锁定我们的软件创建的某些.class文件。发生这种情况时,这会给他们带来一些麻烦的问题,我正在尝试研究一种变通办法,我们可以将其添加到错误处理中以解决此问题。我想知道java api中是否有任何调用可用于检测文件是否已锁定,如果已锁定,则将其解锁。 问题答案: 在尝试写入文件之前,您可以使用File.canWrite()检查Java

  • 问题内容: 我在这里思考:如果您有2个线程执行需要同步的FAST操作,那么非阻塞方法不是比阻塞/上下文切换方法更快/更好的方法吗? 非阻塞的意思是: while(true){如果(checkAndGetTheLock())中断;} 如果您有太多线程在锁中循环,我唯一想到的就是饥饿(CPU耗尽)。 如何平衡一种方法与另一种方法? 问题答案: 以下是 Java Concurrency in Pract