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
相关产品推荐
相关产品推荐

