android - 当 AlertDialog 中的复选框选中或未选中时,Flutter 更新 Reorderable ListView
问题描述
我目前正在开发一个应该可以在 Android 和 IOS 上运行的 Flutter 移动应用程序。我遇到的问题是关于 ListView 和更新它。
我知道我在很多事情上都做错了,但我正在学习,我想好好学习。因此,如果您有任何意见,请提供有关代码的提示:)
基本上这就是它的作用:主要玩家将通过一个 AlertDialog 选择谁将在游戏中与他一起玩,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。抱歉,法语单词顺便说一句,如果需要我可以翻译,但它们并不重要,简单的文字。
这是列表和警报对话框的两个屏幕截图:
先感谢您 :)
解决方案
父小部件的状态未更新。这就是为什么,即使付款人已添加到列表中。但未显示在父小部件中。您调用的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});
}
希望能帮助到你 :)
推荐阅读
- javascript - 类型错误:setEmail 不是 onChange 函数
- google-apis-explorer - Directory API Explorer 的有效示例数据?
- coq-tactic - 如何解决关于长度交替附加定理和子列表长度定理的 coq 列表分配
- javascript - 如何将 CSS 和 JS 链接或连接到 Python 代码
- java - Java 正则表达式。仅当特殊字符被数字包围时才保留它们
- python - 无法从 JSON 文件打印数据
- mysql - 外键格式不正确 - Laravel8
- assembly - mips: $v0 返回 0 而不是数组指针
- c# - AmazonSQSClient 获取 FIFO 队列组中的消息数
- elasticsearch - 从三台服务器创建 Elasticsearch 集群