setState()或markneedsbuild()错误

发布于 2025-02-03 19:33:28 字数 7914 浏览 4 评论 0原文

我正在学习使用flutter的getx库,我有以下问题:这个想法是要有一个文本列表,其中每个库都可以水平扩展,即,屏幕上的占用大小由大小给出随着单词大小的增加,键入单词的大小也会增加。如果我通过floatingactionbutton添加一行,没有问题,我可以平稳地输入,但是当我添加第二行时,会发生以下错误:

════════例外被小部件库捕获══════════════════════════════════ ═ 以下断言是构建newlineKeyword(肮脏): setState()或Markneedsbuild()在构建过程中调用。

这个getx小部件不能被标记为需要构建,因为框架已经在构建小部件的过程中。仅当其祖先之一目前正在建造时,才能将小部件标记为需要在构建阶段建造。允许使用此例外,因为该框架在孩子们面前建立了父母小部件,这意味着将始终建造一个肮脏的后代。否则,该框架可能不会在此构建阶段访问此小部件。 setState()或MarkneedSbuild()的小部件是:getX< newlineKeywordController>

控制器:null 标签:null 有建筑商 状态:getxstate#ae1d4(控制器:'newlineKeywordController'的实例) 当前构建有问题的电话时正在建造的小部件是:newlinekeyword 肮脏的 相关引起错误的小部件是 newlinekeyword

下图显示在运行时:

错误的图像

以下是水平扩展的代码textfield:

class NewLineKeyword extends GetView<NewLineKeywordController>{
  final Key myKey;

  const NewLineKeyword({ 
    Key? key, 
    required this.myKey, 
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final newLineKeywordController = Get.put(NewLineKeywordController());
    newLineKeywordController.setNewLineSize(myKey, 60.0);

    return GetX<NewLineKeywordController>(
      builder: (_){
        return Row(
          children: <Widget>[
            const SizedBox(
              width: 7.5
            ),
            SizedBox(
              width: newLineKeywordController.getNewLineSize(myKey),
              child: TextField(
                autofocus: true,
                minLines: 1,
                maxLines: 1,
                decoration: const InputDecoration(
                  constraints: BoxConstraints(minWidth: 15.0),
                  border: InputBorder.none,
                  contentPadding: EdgeInsets.only(bottom: 13.0),
                  focusedBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Colors.blue)
                  )
                ),
                style: const TextStyle(color: Colors.white, fontSize: 20.0),
                onChanged: (String newValue){
                  // get the exact size of the String
                  final TextPainter textPainter = TextPainter(
                    text: TextSpan(text: newValue, style: const TextStyle(fontSize: 20.0)),
                    textDirection: TextDirection.ltr,
                    textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
                  )..layout();

                  newLineKeywordController.changeNewLineSize(myKey, textPainter.size.width + 2.0);
                },
                onSubmitted: (String newValue){
                  // get the exact size of the String
                  final TextPainter textPainter = TextPainter(
                    text: TextSpan(text: newValue, style: const TextStyle(fontSize: 20.0)),
                    textDirection: TextDirection.ltr,
                    textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
                  )..layout();

                  newLineKeywordController.changeNewLineSize(myKey, textPainter.size.width + 2.0);
                }
              )
            )
          ]
        );
      }
    );
  }
}

下面是textfield getx控制器代码:

class NewLineKeywordController extends GetxController{
  final RxMap<Key, double> _mapNewLineSize = <Key, double>{}.obs;

  void setNewLineSize(Key key, double size){
    _mapNewLineSize[key] = size;
  }

  void changeNewLineSize(Key key, double newSize){
    if(_mapNewLineSize[key] != null){
      _mapNewLineSize[key] = newSize;
    }
  }

  double getNewLineSize(Key key){
    if(_mapNewLineSize[key] != null){
      return _mapNewLineSize[key]!;
    }
    else{
      return 10.0;
    }
  }
}

在所需的关键字上,关键字窗口小部件创建了所需的小部件(此窗口小部件的控制器是空的):

class Keywords extends GetView<KeywordsController>{
  final String reservedKeyword;
  final Key mykey;

  const Keywords({
    Key? key, 
    required this.reservedKeyword,
    required this.mykey
  }) : super(key: key);

  @override
  Widget build(BuildContext context){
    switch(reservedKeyword){
      case "newLine":
        return NewLineKeyword(myKey: mykey);
      //case "second option":
        //return ... 
      //case "third option":
        //return ...
      default:
        return throw NullThrownError();
    }
  }
}

下面的代码对应于一行的窗口小部件:

class Line extends GetView<LineController> {
  final Key myKey;

  Line({Key? key, required this.myKey}) : super(key: key);

  @override
  final controller = Get.put(LineController());

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: GetX<LineController>(
        builder: (_){
          return Container(
            color: controller.getIsSelected(myKey) ? const Color(0x327a7a7a) : Colors.transparent,
            height: 35.0,
            child: Row(
              children: <Widget>[
                const SizedBox(width: 10.0),
                controller.getKeywords(myKey)
              ]
            )
          );
        }
      ),
      onLongPress: (){
        controller.changeIsSelected(myKey);
      }
    );
  }
}

线窗口窗口窗口控制器的代码:

class LineController extends GetxController{
  final RxMap<Key, Keywords> _mapKeywords = <Key, Keywords>{}.obs;

  void setKeywords(Key key, Keywords keyword){
    _mapKeywords[key] = keyword;
  }

  void changeKeywords(Key key, Keywords newKeyword){
    if(_mapKeywords[key] != null){
      _mapKeywords[key] = newKeyword;
    }
  }

  Keywords getKeywords(Key key){
    if(_mapKeywords[key] != null){
      return _mapKeywords[key]!;
    }
    else{
      return Keywords(reservedKeyword: "newLine", mykey: key);
    }
  }
  // set, get and change for isSelected
}

下面的编辑器小部件是行列表所在的位置:

class Editor extends GetView<EditorController> {
  const Editor({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    ScrollController scrollCode = ScrollController();

    final editorController = Get.put(EditorController());

    List<Line> lineList = editorController.getLineList();

    return Padding(
      padding: const EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 120.0),
      child: GetX<EditorController>(
        builder: (_){
          return DraggableScrollbar.arrows(
            controller: scrollCode,
            child: ListView.builder(
              controller: scrollCode,
              itemCount: lineList.length,
              itemBuilder: (BuildContext context, int index){
                return lineList[index];
              }
            )
          );
        }
      )
    );
  }
}

编辑器getx控制器:

class EditorController extends GetxController{
  final RxList<Line> _codeList = <Line>[].obs;

  void addLine(Line value){
    _codeList.add(value);
  }

  List<Line> getLineList(){
    return _codeList;
  }

}

下面是在编辑器中创建新行的按钮:

class ExpandableFab extends StatelessWidget {

  const ExpandableFab({ Key? key }) : super(key: key);

   @override
   Widget build(BuildContext context) {
    final lineController = Get.put(LineController());
    final editorController = Get.put(EditorController());

    Key mykey;

    return FloatingActionButton(
      child: const Icon(
        Icons.add, 
      ),
      onPressed: (){
        mykey = GlobalKey();
        lineController.setIsSelected(mykey, false);
        lineController.setKeywords(mykey, Keywords(reservedKeyword: "newLine", mykey: mykey));
        editorController.addLine(Line(myKey: mykey));
      }
    );
  }
}

我不太了解此错误,因为它们是同一窗口的不同实例,因此可以是创建没有任何问题。我真的在这个问题上迷失了方向,在这种情况下,如何创建一个以上的小部件?

I'm learning to use the Getx library with Flutter and I have the following problem: the idea is to have a list of TextFields, in which each one is horizontally expandable, that is, its occupied size on the screen is given by the size of the typed word, as the word size increases, the TextField size also increases. If I add a line, through the FloatingActionButton, there is no problem, I can type smoothly, but when I add the second line, the following error occurs:

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building NewLineKeyword(dirty):
setState() or markNeedsBuild() called during build.

This GetX widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: GetX<NewLineKeywordController>

controller: null
tag: null
has builder
state: GetXState#ae1d4(controller: Instance of 'NewLineKeywordController')
The widget which was currently being built when the offending call was made was: NewLineKeyword
dirty
The relevant error-causing widget was
NewLineKeyword

The image below shows at runtime:

image of the error

Below is the code for the horizontally expandable TextField:

class NewLineKeyword extends GetView<NewLineKeywordController>{
  final Key myKey;

  const NewLineKeyword({ 
    Key? key, 
    required this.myKey, 
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final newLineKeywordController = Get.put(NewLineKeywordController());
    newLineKeywordController.setNewLineSize(myKey, 60.0);

    return GetX<NewLineKeywordController>(
      builder: (_){
        return Row(
          children: <Widget>[
            const SizedBox(
              width: 7.5
            ),
            SizedBox(
              width: newLineKeywordController.getNewLineSize(myKey),
              child: TextField(
                autofocus: true,
                minLines: 1,
                maxLines: 1,
                decoration: const InputDecoration(
                  constraints: BoxConstraints(minWidth: 15.0),
                  border: InputBorder.none,
                  contentPadding: EdgeInsets.only(bottom: 13.0),
                  focusedBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Colors.blue)
                  )
                ),
                style: const TextStyle(color: Colors.white, fontSize: 20.0),
                onChanged: (String newValue){
                  // get the exact size of the String
                  final TextPainter textPainter = TextPainter(
                    text: TextSpan(text: newValue, style: const TextStyle(fontSize: 20.0)),
                    textDirection: TextDirection.ltr,
                    textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
                  )..layout();

                  newLineKeywordController.changeNewLineSize(myKey, textPainter.size.width + 2.0);
                },
                onSubmitted: (String newValue){
                  // get the exact size of the String
                  final TextPainter textPainter = TextPainter(
                    text: TextSpan(text: newValue, style: const TextStyle(fontSize: 20.0)),
                    textDirection: TextDirection.ltr,
                    textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,
                  )..layout();

                  newLineKeywordController.changeNewLineSize(myKey, textPainter.size.width + 2.0);
                }
              )
            )
          ]
        );
      }
    );
  }
}

Below is the TextField Getx controller code:

class NewLineKeywordController extends GetxController{
  final RxMap<Key, double> _mapNewLineSize = <Key, double>{}.obs;

  void setNewLineSize(Key key, double size){
    _mapNewLineSize[key] = size;
  }

  void changeNewLineSize(Key key, double newSize){
    if(_mapNewLineSize[key] != null){
      _mapNewLineSize[key] = newSize;
    }
  }

  double getNewLineSize(Key key){
    if(_mapNewLineSize[key] != null){
      return _mapNewLineSize[key]!;
    }
    else{
      return 10.0;
    }
  }
}

Upon the desired keyword, the Keyword widget creates the desired widget(the controller of this widget is empty):

class Keywords extends GetView<KeywordsController>{
  final String reservedKeyword;
  final Key mykey;

  const Keywords({
    Key? key, 
    required this.reservedKeyword,
    required this.mykey
  }) : super(key: key);

  @override
  Widget build(BuildContext context){
    switch(reservedKeyword){
      case "newLine":
        return NewLineKeyword(myKey: mykey);
      //case "second option":
        //return ... 
      //case "third option":
        //return ...
      default:
        return throw NullThrownError();
    }
  }
}

The code below corresponds to the Widget for one line:

class Line extends GetView<LineController> {
  final Key myKey;

  Line({Key? key, required this.myKey}) : super(key: key);

  @override
  final controller = Get.put(LineController());

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: GetX<LineController>(
        builder: (_){
          return Container(
            color: controller.getIsSelected(myKey) ? const Color(0x327a7a7a) : Colors.transparent,
            height: 35.0,
            child: Row(
              children: <Widget>[
                const SizedBox(width: 10.0),
                controller.getKeywords(myKey)
              ]
            )
          );
        }
      ),
      onLongPress: (){
        controller.changeIsSelected(myKey);
      }
    );
  }
}

Code for the Line widget controller:

class LineController extends GetxController{
  final RxMap<Key, Keywords> _mapKeywords = <Key, Keywords>{}.obs;

  void setKeywords(Key key, Keywords keyword){
    _mapKeywords[key] = keyword;
  }

  void changeKeywords(Key key, Keywords newKeyword){
    if(_mapKeywords[key] != null){
      _mapKeywords[key] = newKeyword;
    }
  }

  Keywords getKeywords(Key key){
    if(_mapKeywords[key] != null){
      return _mapKeywords[key]!;
    }
    else{
      return Keywords(reservedKeyword: "newLine", mykey: key);
    }
  }
  // set, get and change for isSelected
}

The Editor widget below is where the Line list is:

class Editor extends GetView<EditorController> {
  const Editor({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    ScrollController scrollCode = ScrollController();

    final editorController = Get.put(EditorController());

    List<Line> lineList = editorController.getLineList();

    return Padding(
      padding: const EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 120.0),
      child: GetX<EditorController>(
        builder: (_){
          return DraggableScrollbar.arrows(
            controller: scrollCode,
            child: ListView.builder(
              controller: scrollCode,
              itemCount: lineList.length,
              itemBuilder: (BuildContext context, int index){
                return lineList[index];
              }
            )
          );
        }
      )
    );
  }
}

Editor Getx Controller:

class EditorController extends GetxController{
  final RxList<Line> _codeList = <Line>[].obs;

  void addLine(Line value){
    _codeList.add(value);
  }

  List<Line> getLineList(){
    return _codeList;
  }

}

Below is the button to create a new Line in Editor:

class ExpandableFab extends StatelessWidget {

  const ExpandableFab({ Key? key }) : super(key: key);

   @override
   Widget build(BuildContext context) {
    final lineController = Get.put(LineController());
    final editorController = Get.put(EditorController());

    Key mykey;

    return FloatingActionButton(
      child: const Icon(
        Icons.add, 
      ),
      onPressed: (){
        mykey = GlobalKey();
        lineController.setIsSelected(mykey, false);
        lineController.setKeywords(mykey, Keywords(reservedKeyword: "newLine", mykey: mykey));
        editorController.addLine(Line(myKey: mykey));
      }
    );
  }
}

I didn't understand this error very well because as they are different instances of the same widget, it could be created without any problems. I'm really lost in this problem, how can I create more than one widget in this case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

青瓷清茶倾城歌 2025-02-10 19:33:28

这是因为您正在尝试更新小部件树时可观察的值,
这可能是由于方法newlinekeywordcontroller.setNewlineSize(MyKey,60.0);

要解决此问题而引起的

void setNewLineSize(Key key, double size) async {
    await Future.delayed(Duration.zero);
    _mapNewLineSize[key] = size;
}

This is because you are trying to update an observable value while the widget tree is getting built,
this is probably caused because of the method newLineKeywordController.setNewLineSize(myKey, 60.0);

to solve this, you can change your setNewLineSize method to be:

void setNewLineSize(Key key, double size) async {
    await Future.delayed(Duration.zero);
    _mapNewLineSize[key] = size;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文