繁体   English   中英

Getx 没有正确更新 TextFormField 列表

[英]Getx not updating list of TextFormField correctly

我在 Flutter Web 应用程序中使用 Getx 和ListView.builder来呈现带有TextFormField的项目列表。 每个呈现的项目都有一个删除按钮。 当我单击删除项目时,包含数据的列表似乎正确更新,但相应的 UI 错误地删除了显示的绝对最后一项,而不是您单击要删除的实际项目。 这个问题似乎专门发生在TextFormField上。

我在下面包含了一个说明问题的示例应用程序。 要进行测试,只需安装Getx ,然后运行该应用程序(我将其作为 Web 应用程序运行)。 应用程序运行后,在左侧列(名为“ Using TextFormFields ”)中尝试删除项目,您会发现问题所在——删除的始终是最后显示的项目,即使您单击删除第一个项目也是如此. 为了进行比较,我在右侧包含了一个使用ListTile s 而不是TextFormField s 的设置,并且可以正常工作。

有谁知道为什么TextFormField会出现这个问题? 你知道如何解决这个问题吗? 在此先感谢您的帮助!

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class Item {
  final int id;
  final String name;

  Item({
    required this.id,
    required this.name,
  });
}

class OverviewPageController extends GetxController {

  final itemsList = [
    Item(id: 0, name: 'Item 1'),
    Item(id: 1, name: 'Item 2'),
    Item(id: 2, name: 'Item 3'),
    Item(id: 3, name: 'Item 4'),
  ].obs;

  void deleteItem(Item item) {
    int index = itemsList.indexOf(item);
    var itemRemoved = itemsList.removeAt(index);
    print('item deleted: ${itemRemoved.name}');
    itemsList.refresh();
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(
      debugShowCheckedModeBanner: false,
      home: OverviewPage(),
    );
  }
}

class OverviewPage extends StatelessWidget {
  const OverviewPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(40),
          child: Row(
            children: const [
              Expanded(child: TextFormFieldsSection()),
              SizedBox(width: 40),
              Expanded(child: ListTilesSection()),
            ],
          ),
        ),
      ),
    );
  }
}

class ListTilesSection extends StatelessWidget {
  const ListTilesSection({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller1 = Get.put(OverviewPageController(), tag: '1');

    return Column(
      children: [
        const Text('Using ListTiles', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
        const SizedBox(height: 20),
        Obx(
              () => ListView.builder(
            shrinkWrap: true,
            itemCount: controller1.itemsList.length,
            itemBuilder: (context, index) {
              return Column(
                children: [
                  ListTile(
                    title: Text(controller1.itemsList[index].name),
                    trailing: OutlinedButton(
                      onPressed: () => controller1.deleteItem(controller1.itemsList[index]),
                      child: const Icon(Icons.delete_forever),
                    ),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10),
                      side: const BorderSide(color: Colors.black12),
                    ),
                  ),
                  const SizedBox(height: 5),
                ],
              );
            },
          ),
        ),
      ],
    );
  }
}

