简体   繁体   English

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

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

To start, I have gone through more than 20 different questions and solutions here on Stack Overflow about this topic (most of them are related to the web version), I have also tried twitter, and even the FlutterDev Discord server and cannot seem to find this issue.首先,我在 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. 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.

Here is my main:这是我的主要内容:

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,
        );
    }
}

the ... is just code that I think is unrelated to my question and so I am hiding it for brevity. ...只是我认为与我的问题无关的代码,因此为了简洁起见,我将其隐藏起来。 Mostly themes, and private data主要是主题和私人数据

Let's just start with my google-sign-in-button and if necessary I can share others if it is important.让我们从我的 google-sign-in-button 开始,如果有必要,我可以分享其他人,如果它很重要。 We are using Facebook, Google, and Apple for iOS.我们正在使用 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,
                          ),
                        ))
                  ],
                ),
              ),
            ),
    );
  }
}

I am using the provider pub, which is what context.read<Object?>() is from.我正在使用提供者pub,这就是context.read<Object?>()的来源。

Here is the signInWithGoogle function;这是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();
  }

This is contained in my Member object, which just stores all of the Auth data as well as the Member specific data that comes from one of our internal API's, and the member data is stored as an App State object in provider, which is linked in the main.dart file这包含在我的会员 object 中,它只存储所有 Auth 数据以及来自我们内部 API 之一的会员特定数据,并且会员数据存储为应用程序 State ZA8CFDE6331BD59EB2AC96F8911 中链接在提供程序中main.dart文件

The getMemberLogin() function is just taking the UUID from the auth and sending it to an API and getting internal member data, I would hope that a simple post request isn't what is causing this. getMemberLogin() function 只是从身份验证中获取 UUID 并将其发送到 API 并获取内部成员数据,我希望一个简单的发布请求不是导致这种情况的原因。 but if you think it might let me know and I will try to post it while obfuscating any NDA related data.但如果您认为它可能会让我知道,我会尝试在混淆任何 NDA 相关数据的同时发布它。

This is the home/splash Screen that handles the initial routing and goes to the loadingScreen that is supposed to be checking if there is a persisted login and going to the UserInfo screen instead of the Auth Screen.这是处理初始路由并转到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,
          ),
        ],
      ),
    );
  }
}

And lastly, this is the LoadingScreen that the HomeScreen navigates to:最后,这是 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();
      },
    );
  }
}

Not sure if possibly the way that I am handing routing may be the issue, but it is very common for me to use Navigator.of(context).pushReplacementNamed();不确定我处理路由的方式是否可能是问题,但对我来说使用Navigator.of(context).pushReplacementNamed();是很常见的。 unless popping is necessary then I will typically just use Navigator.of(context).pop();除非需要弹出,否则我通常只会使用Navigator.of(context).pop(); . . I usually only use .pop() for modals/alertDialogs, and for things like QR scanners to return to previous screen.我通常只将.pop()用于 modals/alertDialogs,以及 QR 扫描仪之类的东西返回到上一个屏幕。

Sorry if this is too much info, or I forgot a ton of stuff.抱歉,如果这是太多信息,或者我忘记了很多东西。 I have been working on trying to get this fixed for a little over a week now and am kind of getting frustrated.一个多星期以来,我一直在努力解决这个问题,我有点沮丧。

Thank you for any and all responses.感谢您的所有回复。

Just because I think it is important to see what I have looked at already, here is a list of a couple of other questions I have looked through that did not help.仅仅因为我认为看看我已经看过的内容很重要,这里列出了我看过的其他几个没有帮助的问题。

This one I believe is dated as of August 2020, especially considering that onAuthStateChanges has been changed to a stream authStateChanges() .我相信这个日期为 2020 年 8 月,特别是考虑到onAuthStateChanges已更改为 stream authStateChanges()

I have also tried just implementing auth in the exact way described in the docs here but same issue.我也尝试过以此处文档中描述的确切方式实现身份验证,但同样的问题。

I also tried just using:我也尝试过使用:

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

Which didn't work.这没有用。 I have also attempted to just simply check if there is a current user with:我还试图简单地检查是否有当前用户:

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

which still always went to AuthScreen I have also tried all of these methods as asynchronous tasks to see if maybe it is just taking a second to load, and same issue.仍然总是去AuthScreen我也尝试了所有这些方法作为异步任务,看看是否可能只需要一秒钟的时间来加载,以及同样的问题。 The weirdest one is with the current method if I take out the if(snapshot.connectionState == ConnectionState.waiting) from the LoadingScreen it will print out no user is logged in immediately followed by user is logged in and then no user is logged in again and then it will navigate to AuthScreen最奇怪的是使用当前方法,如果我从LoadingScreen中取出if(snapshot.connectionState == ConnectionState.waiting)它将打印出no user is logged in紧接着user is logged in ,然后no user is logged in再次,然后它将导航到AuthScreen

If you follow what I have done up above, and make a single change, it will work with persisted logins.如果您按照我在上面所做的操作并进行一次更改,它将适用于持久登录。

change:改变:

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();
      },
    );
  }
}

to

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