Error Nested ListView.builder() with GetX

huangapple go评论58阅读模式
英文:

Error Nested ListView.builder() with GetX

问题

我正在尝试使用GetX创建一个示例,其中有以下结构:

一个HomePage,其中有一个Widget1列表,每个Widget1都有一个Widget2列表。

我遇到了两个问题:

  • 第一个问题是,我只能添加一个Widget1,当我尝试添加第二个时,出现以下错误:

    在构建Widget1(dirty)时引发了以下断言:
    在构建期间调用了setState()或markNeedsBuild()。
    不能将此GetX小部件标记为需要构建,因为框架已经在构建小部件的过程中。只有在其祖先之一当前在构建时,才可以在构建阶段标记小部件为需要构建。这种情况是允许的,因为框架在构建子级之前构建父级小部件,这意味着脏的子孙将始终被构建。否则,在此构建阶段,框架可能不会访问此小部件。
    调用setState()或markNeedsBuild()的小部件是:GetX

  • 第二个问题是,当我按下在Widget1内添加新Widget2的按钮时,没有任何反应。我在Widget2列表中放置了一个打印语句,它确实被填充,但在视觉上没有显示。

    I/flutter (27561): list: 1
    I/flutter (27561): list: 2
    I/flutter (27561): list: 3
    I/flutter (27561): list: 4

以下是你的代码的一些可能的解决方案:

  1. 第一个问题:在每次添加Widget1时,你都在Get.put(Widget1Controller()),这可能会导致问题。你可以尝试在main() 函数中创建一个全局的Widget1Controller实例,并将其传递给需要它的Widget1。这样可以避免多次创建相同的控制器。

  2. 第二个问题:在Widget1内部使用ExpansionTile可能会导致问题。尝试使用ExpansionPanelList或其他适合你的UI结构的小部件来显示Widget2列表。

请注意,这些建议可能不是解决问题的唯一方法,但它们可以帮助你排除一些潜在的问题。你可能需要更深入地检查代码以查找其他可能的问题和解决方案。

英文:

I'm trying to make an example with GetX where I have the following structure:

A HomePage with a list of Widget1, each Widget1 has a list of Widget2.

I'm having two problems:

  • The first one is that I can only add one Widget1, when I go to add the second one, it shows the following error:

>The following assertion was thrown building Widget1(dirty):
setState() or markNeedsBuild() called during build.

>This GetX<Widget1Controller> 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<Widget1Controller>

  • The second problem is that when I press the button to add new Widget2, inside Widget1, nothing happens. I put a print in the list of Widget2, and it is being filled, but visually it doesn't show.

> I/flutter (27561): list: 1

> I/flutter (27561): list: 2

> I/flutter (27561): list: 3

> I/flutter (27561): list: 4

HomePage:

class HomePage extends GetView&lt;HomeController&gt; {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final homeController = Get.put(HomeController());
    final widget1Controller = Get.put(Widget1Controller());

    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          final mykey = GlobalKey();

          controller.addWidget1(Widget1(mykey: mykey));
        },
      ),
      body: GetX&lt;HomeController&gt;(
        builder: (_) {
          return ListView.builder(
            itemBuilder: (context, index) {
              return controller.getWidget1[index];
            },
            itemCount: controller.getWidget1.length,
          );
        },
      ),
    );
  }
}

HomePage Controller:

class HomeController extends GetxController {
  final RxList&lt;Widget1&gt; _widget1List = &lt;Widget1&gt;[].obs;

  void addWidget1(Widget1 newWidget1) {
    _widget1List.add(newWidget1);
  }

  List&lt;Widget1&gt; get getWidget1 {
    return _widget1List;
  }
}

Widget1:

class Widget1 extends GetView&lt;Widget1Controller&gt; {
  final Key mykey;

  const Widget1({required this.mykey, super.key});

