Flutter中FutureBuilder未在initState初始化却仍运行,如何阻止?
问题原因
你当前代码里,FutureBuilder的future参数直接传入了getdataCat()的调用——这意味着每次build()方法执行时,都会立刻触发这个函数发起请求。而组件初始化时build()必然会运行,所以哪怕你没在initState里手动调用,它也会自动执行。另外这种写法还会导致重复请求:只要触发setState让build重新执行,就会再次发起新的请求,属于隐藏的bug。
解决方案
要实现「先运行其他函数,再加载数据」的需求,你需要把请求的Future对象单独存储,手动控制它的触发时机:
修改步骤
- 在State类中声明
Future? _dataFuture变量,初始为null,专门用来存储数据请求的Future实例 - 先执行你需要优先运行的前置函数,等它完成后,再调用
getdataCat()并将结果赋值给_dataFuture(通过setState更新状态) - FutureBuilder的
future参数替换为_dataFuture,当_dataFuture为null时,展示前置函数执行中的状态
修改后的完整代码
class MyHomePage extends StatefulWidget { const MyHomePage({Key? key}) : super(key: key); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { @override void initState() { super.initState(); // 先执行你需要优先运行的函数,这里用initOtherFunction示例 initOtherFunction().then((_) { // 前置函数执行完,再触发数据请求 setState(() { _dataFuture = getdataCat(); }); }); } bool showicon = false; var apiURL; Future? _dataFuture; // 存储数据请求的Future对象 // 示例前置函数:替换成你实际需要优先执行的逻辑 Future<void> initOtherFunction() async { // 模拟耗时操作,比如初始化配置、获取前置数据等 await Future.delayed(const Duration(seconds: 2)); print("前置函数执行完成"); } Future getdataCat() async { setState(() { showicon = true; }); apiURL = '***********************'; var response = await http.post(Uri.parse(apiURL)); var responsebody = jsonDecode(response.body); setState(() { showicon = false; }); return responsebody.length > 0 ? responsebody : []; } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( children: <Widget>[ Flexible( child: _dataFuture == null ? const Center(child: Text("正在执行前置操作...")) // 前置函数未完成时的状态 : FutureBuilder( future: _dataFuture, builder: (context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return showicon ? const Center( child: CircularProgressIndicator(color: Colors.black)) : SizedBox( height: 10, child: Center(child: Image.asset('assets/data.png'))); } else if (snapshot.hasError) { return const Center(child: Text("加载出错")); } else if (snapshot.hasData && snapshot.data.isEmpty) { return SizedBox( height: 10, child: Center(child: Image.asset('assets/data.png'))); } else { return GridView.builder( physics: const ScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 3.4), itemCount: snapshot.data.length, itemBuilder: (context, index) { return Container( child: Card( child: Column( children: <Widget>[ Flexible( child: GestureDetector( child: Column(children: <Widget>[ Center(child: Text("${snapshot.data[index]['name']}")) ]), )), ], ), ), ); }, ); } }, ), ) ], ), ), ); } }
关键说明
- 用
_dataFuture存储请求实例,避免了每次build时重复发起请求,同时实现了「按需触发」的效果——只有你主动赋值时,FutureBuilder才会开始处理请求 - 前置函数执行完成后,通过setState更新
_dataFuture,触发build更新FutureBuilder的状态,正式开始加载数据 - 优化了原代码的细节:统一在请求完成后设置
showicon = false,新增了错误状态处理,避免空指针风险
内容的提问来源于stack exchange,提问作者M Al
相关产品推荐
相关产品推荐

