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

如何停止FirebaseAuth的Stream?避免重复触发后端数据请求

解决FirebaseAuth authStateChanges()触发API重复调用的问题

问题原因

authStateChanges()流可能多次发射用户登录状态事件(比如Firebase内部状态同步、组件重建导致StreamBuilder重新监听),而你直接在StreamBuilder的builder回调里调用CallDatabase,每次builder执行且snapshot.hasData为true时都会触发,最终导致重复调用。

解决方案

方案一:用状态标记控制仅调用一次

在页面State类中添加布尔标记,确保只有第一次检测到用户登录时才执行数据库调用,用户退出后重置标记以便下次登录重新触发。

修改后的代码:

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  bool _hasCalledDatabase = false; // 新增标记变量

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: StreamBuilder<User?>(
            stream: FirebaseAuth.instance.authStateChanges(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return const Center(child: Text('Incorrect Username or Password!'));
              } else if (snapshot.hasData) {
                // 仅当未执行过数据库调用时触发
                if (!_hasCalledDatabase) {
                  setState(() {
                    _hasCalledDatabase = true;
                  });
                  Email = FirebaseAuth.instance.currentUser?.email.toString();
                  CallDatabase();
                }

                if (userRole == 'Education') {
                  return eDisclaimer();
                } else if (userRole == 'Packaging') {
                  return pDisclaimer();
                } else if (userRole == 'Welding') {
                  return wDisclaimer();
                }
                return const LoginPage();
              } else {
                // 用户退出时重置标记
                setState(() {
                  _hasCalledDatabase = false;
                });
                return LoginBody();
              }
            }),
      ),
    );
  }
}

// 以下CallAPIs、CallDatabase代码保持不变
Future CallAPIs() async {
  await CustomerCall(userCustomer.toString(), ApiConstants.APIKEY);
  await OpenProjectCall(userCustomer.toString(), ApiConstants.APIKEY);
  await OpenTaskCall(userCustomer.toString(), ApiConstants.APIKEY);

  for (var project in projectlist) {
    await WIPRequestCall(project.job, userCustomer.toString(), ApiConstants.APIKEY);
  }

  Future.delayed(Duration(seconds: 30), () async {
    await ProjectTimelineCall(userCustomer.toString(), ApiConstants.APIKEY);
  });

  await GetPortalFiles(ApiConstants.APIKEY);
}

Future CallDatabase() async{
   await ReadUserinDatabase(Email);
   userCustomer = userCustomer.replaceAll("(", "");
   userCustomer = userCustomer.replaceAll(")", "");
   await CallAPIs();
}

方案二:手动订阅authStateChanges()流(更灵活)

在initState中手动监听状态变化,分离状态逻辑和UI构建,避免StreamBuilder多次触发builder回调。

修改后的代码:

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  StreamSubscription? _authSubscription;
  bool _hasCalledDatabase = false;

  @override
  void initState() {
    super.initState();
    // 手动订阅auth状态流
    _authSubscription = FirebaseAuth.instance.authStateChanges().listen((user) {
      if (user != null && !_hasCalledDatabase) {
        setState(() {
          _hasCalledDatabase = true;
        });
        Email = user.email.toString();
        CallDatabase();
      } else if (user == null) {
        // 用户退出时重置标记
        setState(() {
          _hasCalledDatabase = false;
        });
      }
    });
  }

  @override
  void dispose() {
    _authSubscription?.cancel(); // 页面销毁时取消订阅,避免内存泄漏
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: StreamBuilder<User?>(
            stream: FirebaseAuth.instance.authStateChanges(),
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (snapshot.hasError) {
                return const Center(child: Text('Incorrect Username or Password!'));
              } else if (snapshot.hasData) {
                if (userRole == 'Education') {
                  return eDisclaimer();
                } else if (userRole == 'Packaging') {
                  return pDisclaimer();
                } else if (userRole == 'Welding') {
                  return wDisclaimer();
                }
                return const LoginPage();
              } else {
                return LoginBody();
              }
            }),
      ),
    );
  }
}

// CallAPIs、CallDatabase代码保持不变

额外注意事项

  • 如果userRole是在CallDatabase中异步获取的,当前直接在builder中判断可能会出现空值问题,建议将userRole纳入状态管理(比如用FutureBuilder或状态变量存储)
  • 若页面在CallDatabase执行过程中跳转,可添加上下文判断避免异步操作报错:if (mounted) { ... }

内容的提问来源于stack exchange,提问作者Aiden P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:30:51