  @override
  Widget build(BuildContext context) {
    controller.setListWidget2(mykey);

    return GetX&lt;Widget1Controller&gt;(
      builder: (_) {
        return ExpansionTile(
          title: Container(
            color: Colors.blue,
            width: 50.0,
            height: 50.0,
          ),
          children: [
            ListView.builder(
              itemBuilder: (context, index) {
                return controller.getListWidget2(mykey)[index];
              },
              itemCount: controller.getListWidget2(mykey).length,
              shrinkWrap: true,
            ),
            IconButton(
              onPressed: () {
                controller.addWidget2(const Widget2(), mykey);

                debugPrint(&quot;list: ${controller.getListWidget2(mykey).length}&quot;);
              },
              icon: const Icon(Icons.add),
            )
          ],
        );
      },
    );
  }
}

Widget1 Controller:

class Widget1Controller extends GetxController {
  final RxMap&lt;Key, List&lt;Widget2&gt;&gt; _mapListWidget2 = &lt;Key, List&lt;Widget2&gt;&gt;{}.obs;

  void setListWidget2(Key mykey) {
    _mapListWidget2[mykey] = &lt;Widget2&gt;[];
  }

  void addWidget2(Widget2 newWidget2, Key mykey) {
    _mapListWidget2[mykey]!.add(newWidget2);
  }

  List&lt;Widget2&gt; getListWidget2(Key key) {
    if (_mapListWidget2[key] != null) {
      return _mapListWidget2[key]!;
    } else {
      return &lt;Widget2&gt;[];
    }
  }
}

Widget2:

class Widget2 extends StatelessWidget {
  const Widget2({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 300.0,
      width: 300.0,
      color: Colors.red,
    );
  }
}

Main:

void main() {
  runApp(
    const GetMaterialApp(
      home: HomePage(),
    ),
  );
}

I am really lost, how can i solve these two problems?

答案1

得分: 2

以下是您要翻译的部分:

You were facing these issues because some of you have not implemented some of the functionality properly as GetX suggests.

Some of the issues were like putting the controller from initial bindings instead of directly putting it inside the build method.

Here's the working code.

Main app and HomePage

void main() {
  runApp(
    GetMaterialApp(
      initialBinding: InitalBinding(),
      home: const HomePage(),
    ),
  );
}

class HomePage extends GetView<HomeController> {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          var mykey = UniqueKey();
          controller.addWidget1(Widget1(mykey: mykey));
        },
      ),
      body: GetX<HomeController>(
        builder: (logic) {
          return ListView.builder(
            itemBuilder: (context, index) {
              return logic.getWidget1[index];
            },
            itemCount: logic.getWidget1.length,
          );
        },
      ),
    );
  }
}

HomeController (It remains the same)

class HomeController extends GetxController {
  final RxList<Widget1> _widget1List = <Widget1>[].obs;

  void addWidget1(Widget1 newWidget1) {
    _widget1List.add(newWidget1);
  }

  List<Widget1> get getWidget1 {
    return _widget1List;
  }
}

Widget - 1

class Widget1 extends GetView<Widget1Controller> {
  final Key mykey;
  const Widget1({required this.mykey, super.key});

  @override
  Widget build(BuildContext context) {
    return GetBuilder<Widget1Controller>(
      builder: (controller) {
        return ExpansionTile(
          title: Container(
            color: Colors.blue,
            width: 50.0,
            height: 50.0,
          ),
          children: [
            ListView.builder(
              itemBuilder: (context, index) {
                return controller.getListWidget2(mykey)[index];
              },
              itemCount: controller.getListWidget2(mykey).length,
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
            ),
            IconButton(
              onPressed: () {
                if (controller.mapListWidget2.containsKey(mykey)) {
                  controller.addWidget2(const Widget2(), mykey);
                  controller.update();
                } else {
                  controller.setListWidget2(mykey);
                  controller.addWidget2(const Widget2(), mykey);
                  controller.update();
                }

                debugPrint("list: ${controller.getListWidget2(mykey).length}");
              },
              icon: const Icon(Icons.add),
            )
          ],
        );
      },
    );
  }
}

Widget - 1 Controller

class Widget1Controller extends GetxController {
  final RxMap<Key, List<Widget2>> mapListWidget2 = <Key, List<Widget2>>{}.obs;

  void setListWidget2(Key mykey) {
    mapListWidget2[mykey] = <Widget2>[];
  }

