簡體   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