繁体   English   中英

Firebase auth 不在 iOS 或 ZC047B10EEE763AFB6164E077CFCC12 中的 Android 上持续存在

[英]Firebase auth not persisting on iOS or Android in Flutter

首先,我在 Stack Overflow 上经历了 20 多个关于此主题的不同问题和解决方案(其中大部分与 web 版本有关),我也尝试过 twitter,甚至 FlutterDev Z8F5CC6430613F1C1976 服务器似乎找不到这个问题。

I am using firebase for mobile authentication for my app, and no matter what I try, I cannot seem to get the persistent auth state to work on iOS or Android.

这是我的主要内容:

Future<void> main() async {
    WidgetsFlutterBinding.ensureInitialized();
    await Firebase.initializeApp();
    runApp(
        MultiProvider(
            ...
        child: const MyApp(),
        ),
    );
}

class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);
    final const ColorScheme colorScheme = ColorScheme(
        ...
    );
    
    @override
    Widget build(BuildContext context) {
        bool isDebug = false;
        if (Constants.DEBUG_BANNER == 'true') {
            isDebug = true;
        }
        return MaterialApp(
            theme: ThemeData(
                ...
            ),
            routes: {
               // This is a general layout of how all my routes are in case this is the issue
               Screen.route: (BuildContext context) => const Screen(),
            },
            home: const HomeScreen(),
            debugShowCheckModeBanner: isDebug,
        );
    }
}

...只是我认为与我的问题无关的代码,因此为了简洁起见,我将其隐藏起来。 主要是主题和私人数据

让我们从我的 google-sign-in-button 开始,如果有必要,我可以分享其他人,如果它很重要。 我们正在使用 Facebook、Google 和 Apple 用于 iOS。

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

  @override
  _GoogleSignInButtonState createState() => _GoogleSignInButtonState();
}

class _GoogleSignInButtonState extends State<GoogleSignInButton> {
  bool _isSigningIn = false;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 16.0),
      child: _isSigningIn
          ? CircularProgressIndicator(
              valueColor: AlwaysStoppedAnimation<Color>(MRRM.colorScheme.primary),
            )
          : OutlinedButton(
              key: const Key('google_sign_in_button'),
              style: ButtonStyle(
                backgroundColor: MaterialStateProperty.all(Colors.white),
                shape: MaterialStateProperty.all(
                  RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(40),
                  ),
                ),
              ),
              onPressed: () async {
                setState(() {
                  _isSigningIn = true;
                });

                context.read<Member>().signInWithGoogle(context: context).then<void>((void user) {
                  setState(() {
                    _isSigningIn = false;
                  });

                  Navigator.pushReplacementNamed(context, UserInfoScreen.route);
                });
              },
              child: Padding(
                padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    const Image(
                      image: AssetImage('assets/images/png/google_logo.png'),
                      height: 35.0,
                    ),
                    Padding(
                        padding: const EdgeInsets.only(left: 10),
                        child: Text(
                          'Sign in with Google',
                          style: TextStyle(
                            fontSize: 20,
                            color: MRRM.colorScheme.secondary,
                            fontWeight: FontWeight.w600,
                          ),
                        ))
                  ],
                ),
              ),
            ),
    );
  }
}

我正在使用提供者pub,这就是context.read<Object?>()的来源。

这是signInWithGoogle function;

Future<String> signInWithGoogle({required BuildContext context}) async {
    final FirebaseAuth _auth = FirebaseAuth.instance;

    final GoogleSignIn googleSignIn = GoogleSignIn();

    final GoogleSignInAccount? googleSignInAccount =
        await googleSignIn.signIn();

    if (googleSignInAccount != null) {
      final GoogleSignInAuthentication googleSignInAuthentication =
          await googleSignInAccount.authentication;

      final AuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleSignInAuthentication.accessToken,
        idToken: googleSignInAuthentication.idToken,
      );

      try {
        final UserCredential userCredential =
            await _auth.signInWithCredential(credential);

        _firebaseUser = userCredential.user!;
        _authType = AuthType.Google;
        _uuId = _firebaseUser.uid;
        notifyListeners();
      } on FirebaseAuthException catch (e) {
        if (e.code == 'account-exists-with-different-credential') {
          ScaffoldMessenger.of(context).showSnackBar(
            customSnackBar(
              content: 'The account already exists with different credentials.',
            ),
          );
        } else if (e.code == 'invalid-credential') {
          ScaffoldMessenger.of(context).showSnackBar(
            customSnackBar(
              content: 'Error occurred while accessing credentials. Try again.',
            ),
          );
        }
      } catch (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          customSnackBar(
            content: 'Error occurred using Google Sign-In. Try again.',
          ),
        );
      }
    }
    return getMemberLogin();
  }

这包含在我的会员 object 中,它只存储所有 Auth 数据以及来自我们内部 API 之一的会员特定数据,并且会员数据存储为应用程序 State ZA8CFDE6331BD59EB2AC96F8911 中链接在提供程序中main.dart文件

