【问题标题】:How to remove a TextField from ListView when onPressed button?onPressed按钮时如何从ListView中删除TextField?
【发布时间】:2019-10-16 13:40:07
【问题描述】:

当用户点击“清除图标”按钮时如何删除文本字段? (不只是清除TextField的Text)

用户故事
用户单击按钮添加播放器。 (从技术上讲,这个按钮添加了 TextField)
用户可以在 TextField 上写下玩家的名字。
用户单击“清除图标”按钮以删除当前的 TextField(与添加功能相反)。

new ListView.builder(
                     padding: EdgeInsets.all(0),
                      shrinkWrap: true,
                      physics: NeverScrollableScrollPhysics(),
                      itemCount: 5,
                      itemBuilder: (context, index) {
                        print(index);
                        return TextField(
                          maxLength: 20,
                          decoration: InputDecoration(
                            labelText: "Player ${index+1}",
                            counterText: "",
                            prefixIcon: const Icon(Icons.person),
                            suffixIcon: new IconButton(
                                icon: Icon(Icons.clear),
                                onPressed: () =>
                                  setState(() {
                                    this.dispose(); // -----Doesn't work----
                                  })
                                ),
                          ),
                        );
                      }
                    ),

例如,如果用户单击“清除按钮”,则用户在 Player 4 上设置“John”,然后 Player 4 TextField 被删除。它将仅保留 4 个 TextField