class TextFormFieldsSection extends StatelessWidget {
  const TextFormFieldsSection({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller2 = Get.put(OverviewPageController(), tag: '2');

    return Column(
      children: [
        const Text('Using TextFormFields', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
        const SizedBox(height: 20),
        Obx(
              () => ListView.builder(
            shrinkWrap: true,
            itemCount: controller2.itemsList.length,
            itemBuilder: (context, index) {
              return Column(
                children: [
                  Container(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(10),
                        border: Border.all(color: Colors.black12)
                    ),
                    child: Row(
                      children: [
                        Expanded(
                          child: TextFormField(
                            readOnly: true,
                            initialValue: controller2.itemsList[index].name,
                            decoration: const InputDecoration(border: InputBorder.none),
                          ),
                        ),
                        OutlinedButton(
                          onPressed: () => controller2.deleteItem(controller2.itemsList[index]),
                          child: const Icon(Icons.delete_forever),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(height: 5),
                ],
              );

            },
          ),
        ),
      ],
    );
  }
}

你的代码是完全正确的,应该可以正常工作,它是 Flutter 不知道要删除哪个小部件,我将解释:

当 Flutter 引擎注意到树中存在的元素已从树中删除(您的代码确实如此)时,它会用具有相同runtimeType的其他小部件替换它,因此当您想删除TextFormField时,Flutter 将确切的小部件误认为是即使您的代码完全没问题,也请删除。

在大多数情况下,此行为对性能确实有帮助,可以避免在树中进行额外的必要构建。

你的问题的答案是告诉 Flutter 每个TextFormField都是独一无二的,通过为 TextFormField 分配一个唯一的Key ,这是你的新代码:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class Item {
  final int id;
  final String name;

  Item({
    required this.id,
    required this.name,
  });
}

class OverviewPageController extends GetxController {
  final itemsList = [
    Item(id: 0, name: 'Item 1'),
    Item(id: 1, name: 'Item 2'),
    Item(id: 2, name: 'Item 3'),
    Item(id: 3, name: 'Item 4'),
  ].obs;

  void deleteItem(Item item) {
    int index = itemsList.indexOf(item);
    var itemRemoved = itemsList.removeAt(index);
    print('item deleted: ${itemRemoved.name}');
    itemsList.refresh();
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const GetMaterialApp(
      debugShowCheckedModeBanner: false,
      home: OverviewPage(),
    );
  }
}

class OverviewPage extends StatelessWidget {
  const OverviewPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(40),
          child: Row(
            children: const [
              Expanded(child: TextFormFieldsSection()),
              SizedBox(width: 40),
              Expanded(child: ListTilesSection()),
            ],
          ),
        ),
      ),
    );
  }
}

class ListTilesSection extends StatelessWidget {
  const ListTilesSection({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller1 = Get.put(OverviewPageController(), tag: '1');

    return Column(
      children: [
        const Text('Using ListTiles',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
        const SizedBox(height: 20),
        Obx(
          () => ListView.builder(
            shrinkWrap: true,
            itemCount: controller1.itemsList.length,
            itemBuilder: (context, index) {
              return Column(
                key: UniqueKey(), // add UniqueKey()
                children: [
                  ListTile(
                    key: UniqueKey(),
                    title: Text(controller1.itemsList[index].name),
                    trailing: OutlinedButton(
                      onPressed: () =>
                          controller1.deleteItem(controller1.itemsList[index]),
                      child: const Icon(Icons.delete_forever),
                    ),
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10),
                      side: const BorderSide(color: Colors.black12),
                    ),
                  ),
                  const SizedBox(height: 5),
                ],
              );
            },
          ),
        ),
      ],
    );
  }
}

class TextFormFieldsSection extends StatelessWidget {
  const TextFormFieldsSection({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final controller2 = Get.put(OverviewPageController(), tag: '2');

    return Column(
      children: [
        const Text('Using TextFormFields',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
        const SizedBox(height: 20),
        Obx(
          () => ListView.builder(
            shrinkWrap: true,
            itemCount: controller2.itemsList.length,
            itemBuilder: (context, index) {
              return Column(
                children: [
                  Container(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(10),
                        border: Border.all(color: Colors.black12)),
                    child: Row(
                      children: [
                        Expanded(
                          child: TextFormField(
                            key: UniqueKey(), // add UniqueKey()
                            readOnly: true,
                            initialValue: controller2.itemsList[index].name,
                            decoration:
                                const InputDecoration(border: InputBorder.none),
                          ),
                        ),
                        OutlinedButton(
                          onPressed: () => controller2
                              .deleteItem(controller2.itemsList[index]),
                          child: const Icon(Icons.delete_forever),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(height: 5),
                ],
              );
            },
          ),
        ),
      ],
    );
  }
}

请注意我分配给小部件的UniqueKey() ,现在再次运行您的应用程序,它应该会按预期工作。

请同时参考这些主题:

何时使用按键 - Flutter 小部件

钥匙

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM