Flutter FutureBuilder反复重建生成新随机数,如何阻止?
解决Flutter FutureBuilder每次重建都重新生成Future的问题
问题根源
你遇到的问题本质是:在build方法中直接调用fetchRandomNumber()创建Future。每次组件重建(比如点击计数器触发setState)时,build方法会重新执行,导致新的Future被创建,FutureBuilder自然会重新请求数据,生成新的随机数。
两种有效解决方案
方案1:将Future作为State成员变量初始化
把Future存在State类的成员变量里,在initState中初始化,确保只执行一次。需要手动刷新时,再重新赋值这个变量并触发setState。
修改后的完整代码
import 'package:flutter/material.dart'; import 'dart:math'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( title: 'FutureBuilder Example', home: FutureBuilderExample(), ); } } class FutureBuilderExample extends StatefulWidget { const FutureBuilderExample({super.key}); @override _FutureBuilderExampleState createState() => _FutureBuilderExampleState(); } class _FutureBuilderExampleState extends State<FutureBuilderExample> { int counter = 0; Future<int>? _randomNumberFuture; // 存储Future的成员变量 Future<int> fetchRandomNumber() async { await Future.delayed(const Duration(milliseconds: 500)); return Random().nextInt(100); } @override void initState() { super.initState(); _randomNumberFuture = fetchRandomNumber(); // 初始化时仅执行一次 } // 手动刷新随机数的方法 void _refreshRandomNumber() { setState(() { _randomNumberFuture = fetchRandomNumber(); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('FutureBuilder with Random Number')), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Counter: $counter', style: const TextStyle(fontSize: 20)), const SizedBox(height: 20), FutureBuilder<int>( future: _randomNumberFuture, // 使用成员变量的Future builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else { return Column( children: [ Text('Random Number: ${snapshot.data}', style: const TextStyle(fontSize: 20)), const SizedBox(height: 10), ElevatedButton( onPressed: _refreshRandomNumber, child: const Text('刷新随机数'), ), ], ); } }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { counter++; // 触发重建,但不会重新生成随机数 }); }, child: const Icon(Icons.add), ), ); } }
关键改动说明
- 新增
_randomNumberFuture成员变量存储Future对象 - 在
initState中初始化Future,确保组件首次加载时仅执行一次数据请求 - FutureBuilder的
future参数改为使用这个成员变量,避免每次build重新创建Future - 添加
_refreshRandomNumber方法,手动触发新的Future创建并更新UI
方案2:使用AsyncMemoizer缓存Future结果
Flutter自带的AsyncMemoizer(位于package:flutter/foundation.dart)可以帮助我们缓存Future的执行结果,确保同一Future仅被执行一次,直到手动清除缓存。
修改后的完整代码
import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; // 引入AsyncMemoizer import 'dart:math'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( title: 'FutureBuilder Example', home: FutureBuilderExample(), ); } } class FutureBuilderExample extends StatefulWidget { const FutureBuilderExample({super.key}); @override _FutureBuilderExampleState createState() => _FutureBuilderExampleState(); } class _FutureBuilderExampleState extends State<FutureBuilderExample> { int counter = 0; final _memoizer = AsyncMemoizer<int>(); // 声明AsyncMemoizer实例 Future<int> fetchRandomNumber() { // 使用runOnce确保Future仅执行一次 return _memoizer.runOnce(() async { await Future.delayed(const Duration(milliseconds: 500)); return Random().nextInt(100); }); } void _refreshRandomNumber() { setState(() { _memoizer.clear(); // 清除缓存,下次调用fetchRandomNumber会重新执行 }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('FutureBuilder with Random Number')), body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Counter: $counter', style: const TextStyle(fontSize: 20)), const SizedBox(height: 20), FutureBuilder<int>( future: fetchRandomNumber(), // 直接调用方法,但runOnce会缓存结果 builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else { return Column( children: [ Text('Random Number: ${snapshot.data}', style: const TextStyle(fontSize: 20)), const SizedBox(height: 10), ElevatedButton( onPressed: _refreshRandomNumber, child: const Text('刷新随机数'), ), ], ); } }, ), ], ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { counter++; }); }, child: const Icon(Icons.add), ), ); } }
关键改动说明
- 引入
AsyncMemoizer并创建实例 - 用
_memoizer.runOnce()包裹原有的异步逻辑,确保逻辑仅执行一次 - 手动刷新时调用
_memoizer.clear()清除缓存,让下次调用重新执行异步逻辑
方案选择建议
- 如果需要灵活控制Future的创建时机(比如手动刷新),优先选方案1,逻辑更直观
- 如果只需要Future执行一次,偶尔需要重置,方案2更简洁,无需额外管理Future变量
内容的提问来源于stack exchange,提问作者MendelG
相关产品推荐
相关产品推荐