getMemberLogin() function 只是从身份验证中获取 UUID 并将其发送到 API 并获取内部成员数据,我希望一个简单的发布请求不是导致这种情况的原因。 但如果您认为它可能会让我知道,我会尝试在混淆任何 NDA 相关数据的同时发布它。

这是处理初始路由并转到loadingScreen屏幕的主屏幕/启动屏幕,该屏幕应该检查是否存在持久登录并转到 UserInfo 屏幕而不是 Auth 屏幕。

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);
  static const String route = '/home';

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        key: const Key('Home'),
        children: <Widget>[
          Expanded(
            child: Image.asset('assets/images/png/Retail_Rebel_Primary.png'),
          ),
          BlinkingTextButton(
            key: const Key('blinking_text_button'),
            textButton: TextButton(
              child: Text(
                'Tap to continue',
                style: TextStyle(
                  color: MRRM.colorScheme.primary,
                  fontSize: 16.0,
                ),
              ),
              onPressed: () {
                Navigator.of(context).pushReplacementNamed(LoadingScreen.route);
              },
            ),
          ),
          Container(
            height: 8.0,
          ),
        ],
      ),
    );
  }
}

最后,这是 HomeScreen 导航到的LoadingScreen

class LoadingScreen extends StatelessWidget {
  const LoadingScreen({Key? key}) : super(key: key);
  static const String route = '/loadingScreen';
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          if (snapshot.hasData) {
            print('user is logged in');
            SchedulerBinding.instance!.addPostFrameCallback((_) {
              Navigator.of(context).pushReplacementNamed(UserInfoScreen.route);
            });
            return const Text('');
          } else {
            print('no user is logged in');
            SchedulerBinding.instance!.addPostFrameCallback((_) {
              Navigator.of(context).pushReplacementNamed(AuthScreen.route);
            });
            return const Text('');
          }
        }
        return const SplashScreen();
      },
    );
  }
}

不确定我处理路由的方式是否可能是问题,但对我来说使用Navigator.of(context).pushReplacementNamed();是很常见的。 除非需要弹出,否则我通常只会使用Navigator.of(context).pop(); . 我通常只将.pop()用于 modals/alertDialogs,以及 QR 扫描仪之类的东西返回到上一个屏幕。

抱歉,如果这是太多信息,或者我忘记了很多东西。 一个多星期以来,我一直在努力解决这个问题,我有点沮丧。

感谢您的所有回复。

仅仅因为我认为看看我已经看过的内容很重要,这里列出了我看过的其他几个没有帮助的问题。

我相信这个日期为 2020 年 8 月,特别是考虑到onAuthStateChanges已更改为 stream authStateChanges()

我也尝试过以此处文档中描述的确切方式实现身份验证,但同样的问题。

我也尝试过使用:

FirebaseAuth.instance.authStateChanges().then((User? user) {
    if (user != null) {
       Navigator.of(context).pushReplacementNamed(UserInfoScreen.route);
    } else {
       Navigator.of(context).pushReplacementNamed(AuthScreen.route);
    }

这没有用。 我还试图简单地检查是否有当前用户:

User user = FirebaseAuth.instance.currentUser;
if (user != null && user.uid != null) {
    Navigator.of(context).pushReplacementNamed(UserInfoScreen.route);
} else {
    Navigator.of(context).pushReplacementNamed(AuthScreen.route);
}

仍然总是去AuthScreen我也尝试了所有这些方法作为异步任务,看看是否可能只需要一秒钟的时间来加载,以及同样的问题。 最奇怪的是使用当前方法,如果我从LoadingScreen中取出if(snapshot.connectionState == ConnectionState.waiting)它将打印出no user is logged in紧接着user is logged in ,然后no user is logged in再次,然后它将导航到AuthScreen

如果您按照我在上面所做的操作并进行一次更改,它将适用于持久登录。

改变:

class LoadingScreen extends StatelessWidget {
  const LoadingScreen({Key? key}) : super(key: key);
  static const String route = '/loadingScreen';
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          if (snapshot.hasData) {
            print('user is logged in');
            SchedulerBinding.instance!.addPostFrameCallback((_) {
              Navigator.of(context).pushReplacementNamed(UserInfoScreen.route);
            });
            return const Text('');
          } else {
            print('no user is logged in');
            SchedulerBinding.instance!.addPostFrameCallback((_) {
              Navigator.of(context).pushReplacementNamed(AuthScreen.route);
            });
            return const Text('');
          }
        }
        return const SplashScreen();
      },
    );
  }
}

class LoadingScreen extends StatelessWidget {
  const LoadingScreen({Key? key}) : super(key: key);
  static const String route = '/loadingScreen';
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          if (snapshot.hasData) {
            print('user is logged in');
            SchedulerBinding.instance!.addPostFrameCallback((_) {
              Navigator.of(context).pushReplacementNamed(UserInfoScreen.route);
            });
            return const Text('');
          } else {
            print('no user is logged in');
            SchedulerBinding.instance!.addPostFrameCallback((_) {
              Navigator.of(context).pushReplacementNamed(AuthScreen.route);
            });
            return const Text('');
          }
        }
        return const SplashScreen();
      },
    );
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM