【发布时间】:2021-08-29 19:03:04
【问题描述】:
首先,我在 Stack Overflow 上就这个话题经历了 20 多个不同的问题和解决方案(其中大部分与网页版有关),我也尝试过 twitter,甚至 FlutterDev Discord 服务器也无法好像发现了这个问题。
我正在使用 firebase 对我的应用进行移动身份验证,无论我尝试什么,我似乎都无法让持久身份验证状态在 iOS 或 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 开始,如有必要,我可以分享其他重要的信息。我们在 iOS 上使用 Facebook、Google 和 Apple。
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,
),
))
],
),
),
),
);
}
}
我正在使用provider pub,这是context.read<Object?>() 的来源。
这里是signInWithGoogle函数;
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();
}
这包含在我的 Member 对象中,它只存储所有 Auth 数据以及来自我们内部 API 之一的特定于成员的数据,并且成员数据作为 App State 对象存储在提供程序中,它链接在 main.dart 文件中
getMemberLogin() 函数只是从身份验证中获取 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,
),
],
),
);
}
}
最后,这是主屏幕导航到的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() 用于模态框/alertDialogs,以及诸如 QR 扫描仪之类的东西返回到上一个屏幕。
对不起,如果信息太多,或者我忘记了很多东西。一个多星期以来,我一直在努力解决这个问题,现在有点沮丧。
感谢您的所有回复。
仅仅因为我认为看看我已经看过的内容很重要,这里列出了我看过的其他几个问题,但没有帮助。
This 我相信它的日期为 2020 年 8 月,特别是考虑到 onAuthStateChanges 已更改为流 authStateChanges()。
我也尝试过以文档here 中描述的确切方式实现身份验证,但同样的问题。
我也尝试过使用:
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
【问题讨论】:
-
最后一个方法可以用ConnectionState.active代替connectionstate.waiting
-
Welp 成功了,谢谢......
标签: android ios firebase flutter firebase-authentication