The getter 'email' was called on null - firebase

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.

Related

How to get another users ID from Firebase in Flutter?

i'm trying to add user in my contacts by writing his uid from firebase. I need to write users id in textfield and on pressing button i need to firebase check this uid, and if it's exists, my app needs to add it in contacts. Is there any solutions? p.s i'm making messenger app and it's "Add new contact" function. It would be nice if someone tells me how to create chat using their ID or create separate "chat id" which would be same to 2 users. btw i'm using realtime database
To check if the user exsits, you can do something like this:
final users = FirebaseFirestore.instance.collection('users');
final userDoc =
await users.doc('PUT THE UID TO SEARCH FOR HERE').get();
if (userDoc.exists) {
// do something
}
To make this reusable convert it to a function:
Future<bool> userExists(String uid) async {
final users = FirebaseFirestore.instance.collection('users');
final userDoc =
await users.doc(uid).get();
return userDoc.exists;
}
Now you can call the function with any uid:
bool _userExists = await userExists('UID');
Here is a full code example:
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
class App extends StatefulWidget {
#override
_AppState createState() => new _AppState();
}
class _AppState extends State<App> {
// This key is used to control the SnackBars
final key = GlobalKey<ScaffoldMessengerState>();
final textCtrlr = TextEditingController();
bool isLoading = false;
#override
void dispose() {
super.dispose();
textCtrlr.dispose();
}
Future<bool> userExists(String uid) async {
final users = FirebaseFirestore.instance.collection('users');
final userDoc = await users.doc(uid).get();
return userDoc.exists;
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ScaffoldMessenger(
key: key, // Assign the key to the ScaffoldMessenger
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
child: TextFormField(
controller: textCtrlr,
),
width: 200.0,
),
),
isLoading
? const SizedBox(
child: CircularProgressIndicator(
strokeWidth: 2.0,
),
width: 20.0,
height: 20.0,
)
: ElevatedButton(
onPressed: () async {
final uid = textCtrlr.text;
setState(() => isLoading = false);
final bool _userExists = await userExists(uid);
setState(() => isLoading = false);
if (_userExists) {
// The user exists and can
// be added to contacts
key.currentState?.showSnackBar(
SnackBar(
content: Text(
'User added!',
),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
),
);
return;
}
// The user doesn't exist
key.currentState?.showSnackBar(
SnackBar(
content: Text(
'The user doesn\'t exist',
),
backgroundColor: Theme.of(context).errorColor,
behavior: SnackBarBehavior.floating,
),
);
},
child: const Text('Search for user'),
),
],
),
),
),
),
);
}
}

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

signOut of user in Flutter using Firebase authentication is not working

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

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"];
)};

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