我正在尝试实施google登录/注销,但是我的注销无法正常工作。我收到一个错误signOut()在null上被调用。当用户登录后我打印出用户时,我确实获得了所有正确的信息,但是当我在退出功能中打印出时,它说它为空。自定义Firebase用户确实可以工作。这是我的auth.dart文件:
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<FirebaseUser> getCurrentUser();
Future<void> sendEmailVerification();
Future<void> signOut();
Future<bool> isEmailVerified();
Future<String> signInWithGoogle();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
FirebaseUser user;
Future<String> signIn(String email, String password) async {
user = (await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password)).user;
return user.email;
}
Future<String> signUp(String email, String password) async {
FirebaseUser user = (await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password)).user;
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
user = await _firebaseAuth.currentUser();
return user;
}
signOut() async {
//print("signed in user: ${authService.user}");
await _firebaseAuth.signOut();
}
Future<void> sendEmailVerification() async {
FirebaseUser user = await _firebaseAuth.currentUser();
user.sendEmailVerification();
}
Future<bool> isEmailVerified() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.isEmailVerified;
}
Future<String> signInWithGoogle() async {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
user = (await _firebaseAuth.signInWithCredential(credential)).user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _firebaseAuth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
}
还有一点奇怪的是,如果我启动该应用程序并已经登录(以google身份登录),则我点击了退出按钮,它似乎可以正常工作。我的控制台中什么也没有,但它会返回登录屏幕。然后,如果我以Google身份登录并退出,则错误开始发生。另一个怪异的事情是,如果我碰巧已经登录并且一次单击就让它注销,如果我在android studio中重新启动我的应用程序而没有任何更改,它将带我回到我应该已经登录的屏幕。这仅在Google注销(而不是Firebase注销)上发生。 知道我做错了什么吗?谢谢
pubspec.yaml
dependencies:
firebase_auth: ^0.14.0+5
firebase_database: ^3.0.7
google_sign_in: ^4.0.7
firebase_storage:
image_picker:
cloud_firestore:
shared_preferences:
fluttertoast:
cached_network_image:
intl:
我使用它的一个页面:(我有很多页面,但是实现方式与此类似,并且在我的任何页面中都无法使用)
import 'package:flutter/material.dart';
import 'package:pet_helper/chat.dart';
import 'package:pet_helper/lost_and_found.dart';
import 'package:pet_helper/pet_adoption.dart';
import 'authentication.dart';
class HomePage extends StatefulWidget {
HomePage({Key key, this.auth, this.userId, this.onSignedOut})
: super(key: key);
final BaseAuth auth;
final VoidCallback onSignedOut;
final String userId;
@override
State<StatefulWidget> createState() => new _HomePageState();
}
class _HomePageState extends State<HomePage> {
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
int _currentIndex = 0;
@override
void initState() {
super.initState();
}
final List<Widget> _children = [
new LostAndFoundPage(),
new PetAdoptionPage(),
new ChatPage(),
];
_signOut() async {
try {
await widget.auth.signOut();
widget.onSignedOut();
} catch (e) {
print(e);
}
}
onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
automaticallyImplyLeading:false,
title: new Text('Pet Helper'),
actions: <Widget>[
new FlatButton(
child: new Text('Logout',
style: new TextStyle(fontSize: 17.0, color: Colors.white)),
onPressed: _signOut)
],
),
body: _children[_currentIndex], // new
bottomNavigationBar: BottomNavigationBar(
onTap: onTabTapped, // new
currentIndex: _currentIndex, // new
items: [
new BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('Lost & Found'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.pets),
title: Text('Pet Adoption'),
),
new BottomNavigationBarItem(
icon: Icon(Icons.person), title: Text('Chat'))
],
));
}
}
为了完成,这是我的登录页面:
import 'package:flutter/material.dart';
import 'package:pet_helper/home_page.dart';
import 'authentication.dart';
class LoginSignUpPage extends StatefulWidget {
LoginSignUpPage({this.auth, this.onSignedIn});
final BaseAuth auth;
final VoidCallback onSignedIn;
@override
State<StatefulWidget> createState() => new _LoginSignUpPageState();
}
enum FormMode { LOGIN, SIGNUP }
class _LoginSignUpPageState extends State<LoginSignUpPage> {
final _formKey = new GlobalKey<FormState>();
String _email;
String _password;
String _errorMessage;
// Initial form is login form
FormMode _formMode = FormMode.LOGIN;
bool _isIos;
bool _isLoading;
// Check if form is valid before perform login or signup
bool _validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
// Perform login or signup
void _validateAndSubmit() async {
setState(() {
_errorMessage = "";
_isLoading = true;
});
if (_validateAndSave()) {
String userId = "";
try {
if (_formMode == FormMode.LOGIN) {
userId = await widget.auth.signIn(_email, _password);
print('Signed in: $userId');
} else {
userId = await widget.auth.signUp(_email, _password);
widget.auth.sendEmailVerification();
_showVerifyEmailSentDialog();
print('Signed up user: $userId');
}
setState(() {
_isLoading = false;
});
if (userId.length > 0 && userId != null && _formMode == FormMode.LOGIN) {
widget.onSignedIn();
}
} catch (e) {
print('Error: $e');
setState(() {
_isLoading = false;
if (_isIos) {
_errorMessage = e.details;
} else
_errorMessage = 'Incorrect user or password';
});
}
}
}
@override
void initState() {
_errorMessage = "";
_isLoading = false;
super.initState();
}
void _changeFormToSignUp() {
_formKey.currentState.reset();
_errorMessage = "";
setState(() {
_formMode = FormMode.SIGNUP;
});
}
void _changeFormToLogin() {
_formKey.currentState.reset();
_errorMessage = "";
setState(() {
_formMode = FormMode.LOGIN;
});
}
@override
Widget build(BuildContext context) {
_isIos = Theme.of(context).platform == TargetPlatform.iOS;
return new Scaffold(
appBar: new AppBar(
title: new Text('Pet Helper'),
),
body: Stack(
children: <Widget>[
_showBody(),
_showCircularProgress(),
],
));
}
Widget _showCircularProgress(){
if (_isLoading) {
return Center(child: CircularProgressIndicator());
} return Container(height: 0.0, width: 0.0,);
}
void _showVerifyEmailSentDialog() {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Verify your account"),
content: new Text("Link to verify account has been sent to your email"),
actions: <Widget>[
new FlatButton(
child: new Text("Dismiss"),
onPressed: () {
_changeFormToLogin();
Navigator.of(context).pop();
},
),
],
);
},
);
}
Widget _showBody(){
return new Container(
padding: EdgeInsets.all(16.0),
child: new Form(
key: _formKey,
child: new ListView(
shrinkWrap: true,
children: <Widget>[
_showLogo(),
_showEmailInput(),
_showPasswordInput(),
_showErrorMessage(),
_showPrimaryButton(),
_showSecondaryButton(),
_googleSignInButton(),
],
),
));
}
Widget _showErrorMessage() {
if (_errorMessage.length > 0 && _errorMessage != null) {
return new Text(
_errorMessage,
style: TextStyle(
fontSize: 13.0,
color: Colors.red,
height: 1.0,
fontWeight: FontWeight.w300),
);
} else {
return new Container(
height: 0.0,
);
}
}
Widget _showLogo() {
return new Hero(
tag: 'hero',
child: Padding(
padding: EdgeInsets.fromLTRB(0.0, 30.0, 0.0, 0.0),
child: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 120.0,
child: Image.asset('assets/babies.png'),
),
),
);
}
Widget _showEmailInput() {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 25.0, 0.0, 0.0),
child: new TextFormField(
maxLines: 1,
keyboardType: TextInputType.emailAddress,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Email',
icon: new Icon(
Icons.mail,
color: Colors.grey,
)),
validator: (String value) {
if (value.isEmpty) {
_isLoading = false;
return 'Email can\'t be empty';
}
else{
return null;
}
},
onSaved: (value) => _email = value.trim(),
),
);
}
Widget _showPasswordInput() {
return Padding(
padding: const EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
child: new TextFormField(
maxLines: 1,
obscureText: true,
autofocus: false,
decoration: new InputDecoration(
hintText: 'Password',
icon: new Icon(
Icons.lock,
color: Colors.grey,
)),
validator: (String value) {
if (value.isEmpty) {
_isLoading = false;
return 'Password can\'t be empty';
}
else{
return null;
}
},
onSaved: (value) => _password = value.trim(),
),
);
}
Widget _showSecondaryButton() {
return new FlatButton(
child: _formMode == FormMode.LOGIN
? new Text('Create an account',
style: new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300))
: new Text('Have an account? Sign in',
style:
new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)),
onPressed: _formMode == FormMode.LOGIN
? _changeFormToSignUp
: _changeFormToLogin,
);
}
Widget _showPrimaryButton() {
return new Padding(
padding: EdgeInsets.fromLTRB(0.0, 35.0, 0.0, 0.0),
child: SizedBox(
height: 40.0,
child: new RaisedButton(
elevation: 5.0,
shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(30.0)),
color: Colors.blue,
child: _formMode == FormMode.LOGIN
? new Text('Login',
style: new TextStyle(fontSize: 20.0, color: Colors.white))
: new Text('Create account',
style: new TextStyle(fontSize: 20.0, color: Colors.white)),
onPressed: _validateAndSubmit,
),
));
}
void submitGoogleLogin() async{
setState(() {
_errorMessage = "";
_isLoading = true;
});
String userId = "";
userId = await widget.auth.signInWithGoogle().whenComplete(() {
widget.onSignedIn();
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return new HomePage();
},
),
);
});
print('Signed in: $userId');
}
Widget _googleSignInButton() {
return OutlineButton(
splashColor: Colors.grey,
onPressed: submitGoogleLogin,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)),
highlightElevation: 0,
borderSide: BorderSide(color: Colors.grey),
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image(image: AssetImage("assets/google_logo.png"), height: 30.0),
Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
'Sign in with Google',
style: TextStyle(
fontSize: 15,
color: Colors.grey,
),
),
)
],
),
),
);
}
}
哦,根页面会更改用户状态:
import 'package:flutter/material.dart';
import 'login_signup_page.dart';
import 'authentication.dart';
import 'home_page.dart';
class RootPage extends StatefulWidget {
RootPage({this.auth});
final BaseAuth auth;
@override
State<StatefulWidget> createState() => new _RootPageState();
}
enum AuthStatus {
NOT_DETERMINED,
NOT_LOGGED_IN,
LOGGED_IN,
}
class _RootPageState extends State<RootPage> {
AuthStatus authStatus = AuthStatus.NOT_DETERMINED;
String _userId = "";
@override
void initState() {
super.initState();
widget.auth.getCurrentUser().then((user) {
setState(() {
if (user != null) {
_userId = user?.uid;
}
authStatus =
user?.uid == null ? AuthStatus.NOT_LOGGED_IN : AuthStatus.LOGGED_IN;
});
});
}
void _onLoggedIn() {
widget.auth.getCurrentUser().then((user){
setState(() {
_userId = user.uid.toString();
});
});
setState(() {
authStatus = AuthStatus.LOGGED_IN;
});
}
void _onSignedOut() {
setState(() {
authStatus = AuthStatus.NOT_LOGGED_IN;
_userId = "";
});
}
Widget _buildWaitingScreen() {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
),
);
}
@override
Widget build(BuildContext context) {
switch (authStatus) {
case AuthStatus.NOT_DETERMINED:
return _buildWaitingScreen();
break;
case AuthStatus.NOT_LOGGED_IN:
return new LoginSignUpPage(
auth: widget.auth,
onSignedIn: _onLoggedIn,
);
break;
case AuthStatus.LOGGED_IN:
if (_userId.length > 0 && _userId != null) {
return new HomePage(
userId: _userId,
auth: widget.auth,
onSignedOut: _onSignedOut,
);
} else return _buildWaitingScreen();
break;
default:
return _buildWaitingScreen();
}
}
}
初始化根页面 Main.dart
void main() async{
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Pet Helper',
debugShowCheckedModeBanner: false,
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new RootPage(auth: new Auth()));
}
}
答案 0 :(得分:3)
abstract class BaseAuth {
Future<String> signIn(String email, String password);
Future<String> signUp(String email, String password);
Future<FirebaseUser> getCurrentUser();
Future<void> sendEmailVerification();
Future<void> signOut();
Future<bool> isEmailVerified();
Future<String> signInWithGoogle();
void signOutGoogle();
}
class Auth implements BaseAuth {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
FirebaseUser user;
Future<String> signIn(String email, String password) async {
user = (await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password)).user;
return user.email;
}
Future<String> signUp(String email, String password) async {
FirebaseUser user = (await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password)).user;
return user.uid;
}
Future<FirebaseUser> getCurrentUser() async {
user = await _firebaseAuth.currentUser();
return user;
}
signOut() async {
print("signed in user: $user");
await _firebaseAuth.signOut();
}
Future<void> sendEmailVerification() async {
FirebaseUser user = await _firebaseAuth.currentUser();
user.sendEmailVerification();
}
Future<bool> isEmailVerified() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user.isEmailVerified;
}
Future<String> signInWithGoogle() async {
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
user = (await _firebaseAuth.signInWithCredential(credential)).user;
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _firebaseAuth.currentUser();
assert(user.uid == currentUser.uid);
return 'signInWithGoogle succeeded: $user';
}
}
final Auth authService = Auth(); // add this to the bottom outside the class
所以让我解释一下。每次调用Auth()
时,它都会创建该类的新实例。因此,在一种情况下,您可以登录用户。在另一种情况下,您可以注销用户。变量将包含变量的不同值。因此,如果您使用与注销用户不同的实例来注销用户,则user变量将为null,因此不允许您注销用户并输出null。
解决方案是一个全局变量,可以一次访问Auth类。例如var authService = Auth();
将变量放置在Auth类之外,以便可以在任何地方,任何地方的任何类中访问它
答案 1 :(得分:0)
感谢大家的回答和支持。我发现了问题。登录google时,widget.onSignedIn会将我重定向到主页(位于根页面)。调用SignedIn之后,我在google登录按钮中重定向到主页,这是我丢失用户变量范围的方式。谢谢大家!