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

Flutter:避免在build()内用带用户参数的Future重复调用API

解决Flutter中依赖用户参数的FutureBuilder重复请求问题

方案1:在State中缓存Future,仅当参数变化时触发请求

在StatefulWidget的状态类里维护一个_navigationFuture变量,只有当用户选择的pointToGoTo参数发生变化时,才重新发起API请求并更新这个变量。让FutureBuilder复用缓存的Future,彻底避免build重建时重复调用API。

代码示例:

class YourPage extends StatefulWidget {
  final LatLng? initialPoint;

  const YourPage({super.key, this.initialPoint});

  @override
  State<YourPage> createState() => _YourPageState();
}

class _YourPageState extends State<YourPage> {
  Future<List<LatLng>>? _navigationFuture;
  LatLng? _currentPoint;

  @override
  void initState() {
    super.initState();
    _currentPoint = widget.initialPoint;
    if (_currentPoint != null) {
      _navigationFuture = Provider.of<NavigationProvider>(context, listen: false)
          .getNavigationPoints(_currentPoint!);
    }
  }

  @override
  void didUpdateWidget(covariant YourPage oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.initialPoint != oldWidget.initialPoint && widget.initialPoint != null) {
      _updateNavigationFuture(widget.initialPoint!);
    }
  }

  void _updateNavigationFuture(LatLng newPoint) {
    if (newPoint != _currentPoint) {
      setState(() {
        _currentPoint = newPoint;
        _navigationFuture = Provider.of<NavigationProvider>(context, listen: false)
            .getNavigationPoints(newPoint);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 这里放用户选择pointToGoTo的UI组件,选择后调用_updateNavigationFuture
        Expanded(
          child: _navigationFuture == null
              ? const Center(child: Text("请选择目标点"))
              : FutureBuilder<List<LatLng>>(
                  future: _navigationFuture,
                  builder: (context, snapshot) {
                    if (snapshot.hasData) {
                      return PolylineLayer(
                        polylines: [
                          Polyline(
                              points: snapshot.data!,
                              strokeWidth: 4,
                              color: Colors.purple),
                        ],
                      );
                    } else if (snapshot.hasError) {
                      return Text("${snapshot.error}");
                    } else {
                      return const Center(child: CircularProgressIndicator());
                    }
                  },
                ),
        ),
      ],
    );
  }
}

方案2:将请求逻辑移至Provider中管理

把请求逻辑封装到NavigationProvider内部,维护当前请求的参数、Future和结果,仅当参数变化时发起新请求。页面只需监听Provider的状态变化,无需直接在build中创建Future。

修改NavigationProvider示例:

class NavigationProvider extends ChangeNotifier {
  LatLng? _currentRequestPoint;
  Future<List<LatLng>>? _currentNavigationFuture;
  List<LatLng>? _navigationResult;
  Object? _navigationError;

  Future<List<LatLng>> getNavigationPoints(LatLng point) async {
    if (_currentRequestPoint == point && _currentNavigationFuture != null) {
      return _currentNavigationFuture!;
    }

    _currentRequestPoint = point;
    _navigationError = null;
    _navigationResult = null;
    notifyListeners();

    try {
      _currentNavigationFuture = _fetchNavigationFromApi(point);
      final result = await _currentNavigationFuture!;
      _navigationResult = result;
      return result;
    } catch (e) {
      _navigationError = e;
      rethrow;
    } finally {
      notifyListeners();
    }
  }

  Future<List<LatLng>> _fetchNavigationFromApi(LatLng point) async {
    // 替换为你的实际API请求逻辑
    await Future.delayed(const Duration(seconds: 1));
    return [const LatLng(0,0), const LatLng(1,1)];
  }

  List<LatLng>? get navigationResult => _navigationResult;
  Object? get navigationError => _navigationError;
  bool get isLoading => _currentRequestPoint != null && _navigationResult == null && _navigationError == null;
}

页面使用方式:

@override
Widget build(BuildContext context) {
  final navProvider = Provider.of<NavigationProvider>(context);

  return Column(
    children: [
      // 用户选择参数的组件,选择后调用navProvider.getNavigationPoints(newPoint)
      Expanded(
        child: Builder(
          builder: (context) {
            if (navProvider.isLoading) {
              return const Center(child: CircularProgressIndicator());
            } else if (navProvider.navigationError != null) {
              return Text("${navProvider.navigationError}");
            } else if (navProvider.navigationResult != null) {
              return PolylineLayer(
                polylines: [
                  Polyline(
                      points: navProvider.navigationResult!,
                      strokeWidth: 4,
                      color: Colors.purple),
                ],
              );
            } else {
              return const Center(child: Text("请选择目标点"));
            }
          },
        ),
      ),
    ],
  );
}

方案3:使用ValueNotifier封装Future

用ValueNotifier<Future<List<LatLng>>?>管理Future,仅当用户选择新参数时更新notifier的值,FutureBuilder监听这个notifier,彻底和build方法解耦。

代码示例:

class _YourPageState extends State<YourPage> {
  final ValueNotifier<Future<List<LatLng>>?> _futureNotifier = ValueNotifier(null);
  LatLng? _currentPoint;

  void _updateNavigationFuture(LatLng newPoint) {
    if (newPoint != _currentPoint) {
      _currentPoint = newPoint;
      _futureNotifier.value = Provider.of<NavigationProvider>(context, listen: false)
          .getNavigationPoints(newPoint);
    }
  }

  @override
  void dispose() {
    _futureNotifier.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // 用户选择参数的组件
        Expanded(
          child: ValueListenableBuilder<Future<List<LatLng>>?>(
            valueListenable: _futureNotifier,
            builder: (context, future, child) {
              if (future == null) {
                return const Center(child: Text("请选择目标点"));
              }
              return FutureBuilder<List<LatLng>>(
                future: future,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    return PolylineLayer(
                      polylines: [
                        Polyline(
                            points: snapshot.data!,
                            strokeWidth: 4,
                            color: Colors.purple),
                      ],
                    );
                  } else if (snapshot.hasError) {
                    return Text("${snapshot.error}");
                  } else {
                    return const Center(child: CircularProgressIndicator());
                  }
                },
              );
            },
          ),
        ),
      ],
    );
  }
}

核心思路总结

所有方案的核心都是将Future的创建与build方法解耦,仅当用户选择的参数变化时才重新创建Future,避免build重建时无意义地重复发起API请求。

内容的提问来源于stack exchange,提问作者Yassin H Rassul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 10:01:16