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

Flutter接入Firebase Auth时登录状态首次不生效需热重载问题求助

问题根因

问题完全出在Profile页面的状态管理逻辑上,和Login页面没有关联:

  1. 异步监听未触发UI重建:FirebaseAuth.instance.authStateChanges().listen是异步回调,你在回调里修改了_status等变量,但没有调用setState()通知Flutter刷新UI,所以页面首次渲染时用的是初始值_status = null,自然命中else分支;热重载会强制重建页面,此时变量已经被异步回调赋值,所以显示正常。
  2. 重复注册监听器导致内存泄漏:你把userStatus()放在build()方法里调用,每次页面重绘都会新增一个监听事件,长期运行会出现内存泄漏、回调多次触发的问题。
  3. 初始状态判断逻辑有漏洞:_status初始值为null,null == true的判断结果为false,首次渲染必然走未登录分支。

修复方案

修改Profile页面的代码即可解决问题:

class Profile extends StatefulWidget {
  const Profile({Key? key}) : super(key: key);
  @override
  _ProfileState createState() => _ProfileState();
}

class _ProfileState extends State<Profile> {
  String? _email;
  DateTime? _creationTime;
  DateTime? _lastSignIn;
  bool? _status;
  // 定义监听订阅对象,用于页面销毁时取消监听
  StreamSubscription<User?>? _authListener;

  @override
  void initState() {
    super.initState();
    // 仅在页面初始化时注册一次监听
    _authListener = FirebaseAuth.instance.authStateChanges().listen((event) {
      // 变量更新后调用setState通知UI刷新
      setState(() {
        if (event == null) {
          _status = false;
          _email = null;
          _creationTime = null;
          _lastSignIn = null;
        } else {
          _status = true;
          _email = event.email;
          _creationTime = event.metadata.creationTime;
          _lastSignIn = event.metadata.lastSignInTime;
        }
      });
    });
  }

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

  @override
  Widget build(BuildContext context) {
    // 新增加载状态,避免状态未更新时闪现未登录界面
    if (_status == null) {
      return const Scaffold(
        body: Center(child: CircularProgressIndicator()),
      );
    }
    return userForm();
  }

  // 以下userForm、button方法保持原有逻辑不变即可
  Widget userForm() {
    if (_status == true) {
      return Scaffold(
        appBar: defaultAppBar('Profile'),
        body: Center(
          child: ListView(
            padding: const EdgeInsets.all(20),
            children: [
              Container(
                margin: const EdgeInsets.all(30),
                child: Text(
                  'Email: $_email',
                  style: const TextStyle(
                      fontSize: 18, fontWeight: FontWeight.bold),
                ),
              ),
              Container(
                margin: const EdgeInsets.all(30),
                child: Text(
                  'Creation Time: $_creationTime',
                  style: const TextStyle(
                      fontSize: 18, fontWeight: FontWeight.bold),
                ),
              ),
              Container(
                margin: const EdgeInsets.all(30),
                child: Text(
                  'Last Sign-In: $_lastSignIn',
                  style: const TextStyle(
                      fontSize: 18, fontWeight: FontWeight.bold),
                ),
              ),
              const SizedBox(height: 45),
              button('Change password', 1),
              const SizedBox(height: 18),
              button('Change email', 2),
              const SizedBox(height: 18),
              button('Sign-out', 0),
            ],
          ),
        ),
      );
    } else {
      return Center(
        child: ListView(
          padding: const EdgeInsets.all(14),
          children: <Widget>[
            const SizedBox(height: 100),
            Container(
              margin: const EdgeInsets.all(30),
              child: const Center(
                child: Text(
                  'Please sign-in to use this Feature',
                  style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                ),
              ),
            ),
            const SizedBox(height: 50),
            button('Login', 3),
            const SizedBox(height: 18),
            button('Register', 4),
          ],
        ),
      );
    }
  }

  Widget button(String txt, int _nav) {
    Color? _color = const Color(0xFF20348F);
    if (_nav == 0) {
      _color = Colors.red;
    }
    return TextButton(
      style: TextButton.styleFrom(
        primary: _color,
        textStyle: const TextStyle(
          fontSize: 20,
        ),
      ),
      child: Text(txt),
      onPressed: () {
        if (_nav == 0) {
          FirebaseAuth.instance.signOut();
          Navigator.pushAndRemoveUntil(
            context,
            MaterialPageRoute(
              builder: (BuildContext context) => const SelectUser(),
            ),
            (route) => false,
          );
        } else {
          Navigator.push(context, MaterialPageRoute(builder: (contex) {
            if (_nav == 1) {
              return const EditPass();
            } else if (_nav == 2) {
              return const EditEmail();
            } else if (_nav == 3) {
              return const Login();
            } else {
              return const Register();
            }
          }));
        }
      },
    );
  }
}

额外优化建议

如果多个页面都需要用到用户登录状态,建议把Auth监听放在根Widget层级,用Provider、Bloc这类状态管理工具统一管理用户状态,避免每个页面单独做监听,减少重复代码和出错概率。

内容的提问来源于stack exchange,提问作者Johnny Megg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 11:54:03