signOut of user in Flutter using Firebase authentication is not working - firebase

I followed this tutorial-series on Youtube step by step (videolink) and already searched for similar questions but my code just isn't working.
I already made sure that everything is async & watched the tutorial more than twice but I can't find the problem source. Also, logging in and registering is working fine.
What am I doing wrong here? Here's my code:
main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp( MyApp());
}
...
return StreamProvider<UserModel>.value(
value: AuthService().user,
child: MaterialApp(
...
userModel.dart:
class UserModel {
String uid;
String displayName;
String avatarUrl;
UserModel(this.uid, {this.displayName, this.avatarUrl});
}
auth.dart:
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
//create user obj based on User from Firebase
UserModel _currentUser(User user) {
return user != null ? UserModel(user.uid) : null;
}
//auth change user stream
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_currentUser);
}
...
//log out
Future signOut() async{
try {
return await _auth.signOut();
} catch(e) {
print(e.toString());
return null;
}
}
}
page with the logout call:
class Settings extends StatefulWidget {
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
final AuthService _auth = AuthService();
...
onPressed: () {
_showLogoutDialog(context, isRegistered, _auth);
},
...
showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(30.0))
),
backgroundColor: overlayDarkRightBottom,
title: Text('Log out?'),
content: Text('All non synchronized data will be lost.',
style: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600, letterSpacing: 1)),
actions: [
FlatButton(
minWidth: MediaQuery.of(context).size.width*0.8,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.logout, color: Colors.red),
SizedBox(width: 10.0),
Text('Log out',
style: TextStyle(fontSize: 18.0, color: Colors.red)),
],
),
onPressed: () async {
Navigator.of(context).pop();
await _auth.signOut();
},
)
],
)
)
UPDATE: the logout seems to work fine (when I hot restart the app I get to the login screen again), but how come that the user state doesn't change?

I'm sorry, everything did work fine, I just had to pop a Page laying above everything. I'll leave this question in case somebody does the same mistake.
Navigator.of(context).pop();

Related

How do i catch error of authentication while logging in and signup using firebase and flutter?

I am pretty new to flutter and this is my first project in which i am using backend like firebase.
So i pre build an ui and started to integrate firebase in it it was successfully done and it is working as accepted but when i am trying to print out the errors while logging in signup i am failing to do that, i am able to catch the errors in my console but i wait to update my users with that error text.
when ever i am trying to log in though i am providing a wrong password it is taking me to the home page which i dont want
This is my Log In page with a pre build ui and with working setup of log in
import 'package:flutter/material.dart';
import 'package:notepad/Authentigation%20Screens/signUp.dart';
import 'package:notepad/animation/bouncypagetrans.dart';
import 'package:notepad/animation/fadeanimation.dart';
import 'package:notepad/auth/authMethod.dart';
import 'package:notepad/screens/homepage.dart';
import 'package:notepad/widgets/VxTextsHeaders.dart';
import 'package:notepad/widgets/customRaisedButton.dart';
import 'package:notepad/widgets/loading.dart';
import 'package:notepad/widgets/textformField.dart';
import 'ForgotPassword.dart';
class LogIn extends StatefulWidget {
#override
_LogInState createState() => _LogInState();
}
class _LogInState extends State<LogIn> {
String error;
AuthMethods authMethods = new AuthMethods();
bool isLoading = false;
final formKey = GlobalKey<FormState>();
TextEditingController emailTextEditingController =
new TextEditingController();
TextEditingController passwordTextEditingController =
new TextEditingController();
logMein() {
if (formKey.currentState.validate()) {
try {
setState(() {
isLoading = true;
});
authMethods
.signInWithEmailAndPassword(emailTextEditingController.text,
passwordTextEditingController.text)
.then((val) {
// print("${val.uId}");
Navigator.pushReplacement(
context,
BouncyPageRout(
widget: HomePage(),
),
);
});
} catch (e) {
setState(() {
error = e.message;
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: true,
body: isLoading
? Loading()
: SafeArea(
child: Container(
width: double.infinity,
padding: EdgeInsets.fromLTRB(30.0, 100.0, 30.0, 20.0),
height: MediaQuery.of(context).size.height,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FadeAnimation(
1.0,
headerTitle("Hello,"),
),
FadeAnimation(
2.0,
headerSubTitle("Log in to continue"),
),
],
),
SizedBox(height: 65),
Column(
children: [
Form(
key: formKey,
child: Column(children: [
SizedBox(height: 20),
FadeAnimation(
3.0,
buildTextField(
validator: (val) {
return val.isEmpty ||
RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(val)
? null
: "Please provide a valid Email";
},
labelText: "Email",
controller: emailTextEditingController),
),
SizedBox(height: 20),
FadeAnimation(
3.5,
buildTextField(
validator: (val) {
return val.isEmpty || val.length < 6
? "Please Provide a Strong Password,/n Provide somthing greater than 6 "
: null;
},
labelText: "Password",
obscureText: true,
controller:
passwordTextEditingController),
),
])),
SizedBox(height: 10.0),
Container(
alignment: Alignment.centerRight,
child: FadeAnimation(
4.0,
InkWell(
onTap: () {
Navigator.push(
context,
BouncyPageRout(
widget: ForgotPassword(),
),
);
},
child: Text(
'Forgot Password?',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.deepOrange,
fontSize: 14),
),
),
),
),
SizedBox(height: 40),
FadeAnimation(
4.5,
raisedButton(
context: context,
onPressed: () {
logMein();
},
color: Colors.deepOrange,
title: "Log In",
textColor: Colors.white,
),
),
SizedBox(
height: 20.0,
),
FadeAnimation(
5.0,
raisedButton(
context: context,
onPressed: () {
Navigator.push(
context,
BouncyPageRout(
widget: SignUp(),
),
);
},
color: Colors.white,
title: "SignUp"),
),
],
)
],
),
),
),
),
);
}
}
AuthMethods page with contains all the codes of firebase
import 'package:notepad/auth/user.dart';
class AuthMethods {
String error;
final FirebaseAuth _auth = FirebaseAuth.instance;
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(userId: user.uid) : null;
}
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser firebaseUser = result.user;
return _userFromFirebaseUser(firebaseUser);
} catch (e) {
print(e.toString());
}
}
Future signUpwithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser firebaseUser = result.user;
return _userFromFirebaseUser(firebaseUser);
} catch (e) {
print(e.toString());
}
}
Future resetPass(String email) async {
try {
return await _auth.sendPasswordResetEmail(email: email);
} catch (e) {
print(e.toString());
}
}
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
}
}
}
so basically i want to notify my users a valid error message and untill they solve that they should not navigate to a perticular screen
You can use a snackbar widget and call them in your throw function! Follow this link to learn more about snackbars
https://flutter.dev/docs/cookbook/design/snackbars

I am unable to use Firebase Auth Package correctly

Everytime i run the code for the first time in my Android Studio Code, I have to hot reload it just to enable the submit button. I think the problem is that the current version of Auth package isn't returning FirebasAuth.instance.currentuser as a future but returning a User class. Here's my code.
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class NewMessage extends StatefulWidget {
#override
_NewMessageState createState() => _NewMessageState();
}
class _NewMessageState extends State<NewMessage> {
var _enteredMssg = '';
var _controller = new TextEditingController();
void sendMessage() {
FocusScope.of(context).unfocus();
final user = FirebaseAuth.instance.currentUser;
print(user.uid);
FirebaseFirestore.instance.collection('chat').add({
'text': _enteredMssg,
'createdAt': Timestamp.now(),
'userId': user.uid,
});
// print(user.uid);
_controller.clear();
_enteredMssg = '';
}
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(
top: 10,
),
padding: EdgeInsets.all(10),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'Send a message'),
onChanged: (value) {
_enteredMssg = value;
},
),
),
IconButton(
icon: Icon(Icons.send),
color: Theme.of(context).accentColor,
onPressed: _enteredMssg.trim().isEmpty ? null : sendMessage)
],
),
);
}
}
Go to the terminal and write -
Flutter pub get

null an the Display in Flutter but with the right Value in Future