【问题讨论】:

    标签: flutter dart


    【解决方案1】:

    我假设的事实:

    1. 您希望能够从列表中删除(或添加)一个字段
    2. 您希望在删除字段时保留剩余字段的值
    3. 列表可以大于 5

    解决方案:

    如果您希望以上所有内容都为真,那么您实际上需要跟踪 TextField 的 TextEditingControllers,而不是文本字段本身。这是因为 TextField 的值实际上存储在 TextEditingController 中(如果您不为每个小部件提供它,它会在运行中重新创建)。看看这个:

    import 'package:flutter/material.dart';
    
    // needs to be StatefulWidget, so we can keep track of the count of the fields internally
    class PlayerList extends StatefulWidget {
      const PlayerList({
        this.initialCount = 5,
      });
    
      // also allow for a dynamic number of starting players
      final int initialCount;
    
      @override
      _PlayerListState createState() => _PlayerListState();
    }
    
    class _PlayerListState extends State<PlayerList> {
      int fieldCount = 0;
      int nextIndex = 0;
      // you must keep track of the TextEditingControllers if you want the values to persist correctly
      List<TextEditingController> controllers = <TextEditingController>[];
    
      // create the list of TextFields, based off the list of TextControllers
      List<Widget> _buildList() {
        int i;
        // fill in keys if the list is not long enough (in case we added one)
        if (controllers.length < fieldCount) {
          for (i = controllers.length; i < fieldCount; i++) {
            controllers.add(TextEditingController());
          }
        }
    
        i = 0;
        // cycle through the controllers, and recreate each, one per available controller
        return controllers.map<Widget>((TextEditingController controller) {
          int displayNumber = i + 1;
          i++;
          return TextField(
            controller: controller,
            maxLength: 20,
            decoration: InputDecoration(
              labelText: "Player $displayNumber",
              counterText: "",
              prefixIcon: const Icon(Icons.person),
              suffixIcon: IconButton(
                icon: Icon(Icons.clear),
                onPressed: () {
                  // when removing a TextField, you must do two things:
                  // 1. decrement the number of controllers you should have (fieldCount)
                  // 2. actually remove this field's controller from the list of controllers
                  setState(() {
                    fieldCount--;
                    controllers.remove(controller);
                  });
                },
              ),
            ),
          );
        }).toList(); // convert to a list
      }
    
    
      @override
      Widget build(BuildContext context) {
        // generate the list of TextFields
        final List<Widget> children = _buildList();
    
        // append an 'add player' button to the end of the list
        children.add(
          GestureDetector(
            onTap: () {
              // when adding a player, we only need to inc the fieldCount, because the _buildList()
              // will handle the creation of the new TextEditingController
              setState(() {
                fieldCount++;
              });
            },
            child: Container(
              color: Colors.blue,
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Text(
                  'add player',
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
        );
    
        // build the ListView
        return ListView(
          padding: EdgeInsets.all(0),
          shrinkWrap: true,
          physics: NeverScrollableScrollPhysics(),
          children: children,
        );
      }
    
      @override
      void initState() {
        super.initState();
    
        // upon creation, copy the starting count to the current count
        fieldCount = widget.initialCount;
      }
    
      @override
      void dispose() {
        super.dispose();
      }
    
      @override
      void didUpdateWidget(PlayerList oldWidget) {
        super.didUpdateWidget(oldWidget);
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
      }
    }
    

    通过以上内容,您可以:

    • 启动应用程序
    • 将播放器 2 更改为“鲍勃”
    • 将玩家 3 更改为“史蒂夫”
    • 将播放器 4 更改为“查尔斯”
    • 删除播放器 3
    • 观察玩家 2 是 'bob' 而新玩家 3 是 'charles'

    我认为这就是您要在这里寻找的。​​p>

    【讨论】:

    • 非常感谢您的帮助,并且您的回答得到了很好的解释:)
    • 我尝试这样做:当用户点击“添加播放器”按钮时自动滚动到底部页面。我添加了一个 ScrollController 但不起作用ScrollController _scrollController = new ScrollController(); if (controllers.length &lt; fieldCount) { for (i = controllers.length; i &lt; fieldCount; i++) { controllers.add(TextEditingController()); _scrollController.animateTo( 0.0, curve: Curves.easeOut, duration: const Duration(milliseconds: 300), ); } }
    • 您的滚动控制器需要是一个在每次重绘期间都不会重新创建的变量。您需要将其设为_PlayerListState 类的属性,并仅在initState() 函数中对其进行初始化。然后将其作为ListView 的控制器附加到_buildList() 函数中。然后做你的_scrollController.animateTo() 应该可以工作,但前提是你把它放在onTap 回调中。将它移到那里以避免在构建阶段出现关于 setState() 的错误。
    • 好答案,但在 dispose() 覆盖函数中的每个控制器上调用 .dispose() 不是一种好习惯吗?
    • 如果一个控制器被移除,它还需要被disposed()吗?
    【解决方案2】:

    您可以拥有一个属性来检查用户是否单击了该按钮,并根据您显示/隐藏TextField 的值。下面我只使用布尔属性,如果用户单击 X 按钮,然后我将 hideField 设置为 true,TextField 将被替换为零大小的小部件。

     new ListView.builder(
                        padding: EdgeInsets.all(0),
                        shrinkWrap: true,
                        physics: NeverScrollableScrollPhysics(),
                        itemCount: 5,
                        itemBuilder: (context, index) {
                          print(index);
                          bool hideField = false; // I added it here
    
                          return hideField ? SizedBox.shrink() : TextField(
                            maxLength: 20,
                            decoration: InputDecoration(
                              labelText: "Player ${index + 1}",
                              counterText: "",
                              prefixIcon: const Icon(Icons.person),
                              suffixIcon: new IconButton(
                                  icon: Icon(Icons.clear),
                                  onPressed: () =>
                                      setState(() {
                                        hideField = true; // Now it works
                                      })
                              ),
                            ),
                          );
                        }
                    )
    

    【讨论】:

    • 这会有两个问题。 1. 您只能拥有 5 个文本字段,2. 当您删除其中一个字段时,其他字段的值将不会以正确的顺序保留。
    • 不会,不会有问题。 1. 请注意,我只是在更新当前 TextField 的状态 2. 由于我没有重建 while ListView,因此其他人的值将保持不变,我只是将 TextField 替换为大小为零的小部件。
    • 将其插入模拟器。我指出的两个问题是实际问题。
    猜你喜欢
    • 1970-01-01
    • 2015-11-28
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多