Getx 的 setState() 或 markNeedsBuild() 错误

Héc*_*tor 1 state-management dart flutter flutter-getx

我正在学习在 Flutter 中使用 Getx 库,我遇到了以下问题:想法是有一个 TextFields 列表,其中每个 TextFields 都是可水平扩展的,也就是说,它在屏幕上占用的大小由键入的单词的大小,随着单词大小的增加,TextField 的大小也会增加。如果我添加一行,通过FloatingActionButton,没有问题,我可以顺利打字,但是当我添加第二行时,出现以下错误:

\n
\n

\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90 捕获异常通过小部件库 \xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\ x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\ xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\ x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\x90\xe2\x95\ x90\xe2\x95\x90\xe2\x95\x90\n构建 NewLineKeyword(dirty) 时抛出以下断言:\n在构建期间调用 setState() 或 markNeedsBuild()。

\n
\n
\n

此 GetX 小部件无法标记为需要构建,因为框架已经在构建小部件的过程中。仅当小部件的祖先之一当前正在构建时,才可以将其标记为需要在构建阶段构建。允许此异常,因为框架在子窗口之前构建父窗口小部件,这意味着将始终构建脏后代。否则,框架可能不会在此构建阶段访问此小部件。\n调用 setState() 或 markNeedsBuild() 的小部件是:GetX<NewLineKeywordController>

\n
\n
\n

控制器: null\ntag: null\nhas builder\nstate: GetXState#ae1d4(controller: Instance of \'NewLineKeywordController\')\n当进行有问题的调用时当前正在构建的小部件是: NewLineKeyword\ndirty\n相关错误-导致小部件是\nNewLineKeyword

\n
\n

下图显示了运行时的情况:

\n

错误的图像

\n

下面是可水平扩展的 TextField 的代码:

\n
class NewLineKeyword extends GetView<NewLineKeywordController>{\n  final Key myKey;\n\n  const NewLineKeyword({ \n    Key? key, \n    required this.myKey, \n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final newLineKeywordController = Get.put(NewLineKeywordController());\n    newLineKeywordController.setNewLineSize(myKey, 60.0);\n\n    return GetX<NewLineKeywordController>(\n      builder: (_){\n        return Row(\n          children: <Widget>[\n            const SizedBox(\n              width: 7.5\n            ),\n            SizedBox(\n              width: newLineKeywordController.getNewLineSize(myKey),\n              child: TextField(\n                autofocus: true,\n                minLines: 1,\n                maxLines: 1,\n                decoration: const InputDecoration(\n                  constraints: BoxConstraints(minWidth: 15.0),\n                  border: InputBorder.none,\n                  contentPadding: EdgeInsets.only(bottom: 13.0),\n                  focusedBorder: UnderlineInputBorder(\n                    borderSide: BorderSide(color: Colors.blue)\n                  )\n                ),\n                style: const TextStyle(color: Colors.white, fontSize: 20.0),\n                onChanged: (String newValue){\n                  // get the exact size of the String\n                  final TextPainter textPainter = TextPainter(\n                    text: TextSpan(text: newValue, style: const TextStyle(fontSize: 20.0)),\n                    textDirection: TextDirection.ltr,\n                    textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,\n                  )..layout();\n\n                  newLineKeywordController.changeNewLineSize(myKey, textPainter.size.width + 2.0);\n                },\n                onSubmitted: (String newValue){\n                  // get the exact size of the String\n                  final TextPainter textPainter = TextPainter(\n                    text: TextSpan(text: newValue, style: const TextStyle(fontSize: 20.0)),\n                    textDirection: TextDirection.ltr,\n                    textScaleFactor: WidgetsBinding.instance.window.textScaleFactor,\n                  )..layout();\n\n                  newLineKeywordController.changeNewLineSize(myKey, textPainter.size.width + 2.0);\n                }\n              )\n            )\n          ]\n        );\n      }\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

下面是 TextField Getx 控制器代码:

\n
class NewLineKeywordController extends GetxController{\n  final RxMap<Key, double> _mapNewLineSize = <Key, double>{}.obs;\n\n  void setNewLineSize(Key key, double size){\n    _mapNewLineSize[key] = size;\n  }\n\n  void changeNewLineSize(Key key, double newSize){\n    if(_mapNewLineSize[key] != null){\n      _mapNewLineSize[key] = newSize;\n    }\n  }\n\n  double getNewLineSize(Key key){\n    if(_mapNewLineSize[key] != null){\n      return _mapNewLineSize[key]!;\n    }\n    else{\n      return 10.0;\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

根据所需的关键字,关键字小部件创建所需的小部件(该小部件的控制器为空):

\n
class Keywords extends GetView<KeywordsController>{\n  final String reservedKeyword;\n  final Key mykey;\n\n  const Keywords({\n    Key? key, \n    required this.reservedKeyword,\n    required this.mykey\n  }) : super(key: key);\n\n  @override\n  Widget build(BuildContext context){\n    switch(reservedKeyword){\n      case "newLine":\n        return NewLineKeyword(myKey: mykey);\n      //case "second option":\n        //return ... \n      //case "third option":\n        //return ...\n      default:\n        return throw NullThrownError();\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

下面的代码对应一行的Widget:

\n
class Line extends GetView<LineController> {\n  final Key myKey;\n\n  Line({Key? key, required this.myKey}) : super(key: key);\n\n  @override\n  final controller = Get.put(LineController());\n\n  @override\n  Widget build(BuildContext context) {\n    return GestureDetector(\n      child: GetX<LineController>(\n        builder: (_){\n          return Container(\n            color: controller.getIsSelected(myKey) ? const Color(0x327a7a7a) : Colors.transparent,\n            height: 35.0,\n            child: Row(\n              children: <Widget>[\n                const SizedBox(width: 10.0),\n                controller.getKeywords(myKey)\n              ]\n            )\n          );\n        }\n      ),\n      onLongPress: (){\n        controller.changeIsSelected(myKey);\n      }\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Line 小部件控制器的代码:

\n
class LineController extends GetxController{\n  final RxMap<Key, Keywords> _mapKeywords = <Key, Keywords>{}.obs;\n\n  void setKeywords(Key key, Keywords keyword){\n    _mapKeywords[key] = keyword;\n  }\n\n  void changeKeywords(Key key, Keywords newKeyword){\n    if(_mapKeywords[key] != null){\n      _mapKeywords[key] = newKeyword;\n    }\n  }\n\n  Keywords getKeywords(Key key){\n    if(_mapKeywords[key] != null){\n      return _mapKeywords[key]!;\n    }\n    else{\n      return Keywords(reservedKeyword: "newLine", mykey: key);\n    }\n  }\n  // set, get and change for isSelected\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
class Editor extends GetView<EditorController> {\n  const Editor({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n\n    ScrollController scrollCode = ScrollController();\n\n    final editorController = Get.put(EditorController());\n\n    List<Line> lineList = editorController.getLineList();\n\n    return Padding(\n      padding: const EdgeInsets.fromLTRB(0.0, 0.0, 10.0, 120.0),\n      child: GetX<EditorController>(\n        builder: (_){\n          return DraggableScrollbar.arrows(\n            controller: scrollCode,\n            child: ListView.builder(\n              controller: scrollCode,\n              itemCount: lineList.length,\n              itemBuilder: (BuildContext context, int index){\n                return lineList[index];\n              }\n            )\n          );\n        }\n      )\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

编辑Getx控制器:

\n
class EditorController extends GetxController{\n  final RxList<Line> _codeList = <Line>[].obs;\n\n  void addLine(Line value){\n    _codeList.add(value);\n  }\n\n  List<Line> getLineList(){\n    return _codeList;\n  }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
class ExpandableFab extends StatelessWidget {\n\n  const ExpandableFab({ Key? key }) : super(key: key);\n\n   @override\n   Widget build(BuildContext context) {\n    final lineController = Get.put(LineController());\n    final editorController = Get.put(EditorController());\n\n    Key mykey;\n\n    return FloatingActionButton(\n      child: const Icon(\n        Icons.add, \n      ),\n      onPressed: (){\n        mykey = GlobalKey();\n        lineController.setIsSelected(mykey, false);\n        lineController.setKeywords(mykey, Keywords(reservedKeyword: "newLine", mykey: mykey));\n        editorController.addLine(Line(myKey: mykey));\n      }\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我不太理解这个错误,因为它们是同一小部件​​的不同实例,因此可以毫无问题地创建它。我真的迷失在这个问题中,在这种情况下如何创建多个小部件?

\n

Abd*_*deh 7

这是因为您正在尝试在构建小部件树时更新可观察值,这可能是由于该方法引起的newLineKeywordController.setNewLineSize(myKey, 60.0);

要解决这个问题,您可以将setNewLineSize方法更改为:

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