【问题标题】:Flutter change state of an item in a ListView颤振更改 ListView 中项目的状态
【发布时间】:2022-11-15 21:20:12
【问题描述】:

我正在尝试在 ListView 中使用 ListTile 小部件,该小部件将根据应用程序中其他地方收到的一些信息而改变。为了练习,我试着做了一个 Dartpad 的小例子。

问题如下:我已经能够更改项目背后的布尔值和数据,但它们不会在 ListView.builder 中更新。

我在 ListView 中有 ListTile 小部件,我希望有如下四种不同的状态:idlewaitrequestedspeaking

例如,不同的状态如下所示:

我正在尝试更改其中一项的单个状态,但我无法让它们在 ListView 中正确更新。

ListTile 代码如下所示。大部分代码只负责处理每个状态的 UI 应该是什么样子:

class UserItem extends StatefulWidget {
  String name;
  UserTileState userTileState;
  UserItem(this.name, this.userTileState);

  @override
  UserItemState createState() => UserItemState();
}

class UserItemState extends State<UserItem> {
  String _getCorrectTextState(UserTileState userTileState) {
    switch (userTileState) {
      case UserTileState.speaking:
        return "Currently Speaking";
      case UserTileState.requested:
        return "Speak Request";
      case UserTileState.wait:
        return "Wait - Someone Speaking";
      case UserTileState.idle:
        return "Idle";
    }
  }

  Widget _getCorrectTrailingWidget(UserTileState userTileState) {
    switch (userTileState) {
      case UserTileState.speaking:
        return const CircleAvatar(
            backgroundColor: Colors.green,
            foregroundColor: Colors.white,
            child: Icon(Icons.volume_up));
      case UserTileState.requested:
        return CircleAvatar(
          backgroundColor: Colors.green.shade100,
          foregroundColor: Colors.grey[700],
          child: const Icon(Icons.volume_up),
        );
      case UserTileState.wait:
        return CircleAvatar(
          backgroundColor: Colors.green.shade100,
          foregroundColor: Colors.grey[700],
          child: const Icon(Icons.volume_off),
        );
      case UserTileState.idle:
        return CircleAvatar(
          backgroundColor: Colors.green.shade100,
          foregroundColor: Colors.grey[700],
          child: const Icon(Icons.volume_off),
        );
    }
  }

  void kickUser(String name) {
    print("Kick $name");
  }

  @override
  Widget build(BuildContext context) {
    return ListTile(
        onLongPress: () {
          kickUser(widget.name);
        },
        title: Text(widget.name,
            style: TextStyle(
                fontWeight: widget.userTileState == UserTileState.speaking ||
                        widget.userTileState == UserTileState.requested
                    ? FontWeight.bold
                    : FontWeight.normal)),
        subtitle: Text(_getCorrectTextState(widget.userTileState),
            style: const TextStyle(fontStyle: FontStyle.italic)),
        trailing: _getCorrectTrailingWidget(widget.userTileState));
  }
}

enum UserTileState { speaking, requested, idle, wait }

为了尝试触发这些UserTile 项目之一的更改,我编写了如下函数。这应该使idle 磁贴成为requested 磁贴。

void userRequest(String name) {
    // send a speak request to a tile
    int index = users.indexWhere((element) => element.name == name);
    users[index].userTileState = UserTileState.requested;
  }

然后,我将在按钮内的主构建函数中运行 userRequest,如下所示:

class MyApp extends StatefulWidget {
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  
  void userRequest(String name) {
    // send a speak request to a tile
    int index = users.indexWhere((element) => element.name == name);
    users[index].userTileState = UserTileState.requested;
  }

  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(children: [
      Expanded(
          flex: 8,
          child: ListView.separated(
              separatorBuilder: (context, index) {
                return const Divider();
              },
              itemCount: users.length,
              itemBuilder: (context, index) {
                return users[index];
              })),
      Expanded(
          flex: 2,
          child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                
                TextButton(
                    onPressed: () {
                      setState(() {
                        userRequest("Test1");
                      });
                    },
                    child: const Text("Send Request")),
                
              ]))
    ]));
  }
}

当我点击按钮在第一个 UserTile 中设置值时,什么也没有发生。

我不知道在哪里放置 setState,并且 ListView 中对象的状态没有被更新。这个问题最简单的解决方案是什么?提供者?我在同样的情况下使用了 Provider 并且无法让它工作。还有另一种更简单的解决方案吗?

如何更改更新 ListView 中特定元素的状态?

【问题讨论】:

    标签: flutter listview listtile


    【解决方案1】:

    我相信问题出在_getCorrectTextState()

    创建一个变量,String _status; 做这样的事情;

         void _getCorrectTextState(UserTileState userTileState) {
                String tmpState;
                switch (userTileState) {
                  case UserTileState.speaking:
                    tmpState = "Currently Speaking";
                  case UserTileState.requested:
                    tmpState = "Speak Request";
                  case UserTileState.wait:
                    tmpState = "Wait - Someone Speaking";
                  case UserTileState.idle:
                    tmpState = "Idle";
                }
               setState(() => {_state = tmpState});
    }
    

    然后在您的构建中:

    subtitle: _state;
    

    【讨论】:

    • 这是一个想法,但我需要在subtitle 属性中调用_getCorrectTextState,因为文本因userTileState 而不同,我没有其他地方可以调用它。
    • 为什么不添加逻辑_getCorrectTrailingWidget()?
    • 同样的问题。我需要在trailing 属性中调用_getCorrectTrailingWidget。
    【解决方案2】:

    您可以使用 StatefulBuilder() - 通过使用它,您将为列表中的每个项目获得一个单独的状态,可以更新

    像这样:

    ListView.separated(
              separatorBuilder: (context, index) {
                return const Divider();
              },
              itemCount: users.length,
              itemBuilder: (context, index) {
                return StatefulBuilder(
                  builder: ((context, setStateItem){
    
             //return_your_listTile_widget_here
             //use setStateItem (as setState) this will update the state of selected item
    
                    setStateItem(() {
                            //code for update the listView item                 
                           });
    
                      })
                    );
              })),
    

    【讨论】:

    • 更新 listView 项的代码将从按钮调用,而不是从主构建函数内部调用。更新因按下的按钮而异,因此我不确定如何将其放入 Stateful Builder 中。
    • 我只是运行您的代码并没有更改任何内容及其工作。在这里,我使用 2 个按钮更新 ListTile 第一个和第二个。签出附件
    【解决方案3】:

    我发布的代码对某些人来说结果好坏参半。我的主要项目实际上有点复杂,涉及Provider,但是当我发布这个问题时,我尽力将代码剥离到最简单的部分。我想知道是否有其他东西阻碍了这项工作。 (也许是因为我用 dartpad 把这个例子放在一起?)

    同时,我发现在列表中添加或删除元素似乎会改变列表的整个状态。使用这种技术,我没有尝试更改单个值,而是获取它的索引,删除它,然后在该索引处重新添加它,它就像一个魅力。所以代替这个:

    void userRequest(String name) {
        // send a speak request to a tile
        int index = users.indexWhere((element) => element.name == name);
        users[index].userTileState = UserTileState.requested;
      }
    
    

    其次是setState(() =&gt; userRequest("name");,以下似乎可以代替:

    void userRequest(String name) {
        // send a speak request to a tile
        int index = users.indexWhere((element) => element.name == name);
        users.removeAt(index);
        users.insert(index, UserItem(name, UserTileStates.requested));
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 2021-05-23
      • 2011-02-06
      • 2021-09-02
      • 1970-01-01
      相关资源
      最近更新 更多