  void addWidget2(Widget2 newWidget2, Key mykey) {
    mapListWidget2[mykey]!.add(newWidget2);
  }

  List<Widget2> getListWidget2(Key key) {
    if (mapListWidget2[key] != null) {
      return mapListWidget2[key]!;
    } else {
      return <Widget2>[];
    }
  }
}

Widget - 2 remains the same.

I have put dependency injection in another file called bindings.

Initial Bindings

class InitalBinding implements Bindings {
  @override
  void dependencies() {
    Get.put<HomeController>(HomeController(), permanent: true);
    Get.put<Widget1Controller>(Widget1Controller(), permanent: true);
  }
}
英文:

You were facing these issues because some of you have not implemented some of the functionality properly as GetX suggests.

Some of the issues were like putting the controller from initial bindings instead of directly putting it inside the build method.

Here's the working code.

Main app and HomePage

void main() {
  runApp(
    GetMaterialApp(
      initialBinding: InitalBinding(),
      home: const HomePage(),
    ),
  );
}

class HomePage extends GetView&lt;HomeController&gt; {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          var mykey = UniqueKey();
          controller.addWidget1(Widget1(mykey: mykey));
        },
      ),
      body: GetX&lt;HomeController&gt;(
        builder: (logic) {
          return ListView.builder(
            itemBuilder: (context, index) {
              return logic.getWidget1[index];
            },
            itemCount: logic.getWidget1.length,
          );
        },
      ),
    );
  }
}

HomeController (It remains the same)

class HomeController extends GetxController {
  final RxList&lt;Widget1&gt; _widget1List = &lt;Widget1&gt;[].obs;

  void addWidget1(Widget1 newWidget1) {
    _widget1List.add(newWidget1);
  }

  List&lt;Widget1&gt; get getWidget1 {
    return _widget1List;
  }
}

Widget - 1

class Widget1 extends GetView&lt;Widget1Controller&gt; {
  final Key mykey;
  const Widget1({required this.mykey, super.key});

  @override
  Widget build(BuildContext context) {
    return GetBuilder&lt;Widget1Controller&gt;(
      builder: (controller) {
        return ExpansionTile(
          title: Container(
            color: Colors.blue,
            width: 50.0,
            height: 50.0,
          ),
          children: [
            ListView.builder(
              itemBuilder: (context, index) {
                return controller.getListWidget2(mykey)[index];
              },
              itemCount: controller.getListWidget2(mykey).length,
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
            ),
            IconButton(
              onPressed: () {
                if (controller.mapListWidget2.containsKey(mykey)) {
                  controller.addWidget2(const Widget2(), mykey);
                  controller.update();
                } else {
                  controller.setListWidget2(mykey);
                  controller.addWidget2(const Widget2(), mykey);
                  controller.update();
                }

                debugPrint(&quot;list: ${controller.getListWidget2(mykey).length}&quot;);
              },
              icon: const Icon(Icons.add),
            )
          ],
        );
      },
    );
  }
}

Widget - 1 Controller

class Widget1Controller extends GetxController {
  final RxMap&lt;Key, List&lt;Widget2&gt;&gt; mapListWidget2 = &lt;Key, List&lt;Widget2&gt;&gt;{}.obs;

  void setListWidget2(Key mykey) {
    mapListWidget2[mykey] = &lt;Widget2&gt;[];
  }

  void addWidget2(Widget2 newWidget2, Key mykey) {
    mapListWidget2[mykey]!.add(newWidget2);
  }

  List&lt;Widget2&gt; getListWidget2(Key key) {
    if (mapListWidget2[key] != null) {
      return mapListWidget2[key]!;
    } else {
      return &lt;Widget2&gt;[];
    }
  }
}

Widget - 2 remains the same.

I have put dependency injection in another file called bindings.

Initial Bindings

class InitalBinding implements Bindings {
  @override
  void dependencies() {
    Get.put&lt;HomeController&gt;(HomeController(), permanent: true);
    Get.put&lt;Widget1Controller&gt;(Widget1Controller(), permanent: true);
  }
}

huangapple
  • 本文由 发表于 2023年2月16日 13:34:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75468207.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定