I am pretty new in Flutter and i can not solve this problem.
i have a really simple application. it is just a login with google an a User is created in Firebase, that user have a counter and a button this button increased the counter (int _user.count++).
Then my problem: after the login in the next window, it is not visible the count variable until I click "plus" button. the variable is right and the query with fireStore work great I got the variable but if I do not click the button I got an the display in the place of the variable "null".
Thanks a lot for you Help and Time, I really hope that you can help me. maybe it is a tiny problem I have not found information about it but it is happen when some start to learn.
MyDAO: Hier the Method Future it is the responsable of make the Query to FireStore.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:gauth/user_model.dart';
import 'package:rxdart/rxdart.dart';
final UserDAO userDAO = UserDAO();
class UserDAO {
final Firestore _db = Firestore.instance;
PublishSubject loading;
Observable<Future<QuerySnapshot>> profile;
void updateData(UserModel user) async {
DocumentReference documentReference =
_db.collection("users").document(user.uid);
return documentReference.setData({
"uid": user.uid,
"userName": user.name,
"email": user.email,
"photoUrl": user.photoUrl,
"count": user.count,
"lastIn": DateTime.now()
}, merge: true);
}
Future<QuerySnapshot> readDateFutur(String email) async {
// loading.add(true);
QuerySnapshot querySnapshot = await (_db
.collection("users")
.where("email", isEqualTo: email)
.getDocuments());
// loading.add(false);
return querySnapshot;
}
}
hier in the method "void initState()" I hold the variable _user.couner, that works.
class PlusCounter extends StatefulWidget {
UserModel user;
PlusCounter(this.user);
#override
_PlusCounterState createState() => _PlusCounterState();
}
class _PlusCounterState extends State<PlusCounter> {
UserModel _user;
PublishSubject loading;
#override
void initState() {
// TODO: implement initState
super.initState();
setState(() {
_user = widget.user;
//loading.add(false);
userDAO.readDateFutur(_user.email).then((QuerySnapshot docs) {
if (docs.documents.isNotEmpty) {
print("::::::::::NOESTOY VACIO:::::::::::::::::::::");
print(docs.documents.last.data["count"]);
if (docs.documents.last.data["count"] != null) {
_user.count = docs.documents.last.data["count"];
} else {
_user.count = 0;
}
} else {
print(":::::::::::ESTOY VACIO:::::::::::::::::");
_user.count = 0;
}
});
});
}
void _plus() {
setState(() {
_user.count++;
});
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text("Cuantas veces te has \n lavado las manos:"),
Text("${_user.count}"),
MaterialButton(
onPressed: () {
_plus();
},
child: Text("Plus"),
textColor: Colors.white,
color: Colors.blue,
),
MaterialButton(
onPressed: () => userDAO.updateData(_user),
child: Text("Guardar"),
textColor: Colors.white,
color: Colors.blue,
),
],
);
}
}
WelcomePage code is this one.
class userDataWelcome extends StatelessWidget {
UserModel _userModel;
userDataWelcome(this._userModel);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Welcome"),
),
body: Center(
child: Column(
children: <Widget>[
Center(
child: Container(
height: 100.0,
width: 100.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(_userModel.photoUrl),
),
),
),
),
Text("${_userModel.name}"),
PlusCounter(_userModel),
MaterialButton(
onPressed: () => authService.SingOut(),
child: Text("Logout"),
textColor: Colors.white,
color: Colors.deepOrange,
)
],
),
),
);
}
}
Then I really do not why I need to click "plus" button before I can see the Value of _user.count, because I just see null in otherwise. just again I want to say Thanks for your help.
Try wrapping this line in initStat() _user.count = docs.documents.last.data["count"]; in setState((){}); like this
setState((){
_user.count = docs.documents.last.data["count"];
)};

The getter 'email' was called on null

I'm trying to make a chat between two people using Flutter and Firebase but I'm facing an error when I connect into my app using Firebase's signInWithEmailAndPassword, it tells me:
The getter 'email' was called on null. Receiver: null Tried calling:
email
And Flutter also tells me the error come from the MaterialApp widget from my main.dart which doesn't help me to find the error..
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:social/responsive/size_config.dart';
import 'settings.dart';
import 'home.dart';
final _firestore = Firestore.instance;
FirebaseUser loggedInUser;
class ActivityFeed extends StatefulWidget {
static const String id = 'activity_feed_screen';
#override
_ActivityFeedState createState() => _ActivityFeedState();
}
class _ActivityFeedState extends State<ActivityFeed> {
final _auth = FirebaseAuth.instance;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: Container(
color: Colors.red,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
UserInfo(),
FlatButton(
child: Icon(
Icons.settings,
color: Colors.white,
),
onPressed: () {
Navigator.pushNamed(context, Settings.id);
},
),
FlatButton(
child: Icon(
Icons.not_interested,
color: Colors.white,
),
onPressed: () {
_auth.signOut();
Navigator.pushNamed(context, Home.id);
},
),
],
),
),
)
],
),
);
}
}
class UserInfo extends StatefulWidget {
#override
_UserInfoState createState() => _UserInfoState();
}
class _UserInfoState extends State<UserInfo> {
String email = loggedInUser.email;
String username;
#override
void initState() {
super.initState();
getUsername();
}
void getUsername() async {
DocumentReference docRef = _firestore.collection('users').document(email);
docRef.get().then((snapshot) {
if (snapshot.exists) {
username = snapshot.data['username'];
}
});
}
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Column(
children: <Widget>[
Text(
'Welcome $username',
style: TextStyle(
fontFamily: 'Amatic',
fontSize: SizeConfig.safeBlockHorizontal * 10),
),
],
)
;
}
}
So basically what I am just trying to display 'Welcome' + the username, but it doesn't want,
my Firebase Database have one collection named 'users', where the document names are the email the user used when he created his account.
All the Register/Log-In process seems to work fine.
If someone has a clue of what is happening that would be awesome, thanks.
You initialize username as null. You can either check it on the widget tree
username != null ? Text(
'Welcome $username',
style: TextStyle(
fontFamily: 'Amatic',
fontSize: SizeConfig.safeBlockHorizontal * 10),
) : CircularProgressIndicator()
and set it via setState()
setState(() {
username = snapshot.data['username'];
});
Another solution is to change
String username;
to
String username = '';
You should use a state management method by the way. setState() is very primitive and you will end up having sphagetti code if you use it as a state management solution.

