异步功能完成后的 Flutter setState()
Flutter setState() after completion of async function
我已经根据数据库中的条目创建了 ListView.builder 自定义小部件。现在我想添加一个删除功能。当我单击该按钮时,该条目会相应地从数据库中删除。但是,尽管调用了 setState(),但 ListView 不会更新。我怀疑这可能是由于删除方法是异步的,我如何让应用程序先删除条目然后刷新ListView?
我的 ListView.builder 的代码是:
ListView.builder(
padding: const EdgeInsets.all(8.0),
itemExtent: 106.0,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return CustomListItem(
name: transactionsReversed[index].name,
category: transactionsReversed[index].category,
sign: transactionsReversed[index].sign,
amount: transactionsReversed[index].amount,
id: transactionsReversed[index].id,
delete: (){
print('Running delete function on ${transactionsReversed[index].id}');
setState(() {
_remove(transactionsReversed[index].id);
});
}
);
}
),
自定义列表项如下所示:
class CustomListItem extends StatelessWidget {
const CustomListItem({
this.name, this.category, this.sign, this.amount, this.id, this.delete
});
final String name;
final String category;
final String sign;
final double amount;
final int id;
final Function delete;
@override
Widget build(BuildContext context) {
const double radius = 15.0;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: InkWell(
onTap: () {
Navigator.pushNamed(context, '/category_viewer', arguments: {
'category': category
});
},
child: Container(
color: Colors.transparent,
child: Container(
decoration: BoxDecoration(
color: Colors.black,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(radius),
topRight: const Radius.circular(radius),
bottomLeft: const Radius.circular(radius),
bottomRight: const Radius.circular(radius),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: IconButton(
onPressed: delete,
icon: Icon(
Icons.delete_outlined,
color: Colors.red,
size: 40,
),
color: Colors.red
),
),
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 0.0, 0.0, 0.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20.0,
color: Colors.white
),
),
const Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
Text(
category,
style: const TextStyle(
fontSize: 14.0,
color: Colors.white
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 50, 0),
child: Text(
'$sign${amount.toStringAsFixed(2)}€',
style: TextStyle(
fontSize: 25.0,
color: sign == '+' ? Colors.green : Colors.red
),
),
),
],
),
),
),
),
);
}
}
最后,remove方法很简单:
_remove(int id) async {
DatabaseHelper helper = DatabaseHelper.instance;
await helper.deleteTransaction(id);
}
试试这个
将 _remove 更改为将来无效
Future<void> _remove(int id) async {
DatabaseHelper helper = DatabaseHelper.instance;
await helper.deleteTransaction(id);
}
然后更改您的设置状态
setState(() {
_remove(transactionsReversed[index].id).then((_) {
setState(() {
//setstate
});
});
})
});
您的问题似乎与您在调用 delete 时立即调用 setState
这一事实有关,这意味着在调用 helper.deleteTransaction(id);
之前会立即触发视图重建。我建议您仅在 async
_remove
方法返回 future 后才调用 setState
,如下所示:
将 Future<void>
类型添加到您的 _remove
方法:
Future<void> _remove(int id) async {
DatabaseHelper helper = DatabaseHelper.instance;
await helper.deleteTransaction(id);
}
仅当您收到未来时才调用您的 setState
,如下所示:
delete: (){
print('Running delete function on ${transactionsReversed[index].id}');
setState(() {
_remove(transactionsReversed[index].id).then((value) => setState((){}));
});
}
如其他答案中所述,问题是由于 setState
运行 在 async
方法 _remove
完成之前。
由于 _remove
是您的 Widget class 中的私有方法,也许它可以让 setState
负责。
你的_remove
变成:
Future<void> _remove(int id) async {
await DatabaseHelper.instance.deleteTransaction(id);
setState((){});
}
您可以将 delete
回调简化为:
ListView.builder(
[...]
itemBuilder: (context, index) {
return CustomListItem(
[...]
delete: () => _remove(transactionsReversed[index].id),
);
}
),
我已经根据数据库中的条目创建了 ListView.builder 自定义小部件。现在我想添加一个删除功能。当我单击该按钮时,该条目会相应地从数据库中删除。但是,尽管调用了 setState(),但 ListView 不会更新。我怀疑这可能是由于删除方法是异步的,我如何让应用程序先删除条目然后刷新ListView?
我的 ListView.builder 的代码是:
ListView.builder(
padding: const EdgeInsets.all(8.0),
itemExtent: 106.0,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return CustomListItem(
name: transactionsReversed[index].name,
category: transactionsReversed[index].category,
sign: transactionsReversed[index].sign,
amount: transactionsReversed[index].amount,
id: transactionsReversed[index].id,
delete: (){
print('Running delete function on ${transactionsReversed[index].id}');
setState(() {
_remove(transactionsReversed[index].id);
});
}
);
}
),
自定义列表项如下所示:
class CustomListItem extends StatelessWidget {
const CustomListItem({
this.name, this.category, this.sign, this.amount, this.id, this.delete
});
final String name;
final String category;
final String sign;
final double amount;
final int id;
final Function delete;
@override
Widget build(BuildContext context) {
const double radius = 15.0;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: InkWell(
onTap: () {
Navigator.pushNamed(context, '/category_viewer', arguments: {
'category': category
});
},
child: Container(
color: Colors.transparent,
child: Container(
decoration: BoxDecoration(
color: Colors.black,
borderRadius: new BorderRadius.only(
topLeft: const Radius.circular(radius),
topRight: const Radius.circular(radius),
bottomLeft: const Radius.circular(radius),
bottomRight: const Radius.circular(radius),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: IconButton(
onPressed: delete,
icon: Icon(
Icons.delete_outlined,
color: Colors.red,
size: 40,
),
color: Colors.red
),
),
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 0.0, 0.0, 0.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: const TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20.0,
color: Colors.white
),
),
const Padding(padding: EdgeInsets.symmetric(vertical: 2.0)),
Text(
category,
style: const TextStyle(
fontSize: 14.0,
color: Colors.white
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 50, 0),
child: Text(
'$sign${amount.toStringAsFixed(2)}€',
style: TextStyle(
fontSize: 25.0,
color: sign == '+' ? Colors.green : Colors.red
),
),
),
],
),
),
),
),
);
}
}
最后,remove方法很简单:
_remove(int id) async {
DatabaseHelper helper = DatabaseHelper.instance;
await helper.deleteTransaction(id);
}
试试这个
将 _remove 更改为将来无效
Future<void> _remove(int id) async {
DatabaseHelper helper = DatabaseHelper.instance;
await helper.deleteTransaction(id);
}
然后更改您的设置状态
setState(() {
_remove(transactionsReversed[index].id).then((_) {
setState(() {
//setstate
});
});
})
});
您的问题似乎与您在调用 delete 时立即调用 setState
这一事实有关,这意味着在调用 helper.deleteTransaction(id);
之前会立即触发视图重建。我建议您仅在 async
_remove
方法返回 future 后才调用 setState
,如下所示:
将 Future<void>
类型添加到您的 _remove
方法:
Future<void> _remove(int id) async {
DatabaseHelper helper = DatabaseHelper.instance;
await helper.deleteTransaction(id);
}
仅当您收到未来时才调用您的 setState
,如下所示:
delete: (){
print('Running delete function on ${transactionsReversed[index].id}');
setState(() {
_remove(transactionsReversed[index].id).then((value) => setState((){}));
});
}
如其他答案中所述,问题是由于 setState
运行 在 async
方法 _remove
完成之前。
由于 _remove
是您的 Widget class 中的私有方法,也许它可以让 setState
负责。
你的_remove
变成:
Future<void> _remove(int id) async {
await DatabaseHelper.instance.deleteTransaction(id);
setState((){});
}
您可以将 delete
回调简化为:
ListView.builder(
[...]
itemBuilder: (context, index) {
return CustomListItem(
[...]
delete: () => _remove(transactionsReversed[index].id),
);
}
),