You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:53:10