Cloud Firestore not updating correctly when record is added in Flutter

I am developing a Flutter app and I am using the cloud_firestore plugin. I have a collection of submissions and I am using the StreamBuilder to display them (which I am assuming will update when the stream changes). I literally took the example from the plugin examples as there is not much documentation on how to do things using the plugin. When I added a record, the list of documents that I am displaying gets longer, but it seems to be copying one of the submissions instead of inserting the new submission. The new submission does not show after it is added. Here is the code for how I am displaying the list:
// At the top of the class home.dart.
final submissions = Firestore.instance.collection('submissions');
// This is in submission-list.dart and the above submissions
// is passed in to the contructor
Widget build(BuildContext context) {
return new StreamBuilder<QuerySnapshot>(
stream: submissions
.where('owner_uid', isEqualTo: this.user.uid)
.orderBy('timestamp', descending: true)
.snapshots,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView(
children: snapshot.data.documents.map((DocumentSnapshot document) {
var date = _formatDate(document['timestamp']);
String body = _constructCardBody(document['weight'],
bodyFat: document['bodyFat']);
String id = document.documentID;
return new SubmissionCard(id: id, title: date, body: body, submissions: submissions);
}).toList(),
);
},
);
}
Here is submission-card.dart in full:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import '../utils/logger.dart';
import './block-button.dart';
class SubmissionCard extends StatefulWidget {
final String id;
final String title;
final String body;
final CollectionReference submissions;
SubmissionCard({this.id, this.title, this.body, this.submissions});
#override
State<StatefulWidget> createState() =>
new _SubmissionCardState(id: this.id, title: this.title, body: this.body, submissions: this.submissions);
}
class _SubmissionCardState extends State<SubmissionCard> {
final String id;
final String title;
final String body;
bool showActionButtons = false;
final CollectionReference submissions;
_SubmissionCardState({this.id, this.title, this.body, this.submissions});
void _showEditScreen() {}
void _showActionButtons() {
setState(() {
showActionButtons = true;
});
}
void _hideActionButtons() {
setState(() {
showActionButtons = false;
});
}
Future<Null> _deleteSubmission() async {
try {
await submissions.document(id).delete();
await Logger.log('error', 'stackTrace');
} catch (error, stackTrace) {
await Logger.log(error, stackTrace);
}
}
void _closeDialog() {
Navigator.of(context).pop();
_hideActionButtons();
}
Future<Null> _warnAboutDeletion() async {
return showDialog(
context: context,
child: new SimpleDialog(
title: new Text('Are you sure?'),
children: <Widget>[
new SimpleDialogOption(
onPressed: () {
this._deleteSubmission();
this._closeDialog();
},
child: new Text("I'm sure. Delete it."),
),
new SimpleDialogOption(
onPressed: _closeDialog,
child: new Text("Nope. Take me back."),
),
],
)
);
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onLongPress: _showActionButtons,
onTap: _hideActionButtons,
child: new Card(
elevation: showActionButtons ? 8.0 : 2.0,
key: new GlobalKey(),
child: new Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new ListTile(
trailing: showActionButtons
? new Row(
children: <Widget>[
new IconButton(
padding: const EdgeInsets.all(0.0),
icon: const Icon(Icons.edit),
onPressed: _showEditScreen,
color: Colors.black12,
splashColor: Colors.black26,
highlightColor: Colors.black12,
),
new IconButton(
padding: const EdgeInsets.all(0.0),
icon: const Icon(Icons.delete),
onPressed: _warnAboutDeletion,
color: Colors.redAccent,
splashColor: Colors.black26,
highlightColor: Colors.black12,
),
],
)
: new Container(),
isThreeLine: true,
title: new Text(title),
subtitle: new Text(
body,
style: new TextStyle(height: 3.0),
),
),
],
),
),
);
}
}
Link to repo: https://github.com/dericgw/bodwatch
Before, when I have worked with Firebase, this collection would automatically update. I have never seen this weird behavior before. Now, I am new to Flutter and Dart, so I could be missing something for sure.
You need to add the indexing in firebase console.
In your case, you need to a multiple indexes.
1. owner_uid, ascending
2. timestamp, descending
And the problem should solve.

Resources