提问人:bzc0fq 提问时间:10/18/2023 最后编辑:bzc0fq 更新时间:10/19/2023 访问量:86
如何在 Flutter 中从 ListView 中删除项目
How to remove an item from a ListView in Flutter
问:
我正在寻找一种从 ListView.builder 外部删除 ListView 项的方法。 项目名称和索引是已知的。我试图通过ListView.removeAt(itemIndex)来实现这一点;但不幸的是,这不起作用。
通常。。。有两个 FloatingActionButton:fab1 和 fab2。按下 fab2 后,调用 removeItem(itemName) 函数以从数据库中删除数据(这工作正常),一旦删除数据,应从 ListView 中删除项目(这不起作用,因为我不知道如何引用 ListView)。
在这种情况下,您能否就如何从 ListView 中删除项目提供建议?
完整代码附在下面:
Future<List<PostListDetails>> postsFuture = getPosts();
bool showButton = false;
String itemName ="";
int itemIndex = 0;
static Future<List<PostListDetails>> getPosts() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final token = await prefs.getString('token');
final listID = await prefs.getString('listID');
final lista = await prefs.getString('lista');
Response response = await post(Uri.parse('someURL'),
headers: <String, String>{
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
body: <String, String>{
'listID': '$listID',
},
);
if(response.statusCode != 200){
print('Błąd połączenia z bazą danych... status[${response.statusCode}]');
}
final List body = json.decode(response.body);
return body.map((e) => PostListDetails.fromJson(e)).toList();
}
void removeItem(String nazwa) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final token = await prefs.getString('token');
final listID = await prefs.getString('listID');
try{
Response response = await post(Uri.parse('SomeURL'),
headers: <String, String>{
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
body: <String, String>{
'listID': '$listID',
'nazwa': nazwa,
},
);
if(response.statusCode == 200) {
//ListView.removeAt(itemIndex); <--- I do not know how to do this
}else{
print('Błąd połączenia z bazą danych... status[${response.statusCode}]');
}
}catch(e){
print(e.toString());
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton.extended(
heroTag: "fab1",
onPressed: () {
print('---> addList()');
// addList();
},
icon: const Icon(Icons.add_circle_outline),
label: const Text('Dodaj'),
),
if (showButton)
FloatingActionButton.extended(
heroTag: "fab2",
onPressed: () {
print('---> removing $itemName');
removeItem(itemName); // this function removes data from database
setState(() => showButton = false);
},
icon: const Icon(Icons.delete),
label: const Text('Usuń'),
),
],
),
appBar: AppBar(
backgroundColor: Colors.grey[900],
title: const Text('Lista zakupowa'),
actions: [
IconButton(
onPressed: signUserOut,
icon: const Icon(Icons.logout),
),
],
),
body: Center(
child: FutureBuilder<List<PostListDetails>>(
future: postsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
return buildPosts(snapshot.data!);
} else {
return const Text("Brak danych...");
}
},
),
),
);
}
@override
Widget buildPosts(List<PostListDetails> posts) {
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
int customCompare(PostListDetails a, PostListDetails b) {
if (a.status != b.status) {
return a.status!.toInt() - b.status!.toInt();
}
final int categoryCompare = a.kategoria.toString().compareTo(b.kategoria.toString());
if (categoryCompare != 0) {
return categoryCompare;
}
return a.nazwa.toString().compareTo(b.nazwa.toString());
}
return GestureDetector(
onLongPress: () {
itemName = post.nazwa!;
itemIndex = index;
setState(() => showButton = true);
},
child: Container(
color: post.status! % 2 == 1 ? Colors.grey[100] : Colors.white,
margin: const EdgeInsets.symmetric(vertical: 0.5, horizontal: 0),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15),
height: 50,
width: double.maxFinite,
child: Row(
children: [
Expanded(
flex: 8,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
post.nazwa!,
textAlign: TextAlign.left,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black),
),
Text(
'${post.waga!.toString()} g.',
textAlign: TextAlign.left,
style: const TextStyle(fontWeight: FontWeight.normal, fontSize: 15, color: Colors.grey),
),
],
),
),
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Expanded(
flex: 0,
child: Row(),
),
Expanded(
flex: 10,
child: Row(
children: [
CatIcon(imagePath: (Cat2Ico(post.kategoria!.toString()))),
Checkbox(
value: Int2Bool(post.status!.toInt()),
onChanged: (value) {
setState(() {
post.status = Bool2Int(value!);
});
saveStatus(post.nazwa!, post.status!, index);
posts.sort(customCompare);
},
),
],
),
),
],
),
),
],
),
),
);
},
);
}
更新 1 我已经更新了源代码并移动了ListView.remove...放入 removeItem 函数。
更新 2
更改后的最终代码如下:
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FloatingActionButton.extended(
heroTag: "fab1",
onPressed: () {
print('---> addList()');
// addList();
},
icon: const Icon(Icons.add_circle_outline),
label: const Text('Dodaj'),
),
if (showButton)
FloatingActionButton.extended(
heroTag: "fab2",
onPressed: () {
removeItem(itemName).then((value){
setState((){
itemToRemove = true;
});
});
setState(() => showButton = false);
},
icon: const Icon(Icons.delete),
label: const Text('Usuń'),
),
],
),
appBar: AppBar(
backgroundColor: Colors.grey[900],
title: const Text('Lista zakupowa'),
actions: [
IconButton(
onPressed: signUserOut,
icon: const Icon(Icons.logout),
),
],
),
body: Center(
child: FutureBuilder<List<PostListDetails>>(
future: postsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
return buildPosts(snapshot.data!);
} else {
return const Text("Brak danych...");
}
},
),
),
);
}
@override
Widget buildPosts(List<PostListDetails> posts) {
if (itemToRemove) {
posts.removeWhere((element) => element.nazwa == itemName);
itemToRemove=false;
}
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final post = posts[index];
int customCompare(PostListDetails a, PostListDetails b) {
if (a.status != b.status) {
return a.status!.toInt() - b.status!.toInt();
}
final int categoryCompare = a.kategoria.toString().compareTo(b.kategoria.toString());
if (categoryCompare != 0) {
return categoryCompare;
}
return a.nazwa.toString().compareTo(b.nazwa.toString());
}
return GestureDetector(
onLongPress: () {
itemName = post.nazwa!;
itemIndex = index;
setState(() => showButton = true);
},
child: Container(
color: post.status! % 2 == 1 ? Colors.grey[100] : Colors.white,
margin: const EdgeInsets.symmetric(vertical: 0.5, horizontal: 0),
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 15),
height: 50,
width: double.maxFinite,
child: Row(
children: [
Expanded(
flex: 8,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
post.nazwa!,
textAlign: TextAlign.left,
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 15, color: Colors.black),
),
Text(
'${post.waga!.toString()} g.',
textAlign: TextAlign.left,
style: const TextStyle(fontWeight: FontWeight.normal, fontSize: 15, color: Colors.grey),
),
],
),
),
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Expanded(
flex: 0,
child: Row(),
),
Expanded(
flex: 10,
child: Row(
children: [
CatIcon(imagePath: (Cat2Ico(post.kategoria!.toString()))),
Checkbox(
value: Int2Bool(post.status!.toInt()),
onChanged: (value) {
setState(() {
post.status = Bool2Int(value!);
});
saveStatus(post.nazwa!, post.status!, index);
posts.sort(customCompare);
},
),
],
),
),
],
),
),
],
),
),
);
},
);
}
答:
1赞
Davis
10/18/2023
#1
这可以通过多种方式实现。
等待项目成功从数据库中删除,然后更新应用程序。这将使用加载器来等待数据库事务,并使用 setState 在完成后更新 UI。
通过获取列表项的索引并通过更新应用的状态将其删除。
posts.removeAt(itemIndex);
我建议你使用方式 1。因为您只需使用 和Future
.then()
执行此操作
void removeItem(String nazwa) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final token = await prefs.getString('token');
final listID = await prefs.getString('listID');
try{
Response response = await post(Uri.parse('SomeURL'),
headers: <String, String>{
'Accept': 'application/json',
'Authorization': 'Bearer $token',
},
body: <String, String>{
'listID': '$listID',
'nazwa': nazwa,
},
);
if(response.statusCode == 200) {
//ListView.removeAt(itemIndex); Don't need this
// just refresh UI here
setState(() { });//<==remove this if you do not want to refresh the entire screen
}else{
print('Błąd połączenia z bazą danych... status[${response.statusCode}]');
}
}catch(e){
print(e.toString());
}
}
然后,UI会根据DB的变化进行更新
Center(
child: FutureBuilder<List<PostListDetails>>(
future: postsFuture,//Replace with getPosts() to directly/actively read from DB
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
return buildPosts(snapshot.data!);
} else {
return const Text("Brak danych...");
}
},
),
为了显示正在进行的更改,请使用这样CircularProgressIndicator()
...
bool isLoading = false;
...
FloatingActionButton.extended(
heroTag: "fab2",
onPressed: () {
print('---> removing $itemName');
//loading
setState((){
isLoading = true;
});
removeItem(itemName).then((value){
setState((){
posts.removeWhere((element) => element.itemName == itemName);//<==please confirm that element.itemName is in your model class else correct it
isLoading = false;
});}; // this function removes data from database
setState(() => showButton = false);
},
icon: const Icon(Icons.delete),
label: const Text('Usuń'),
),
像这样在你的身体里使用它
body: isLoading ? Center(child: CircularProgressIndicator()) : Center(
child: FutureBuilder<List<PostListDetails>>(
future: postsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
return buildPosts(snapshot.data!);
} else {
return const Text("Brak danych...");
}
},
),
),
评论
0赞
bzc0fq
10/18/2023
还行。。。我添加了更多代码并将 removeAt 移动到 removeItem 函数中。你能看看这个并提出建议吗......?
0赞
bzc0fq
10/18/2023
哇。。。很多变化...感谢您:)的努力。我以为我会是一行代码的问题......我已经应用了所有更改并且它可以工作,但是这有一个问题。整个列表已更新,而不是一行。你能想象在互联网连接缓慢的情况下会发生什么吗?有没有办法只删除或更新 UI - 一个 ListView 项(一行)而不是所有项(行)来改进这一点?
0赞
Davis
10/18/2023
是的,这是可能的。让我做一些改动
0赞
Davis
10/18/2023
如果这解决了您的问题,请标记为正确
0赞
bzc0fq
10/18/2023
谢谢:) - 我已经接受了你的回答。
评论