当 AlertDialog 中的复选框选中或未选中时,Flutter 更新 Reorderable ListView

Posted

技术标签:

【中文标题】当 AlertDialog 中的复选框选中或未选中时,Flutter 更新 Reorderable ListView【英文标题】:Flutter update Reorderable ListView when checkbox in AlertDialog check or unchecked 【发布时间】:2020-08-16 05:46:20 【问题描述】:

我目前正在开发一款应该适用于 androidios 的 Flutter 移动应用。 我遇到的问题是关于 ListView 和更新它。

我知道我在很多事情上都做错了,但我正在学习,我想好好学习。因此,如果您有任何 cmets,请提供有关代码的提示 :)

基本上这就是它的作用: 主要玩家将通过一个 AlertDialog 选择与他一起玩游戏的人,其中包含一个 CheckboxList ,每次他选择一个玩家时,它都会更新一个名为 choosenPlayers 的列表,其中包含所有已选择的 Player 对象。

然后我要做的是显示所有选定玩家的列表(可重新排序的列表以更改玩家的顺序)并在每次更新选择玩家列表时更新它。

我设法显示了这些播放器,但我必须通过进入抽屉菜单并单击页面链接来重新加载页面以查看添加的播放器。

我为我的玩家可重新排序列表使用有状态小部件,并将玩家列表传递给父级(我知道这不是正确的做法):

import 'package:flutter/material.dart';
import 'package:mollky/models/player.dart';

class ChoosenPlayers extends StatefulWidget 
  _ChoosenPlayersState _choosenPlayersState = _ChoosenPlayersState();
  List<Player> choosenPlayers = [];
  ChoosenPlayers(Key key, this.choosenPlayers) : super(key: key);

  @override
  _ChoosenPlayersState createState() => _choosenPlayersState;


class _ChoosenPlayersState extends State<ChoosenPlayers> 
  void initState() 
    super.initState();
  

  @override
  Widget build(BuildContext context) 
    return ReorderableListView(
      onReorder: onReorder,
      children: getListItems(),
    );
  

  List<ListTile> getListItems() => widget.choosenPlayers
      .asMap()
      .map((i, item) => MapEntry(i, buildTenableListTile(item, i)))
      .values
      .toList();

  ListTile buildTenableListTile(Player item, int index) 
    return ListTile(
      key: ValueKey(item.id),
      title: Text(item.nickname + " " + item.name),
      leading: Text("#$index + 1"),
    );
  

  void onReorder(int oldIndex, int newIndex) 
    if (newIndex > oldIndex) 
      newIndex -= 1;
    

    setState(() 
      Player reOrderedPlayer = widget.choosenPlayers[oldIndex];

      widget.choosenPlayers.removeAt(oldIndex);
      widget.choosenPlayers.insert(newIndex, reOrderedPlayer);
    );
  

这是显示可重新排序列表和显示 AlertDialog 的主页的代码。 抱歉,无法用 Dart 格式化,不要运行明显被剪断的代码 xD

class NewGame extends StatefulWidget 
  @override
  State<StatefulWidget> createState() => NewGameState();


class NewGameState extends State<NewGame> 
  List<Player> players = [];

  NewGameState() 
    this.players.add(
        Player(id: 0, name: "Dupont", nickname: "julien", picture: "test"));
    this
        .players
        .add(Player(id: 1, name: "Dpont", nickname: "julien", picture: "test"));
    this
        .players
        .add(Player(id: 2, name: "Dunt", nickname: "juen", picture: "test"));
  

  static List<Player> _choosenPlayers = [];
  ChoosenPlayers choosenPlayersObject = ChoosenPlayers(
    choosenPlayers: _choosenPlayers,
  );

  @override
  Widget build(BuildContext context) 
    return Scaffold(
        drawer: DrawerWidget(),
        appBar: AppBar(title: Text("Nouvelle partie")),
        body: Column(children: <Widget>[
          Card(
              child: ListTile(
                  leading: Icon(Icons.people),
                  title: Text("Choisissez les joueurs"),
                  onTap: () 
                    showDialog(
                        context: context,
                        builder: (BuildContext context) 
                          return AlertDialog(
                              title: Text("Les joueurs existants"),
                              content:
                                  Stack(overflow: Overflow.visible, children: <
                                      Widget>[
                                Positioned(
                                  right: -40.0,
                                  top: -40.0,
                                  child: InkResponse(
                                    onTap: () 
                                      Navigator.of(context).pop();
                                    ,
                                    child: CircleAvatar(
                                      child: Icon(Icons.close),
                                      backgroundColor: Colors.lightBlue,
                                    ),
                                  ),
                                ),
                                Positioned(
                                  child: StatefulBuilder(
                                    builder: (BuildContext context,
                                        StateSetter setState) 
                                      return Container(
                                        width: 350.0,
                                        height: 150.0,
                                        child: ListView.builder(
                                            itemCount: players.length,
                                            itemBuilder:
                                                (context, playerIndex) 
                                              return CheckboxListTile(
                                                title: Text(players[playerIndex]
                                                        .nickname +
                                                    " " +
                                                    players[playerIndex].name),
                                                value: _choosenPlayers.contains(
                                                    players[playerIndex]),
                                                onChanged: (bool value) 
                                                  if (!_choosenPlayers.contains(
                                                      players[playerIndex])) 
                                                    _choosenPlayers.add(
                                                        players[playerIndex]);
                                                    setState(() );
                                                   else 
                                                    _choosenPlayers.remove(
                                                        players[playerIndex]);
                                                    setState(() );
                                                  
                                                ,
                                                secondary: const Icon(
                                                    Icons.hourglass_empty),
                                              );
                                            ),
                                      );
                                    ,
                                  ),
                                ),
                              ]));
                        );
                  )),
          Container(
            width: 350.0,
            height: 150.0,
            child: choosenPlayersObject,
          ),
        ]));
  

