Flutter中如何在Settings提交后更新FutureBuilder的API数据
问题:设置修改后刷新高峰时段数据并更新FutureBuilder
我原本在LocationBasedPrimeTimes.dart中通过API(PrimeTimesFetchByLocation.dart)获取数据,用FutureBuilder展示高峰时段。现在希望在settingsUI.dart中完成设置修改并点击**_handleSubmit**后,重新获取数据并刷新FutureBuilder,不知道该怎么实现。
代码片段
1. Settings.dart(期望通过城市ID重新更新初始获取的高峰时段)
class _BasedPrimeTimesState extends State<BasedPrimeTimes> { @override Widget build(BuildContext context) { return Container( color: Color(0xFFFFFBF8), padding: EdgeInsets.only(top: 10), child: Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Builder( builder: (context) => Container( width: double.infinity, padding: EdgeInsets.symmetric(horizontal: 30), child: ElevatedButton( onPressed: _loading ? null : () async { // Call the submit function when button is pressed final String cityName = _cityInputValue; final int? cityId = null; // Assuming this value should be fetched from somewhere final String countryName = _inputValue; final SharedPreferences prefs = await SharedPreferences.getInstance(); final int? countryId = prefs.getInt('countryId_$countryName'); setState(() { _loading = true; }); await _handleSubmit( cityName, cityId, countryName, countryId); setState(() { _loading = false; }); }, child: _loading ? CircularProgressIndicator() : Text('Submit'), ), ), ), ], ), ); } Future<void> _handleSubmit( String cityName, int? cityId, String countryName, int? countryId) async { if (cityName.isNotEmpty && countryName.isNotEmpty) { final SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setInt('cityId', cityId ?? 0); } else { _showSnackbar('Error: City or Country is empty', true); } } }
2. LocationBasedPrimeTimes.dart(需要在此更新高峰时段)
class _LocationBasedPrimeTimesState extends State<LocationBasedPrimeTimes> { late Future<Map<String, dynamic>> _primeTimesFuture; final primeTimesFetcher = PrimeTimesFetcher(); @override void initState() { super.initState(); _primeTimesFuture = primeTimesFetcher.loadCityIdAndFetchPrimeTimes(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Container( child: Center( child: Padding( padding: const EdgeInsets.all(24), child: FutureBuilder<Map<String, dynamic>>( future: _primeTimesFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const CircularProgressIndicator(); } else if (snapshot.hasError && snapshot.error != null) { return Text('Error: ${snapshot.error}'); } else if (snapshot.hasError && snapshot.error == null) { return Text('Unknown Error'); } else { Map<String, dynamic> primeTimes = snapshot.data!; ); } }, ), ), ), ), ), ); } }
3. PrimeTimesFetchByLocation.dart(初始获取高峰时段的逻辑)
class PrimeTimesFetcher { Future<Map<String, dynamic>> loadCityIdAndFetchPrimeTimes() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int? cityId = prefs.getInt('cityId'); if (cityId != null) { return fetchPrimeTimes(cityId); } else { throw Exception('City ID not available in SharedPreferences.'); } } Future<Map<String, dynamic>> fetchPrimeTimes(int cityId) async { final String url = 'some-api/city'; final Map<String, dynamic> requestBody = { "cityId": cityId, }; DateTime now = DateTime.now(); String currentMonthYear = DateFormat('yyyy-MM').format(now); List<Map<String, dynamic>> cachedPrimeTimes = await LocalDatabase.getPrimeTimesByMonth(currentMonthYear); if (cachedPrimeTimes.isNotEmpty) { return cachedPrimeTimes.first; } final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(requestBody), ); if (response.statusCode == 200) { Map<String, dynamic> responseData = jsonDecode(response.body); List<dynamic> primeTimesData = responseData['primeTimes']; String currentDate = DateFormat('EEEE dd MMMM yyyy').format(DateTime.now()); Map<String, dynamic>? currentPrimeTimes; for (var primeTime in primeTimesData) { if (primeTime['date'] == currentDate) { currentPrimeTimes = primeTime; break; } } if (currentPrimeTimes != null) { await LocalDatabase.locationBasedInsertPrimeTimes( LocationBasedPrimeTimesModel.fromJson(currentPrimeTimes), currentMonthYear); return currentPrimeTimes; } else { throw 'Prime times not available for $currentDate.'; } } else { throw 'Failed to fetch prime times.'; } } }
解决方案
要实现设置提交后刷新FutureBuilder,核心是让_primeTimesFuture重新触发请求,同时完成页面间的状态联动,具体步骤如下:
1. 在LocationBasedPrimeTimes中添加刷新方法
修改_LocationBasedPrimeTimesState,新增一个重新获取数据的方法,通过setState更新_primeTimesFuture:
class _LocationBasedPrimeTimesState extends State<LocationBasedPrimeTimes> { late Future<Map<String, dynamic>> _primeTimesFuture; final primeTimesFetcher = PrimeTimesFetcher(); @override void initState() { super.initState(); _refreshPrimeTimes(); // 替换原来的直接赋值 } // 新增刷新方法 void _refreshPrimeTimes() { setState(() { _primeTimesFuture = primeTimesFetcher.loadCityIdAndFetchPrimeTimes(); }); } // 其余build代码保持不变 }
2. 从Settings页面返回时触发刷新
如果Settings页面是通过Navigator.push打开的,在提交成功后返回时传递刷新信号:
- 在Settings的
_handleSubmit方法末尾添加返回逻辑:
await _handleSubmit(cityName, cityId, countryName, countryId); // 提交成功后返回主页面并告知需要刷新 Navigator.pop(context, true);
- 在LocationBasedPrimeTimes中打开Settings页面时,监听返回结果:
// 示例:假设你通过按钮打开Settings页面 ElevatedButton( onPressed: () async { final shouldRefresh = await Navigator.push( context, MaterialPageRoute(builder: (context) => BasedPrimeTimes()), // 你的Settings页面组件 ); if (shouldRefresh == true) { _refreshPrimeTimes(); // 触发刷新 } }, child: Text('打开设置'), )
3. 清除旧缓存(可选)
由于你的fetchPrimeTimes会优先读取本地缓存,提交新城市ID后需要清除对应月份的缓存,避免显示旧数据:
// 在Settings的_handleSubmit中添加清除缓存逻辑 Future<void> _handleSubmit(...) async { if (cityName.isNotEmpty && countryName.isNotEmpty) { final SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setInt('cityId', cityId ?? 0); // 清除当前月份的旧缓存 String currentMonthYear = DateFormat('yyyy-MM').format(DateTime.now()); await LocalDatabase.clearPrimeTimesByMonth(currentMonthYear); } else { _showSnackbar('Error: City or Country is empty', true); } }
4. 修正Settings中cityId的赋值问题
当前代码里cityId是硬编码为null,你需要补充根据城市名称获取对应ID的逻辑,否则无法存储有效的城市ID到SharedPreferences,后续API请求会失败。
内容的提问来源于stack exchange,提问作者Auvee
相关产品推荐
相关产品推荐

