Flutter中Map<String, dynamic>始终为空的原因排查
问题描述
我编写了Flutter代码定义了Structure类,其中readData方法通过HTTP请求从USDA API获取数据并赋值给dataList。但在其他文件中导入该类并调用readData后,Map<String, dynamic>类型的dataList始终为空。尝试直接调用readData以及在setState中调用该方法,结果都一样。
原代码如下:
import 'package:http/http.dart' as http; import 'dart:convert'; class Structure { Map<String, dynamic> dataList = <String, dynamic>{}; final String apiKey = "Dx6Raz0w6yVEEilPlk2edTmDSJBvTJh7qkou7OwW"; String query = "Cheddar Cheese"; String dataType = "SR Legacy"; String pageSize = "25"; String pageNumber = "2"; String sortBy = "publishedDate"; String sortOrder = "asc"; Future<void> readData() async { query.replaceAll(' ', '%20'); dataType.replaceAll(' ', '%20'); String request = "https://api.nal.usda.gov/fdc/v1/foods/search?query=$query&dataType=$dataType&pageSize=$pageSize&pageNumber=$pageNumber&sortBy=$sortBy&sortOrder=$sortOrder&api_key=$apiKey"; final response = await http.get(Uri.parse(request)); if (response.statusCode == 200) { Map<String, dynamic> data = jsonDecode(response.body); dataList = data; } else { throw Exception('Failed to fetch data'); } } }
调用代码:
Structure s = Structure(); s.readData(); print('$s.dataList');
以及:
Structure s = Structure(); setState(() {s.readData();}); print('$s.dataList');
原因分析
异步操作执行顺序错误
readData是异步方法(返回Future<void>),调用它后会立刻继续执行后续的print语句,此时HTTP请求还没完成,dataList自然还是初始的空Map。哪怕把readData放进setState里,setState也不会等待异步方法完成,print依然会提前执行。字符串替换操作无效
Dart中的String是不可变类型,replaceAll方法不会修改原字符串,而是返回一个新的字符串。原代码里query.replaceAll(' ', '%20')没有把返回值赋值给query,导致URL里的空格没有被转义,可能直接导致请求失败,即便等异步完成也拿不到数据。
解决办法
1. 正确等待异步操作完成
调用readData时必须用await等待其执行完毕,才能确保dataList已经被赋值。如果是在Widget的方法里,需要把方法标记为async:
// 示例调用(需在async方法中执行) Structure s = Structure(); await s.readData(); print(s.dataList);
2. 修复字符串转义问题
将replaceAll的返回值赋值给原变量,或者更推荐用Uri.encodeComponent来处理URL参数的转义,避免手动处理出错:
Future<void> readData() async { // 方式1:接收replaceAll的返回值 // query = query.replaceAll(' ', '%20'); // dataType = dataType.replaceAll(' ', '%20'); // 方式2:更安全的URL参数转义 String request = "https://api.nal.usda.gov/fdc/v1/foods/search?query=${Uri.encodeComponent(query)}&dataType=${Uri.encodeComponent(dataType)}&pageSize=$pageSize&pageNumber=$pageNumber&sortBy=$sortBy&sortOrder=$sortOrder&api_key=$apiKey"; final response = await http.get(Uri.parse(request)); if (response.statusCode == 200) { Map<String, dynamic> data = jsonDecode(response.body); dataList = data; } else { throw Exception('Failed to fetch data'); } }
3. 在Widget中正确更新UI
如果是在Widget里使用,不要把异步方法放进setState,而是在异步方法完成后调用setState来更新UI:
Structure s = Structure(); // 在State类的方法中 void fetchData() async { await s.readData(); setState(() { // 这里dataList已经有值,可以更新UI }); } // 调用fetchData而不是直接调用readData fetchData();
内容的提问来源于stack exchange,提问作者Volker Deppe