我在论坛上没有看到关于更新列表而不触发像 onRefresh 这样的回调,这不是我想要的。

这是一场真正的噩梦 xD。抱歉法语单词顺便说一句,如果需要我可以翻译,但它们并不重要,简单的文本。

这是列表和警告对话框的两个屏幕截图:

提前谢谢你:)

【问题讨论】:

【参考方案1】:

父窗口小部件的状态没有更新。这就是为什么,即使付款人已添加到列表中。但未显示在父小部件中。 您调用的 setState 只更新 StatefulBuilder 的状态,而不是 NewGame 的状态。 查看下面的代码。

  import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: NewGameScreen(),
    );
  


class NewGameScreen extends StatefulWidget 
  @override
  _NewGameScreenState createState() => _NewGameScreenState();


class _NewGameScreenState extends State<NewGameScreen> 
  List<Player> _availablePlayers = [];
  List<Player> _selectedPlayers = [];

  @override
  void initState() 
    super.initState();
    _availablePlayers = [
      Player(id: 0, name: "Ross", nickname: "Geller", picture: "test"),
      Player(id: 1, name: "Rachel", nickname: "Green", picture: "test"),
      Player(id: 2, name: "Chandler", nickname: "Bing", picture: "test"),
    ];
  

  _selectPlayer() 
    showDialog(
      context: context,
      builder: (BuildContext context) 
        return AlertDialog(
          title: Text("Existing players"),
          content: Stack(
            overflow: Overflow.visible,
            children: <Widget>[
              Positioned(
                right: -40.0,
                top: -40.0,
                child: InkResponse(
                  onTap: () 
                    Navigator.of(context).pop();
                  ,
                  child: CircleAvatar(
                    child: Icon(Icons.close),
                    backgroundColor: Colors.lightBlue,
                  ),
                ),
              ),
              StatefulBuilder(
                builder: (BuildContext context, StateSetter alertState) 
                  return Container(
                    width: 350.0,
                    height: 150.0,
                    child: ListView.builder(
                      itemCount: _availablePlayers.length,
                      itemBuilder: (context, playerIndex) 
                        return CheckboxListTile(
                          title:
                              Text(_availablePlayers[playerIndex].nickname + " " + _availablePlayers[playerIndex].name),
                          value: _selectedPlayers.contains(_availablePlayers[playerIndex]),
                          onChanged: (bool value) 
                            if (_selectedPlayers.contains(_availablePlayers[playerIndex])) 
                              _selectedPlayers.remove(_availablePlayers[playerIndex]);
                             else 
                              _selectedPlayers.add(_availablePlayers[playerIndex]);
                            
                            setState(() );//ALSO UPDATE THE PARENT STATE
                            alertState(() );
                          ,
                          secondary: const Icon(Icons.hourglass_empty),
                        );
                      ,
                    ),
                  );
                ,
              ),
            ],
          ),
        );
      ,
    );
  

  _onReorder(int oldIndex, int newIndex) 
    if (newIndex > oldIndex) 
      newIndex -= 1;
    
    print('oldIndex:$oldIndex');
    print('newIndex:$newIndex');
    setState(() 
      Player player = _selectedPlayers[newIndex];
      _selectedPlayers[newIndex] = _selectedPlayers[oldIndex];
      _selectedPlayers[oldIndex] = player;
    );
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(title: Text("New Game")),
      body: Column(
        children: <Widget>[
          Card(
            child: ListTile(
              leading: Icon(Icons.people),
              title: Text("Choose players"),
              onTap: _selectPlayer,
            ),
          ),
          Flexible(
            child: ReorderableListView(
              onReorder: _onReorder,
              children: _selectedPlayers.map((player) 
                return ListTile(
                  key: ValueKey(player.id),
                  title: Text(player.nickname + " " + player.name),
                  leading: Text("#$_selectedPlayers.indexOf(player) + 1"),
                );
              ).toList(),
            ),
          ),
        ],
      ),
    );
  


class Player 
  int id;
  String name;
  String nickname;
  String picture;

  Player(this.id, this.name, this.nickname, this.picture);

希望对你有帮助:)

【讨论】:

谢谢!!!你帮我省了几个小时的麻烦!感谢您为变量、类和方法使用正确的命名!希望您的回答能帮助到其他人!

以上是关于当 AlertDialog 中的复选框选中或未选中时,Flutter 更新 Reorderable ListView的主要内容,如果未能解决你的问题,请参考以下文章

循环检查复选框并计算每个选中或未选中的复选框

用于检查输入是不是“选中”或未“选中”的复选框功能

PHP jquery设置复选框值,如果选中或未选中并发布

如果 php 和 jQuery 选中或未选中,则在 php 中动态创建复选框值

如何将动态创建的复选框链接到已选中或未选中的事件?

在Angular Js中重新加载后,如何将复选框(选中或未选中)从javascript控制器更新到html页面?