我正在用一个小的移动应用程序学习Dart和Flutter。我已经阅读了我找到的关于await关键字的所有内容,但我仍然有一些我不理解的问题。下面是简化的代码。为了理解我的问题,我删除了所有我认为不必要的东西。如果遗漏了什么重要的东西,请告诉我。
我的问题是TODO下面的行(第7行):List locations = await _useCaseManager.findLocations();在这个方法中,我查询数据库。我希望应用程序等待,直到查询完成并返回数据。
我在build()方法中调用_findFirstLocation()方法。但Flutter并不等待数据。它继续执行其余的代码,特别是createNextAppointmentsList()方法。在这个方法中,我需要应用程序等待将来使用的数据--属性_selectedLocation。但是因为Flutter不等待,所以_selectedLocation为空。
这是类的相关部分。
class _AppointmentsOverviewScreenState extends State<AppointsmentsOverviewScreen> {
UseCaseManager _useCaseManager;
Location _selectedLocation;
void _findFirstLocation() async {
// TODO Hier wartet er schon wieder nicht.
List<Location> locations = await _useCaseManager.findLocations();
_selectedLocation = locations.first;
print(_selectedLocation);
}
@override
Widget build(BuildContext context) {
_useCaseManager = UseCaseManager.getInstance();
_findFirstLocation();
return Scaffold(
appBar: AppBar(
title: Text(LabelConstants.TITLE_APPOINTMENT_OVERVIEW),
),
body: Column(
children: <Widget>[
Container(child: createNextAppointmentsList(),)
],
),
);
}
Widget createNextAppointmentsList() {
return FutureBuilder<List<Appointment>>(
future: _useCaseManager.findAppointmentsForActiveGarbageCans(_selectedLocation.locationId),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text(snapshot.data[index].garbageCanName),
subtitle: Text(snapshot.data[index].date),
);
},
);
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(child: CircularProgressIndicator());
},
);
}
}在方法_findFirstLocation中,有以下方法和一个名为的数据库查询。
Future<List<Location>> findLocations() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query(DatabaseConstants.LOCATION_TABLE_NAME);
return List.generate(maps.length, (i) {
Location location = Location(
locationId: maps[i][DatabaseConstants.COLUMN_NAME_LOCATION_ID],
street: maps[i][DatabaseConstants.COLUMN_NAME_STREET],
houseNumber: maps[i][DatabaseConstants.COLUMN_NAME_HOUSENUMBER],
zipCode: maps[i][DatabaseConstants.COLUMN_NAME_ZIP_CODE],
city: maps[i][DatabaseConstants.COLUMN_NAME_CITY],
);
return location;
});
}因为我已经遇到了await的问题,而导致这些问题的原因是具有lambda表达式的foreach(),所以我尝试了另一种类型的for循环作为替代:
Future<List<Location>> findLocations() async {
final db = await database;
final List<Map<String, dynamic>> maps = await db.query(DatabaseConstants.LOCATION_TABLE_NAME);
final List<Location> locations = List();
for (int i = 0; i < maps.length; i++) {
var locationFromDatabase = maps[i];
Location location = Location(
locationId: maps[i][DatabaseConstants.COLUMN_NAME_LOCATION_ID],
street: maps[i][DatabaseConstants.COLUMN_NAME_STREET],
houseNumber: maps[i][DatabaseConstants.COLUMN_NAME_HOUSENUMBER],
zipCode: maps[i][DatabaseConstants.COLUMN_NAME_ZIP_CODE],
city: maps[i][DatabaseConstants.COLUMN_NAME_CITY],
);
locations.add(location);
}
return locations;
}但在这两种情况下,应用程序都没有等待数据,我不明白其中的原因。
提前谢谢你。
克里斯托弗
发布于 2020-12-29 00:35:14
您的代码中有几个问题:
首先,如果你想' await‘,当你想让流程等待的时候,你必须使用await这个词。您可以在_findFirstLocation()函数中执行此操作,但在调用它时并未执行此操作,因此,您应该这样调用它:
await _findFirstLocation();但即使这样也是不正确的,因为您正在尝试阻止UI线程,这是完全禁止的(这将导致UI冻结,用户体验很差)。
在这种情况下,您需要的是一个FutureBuilder,您可以在其中指定在后台进程运行时应该发生什么,当它抛出错误时应该发生什么,以及在返回结果时应该发生什么。
最后,我建议您不要在build()方法中初始化变量,因为它可以被多次调用:
_useCaseManager = UseCaseManager.getInstance();如果可能,我会将其移到类的主体中,如果不可能,则将其放入initState()方法中。
发布于 2020-12-29 00:32:52
在调用方法_findFirstLocation();的地方,不要对它执行await操作。这意味着调用不会等待结果,它只会转到下一条指令并继续执行。
在这种特殊情况下,您实际上不能await它,因为build方法不是async,并且您不能更改它。在这种情况下,您需要一个FutureBuilder来显示微调器或等待对话框,直到加载结果。
您可以在此处找到更多信息:
https://stackoverflow.com/questions/65480712
复制相似问题