如何阻止Flutter中FutureBuilder重复调用引发重复POST请求报错
问题:FutureBuilder重复调用POST请求导致重复提交报错
我用Dio实现了POST请求方法:
Future<dynamic> postData( String id, String firstName, String lastName, String department) async { final response = await dio.post( url, data: { "id": id, "firstName": firstName, "lastName": lastName, "department": department }, ); return response.data; }
在使用FutureBuilder调用该方法时,首次执行正常,但FutureBuilder会自动重复调用,导致重复提交相同ID,触发“Insert failed, duplicate id”错误。
页面代码如下:
Future<void> main() async { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { final UserRepository _userRepository = UserRepository(); return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: Scaffold( body: FutureBuilder( future: _userRepository.postData("33", "sj", "tt", "it"), builder: (context, snapshot) { print("FutureBuilder builder method called"); // Debug print if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasData) { print('1'); print("Data received: ${snapshot.data.toString()}"); // Debug print print('2'); return Center(child: Text(snapshot.data.toString())); print('3'); } else if (snapshot.hasError) { String errorMessage = snapshot.error.toString(); print("Error occurred: $errorMessage"); // Debug print return Center(child: Text("Error: $errorMessage")); } else { print("Unknown error occurred"); // Debug print return const Center(child: Text("Unknown error occurred")); } }, ), ), ); } }
调试日志显示builder方法重复调用,最终触发重复ID报错:
debug console I/flutter (12062): FutureBuilder builder method called Reloaded 1 of 825 libraries in 393ms (compile: 34 ms, reload: 155 ms, reassemble: 132 ms). I/flutter (12062): FutureBuilder builder method called I/flutter (12062): 1 I/flutter (12062): Data received: {id: 33, firstName: sj, lastName: tt, department: it} I/flutter (12062): 2 D/EGL_emulation(12062): app_time_stats: avg=5496.77ms min=25.07ms max=10968.47ms count=2 Restarted application in 992ms. 2 I/flutter (12062): FutureBuilder builder method called I/flutter (12062): 1 I/flutter (12062): Data received: Error: Insert failed, duplicate id
原因分析
问题核心是直接在build方法中创建Future:Flutter的build方法会在多种场景下重新执行(热重载、布局变化、父组件更新等),每次重建都会调用_userRepository.postData(),发起重复的POST请求。
解决方案
方法1:将Future移到StatefulWidget的initState中
把请求逻辑放到initState生命周期方法里,确保请求只在组件初始化时执行一次:
Future<void> main() async { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { late Future<dynamic> _postFuture; final UserRepository _userRepository = UserRepository(); @override void initState() { super.initState(); // 仅在组件初始化时发起一次请求 _postFuture = _userRepository.postData("33", "sj", "tt", "it"); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: Scaffold( body: FutureBuilder( future: _postFuture, // 使用提前初始化好的Future实例 builder: (context, snapshot) { print("FutureBuilder builder method called"); if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasData) { print('1'); print("Data received: ${snapshot.data.toString()}"); print('2'); return Center(child: Text(snapshot.data.toString())); } else if (snapshot.hasError) { String errorMessage = snapshot.error.toString(); print("Error occurred: $errorMessage"); return Center(child: Text("Error: $errorMessage")); } else { print("Unknown error occurred"); return const Center(child: Text("Unknown error occurred")); } }, ), ), ); } }
方法2:用状态管理控制请求触发时机
如果需要更灵活的请求触发逻辑(比如用户点击按钮后提交),可以使用状态管理工具(如Provider、Bloc),将请求逻辑封装到ViewModel中,确保请求仅在需要时执行:
示例(简化版Provider实现):
- 创建ViewModel类:
class UserViewModel extends ChangeNotifier { final UserRepository _userRepository = UserRepository(); Future<dynamic>? _postFuture; Future<dynamic> submitUser(String id, String firstName, String lastName, String department) { // 确保同一请求只执行一次 if (_postFuture == null) { _postFuture = _userRepository.postData(id, firstName, lastName, department); } return _postFuture!; } }
- 在页面中使用:
class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => UserViewModel(), child: MaterialApp( title: 'Flutter Demo', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: Scaffold( body: Consumer<UserViewModel>( builder: (context, viewModel, child) { return FutureBuilder( future: viewModel.submitUser("33", "sj", "tt", "it"), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasData) { return Center(child: Text(snapshot.data.toString())); } else if (snapshot.hasError) { String errorMessage = snapshot.error.toString(); return Center(child: Text("Error: $errorMessage")); } else { return const Center(child: Text("Unknown error occurred")); } }, ); }, ), ), ), ); } }
方法3:添加请求幂等性处理
如果前端无法完全避免重复触发,可以配合后端做幂等性校验:比如通过唯一请求ID标识,或者当检测到相同ID已存在时返回成功而非报错;前端也可以添加防抖逻辑,用Timer延迟请求,避免短时间内重复提交。
内容的提问来源于stack exchange,提问作者Sanwar Jayswal
相关产品推荐
相关产品推荐

