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

如何阻止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实现):

  1. 创建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!;
  }
}
  1. 在页面中使用:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 02:14:55