Flutter: Firebase Auth endless loop - firebase

I want to build a Flutter login screen working with Firebase Auth. I posted the Code below. When i run the App, the _switchToHomePage() method is called endless. So the app keeps opening an new HomeScreen until I close the app.
Thanks for help!
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
import 'home_page.dart';
import 'register.dart';
import 'error_dialog.dart';
class LoginForm extends StatefulWidget {
#override
State<StatefulWidget> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
bool _hidePassword = true;
void _checkUserIsLoggedIn() {
_firebaseAuth.currentUser().then((firebaseUser) {
if (firebaseUser == null) {
print('no user logged in');
} else {
print('User logged in');
_switchToHomePage();
}
});
}
void _switchToHomePage() {
print('switching to home page...');
Navigator.push(
context, MaterialPageRoute(builder: (context) => HomePage()));
this.deactivate();
}
void _login() {
String email = emailController.text;
String password = passwordController.text;
_firebaseAuth
.signInWithEmailAndPassword(email: email, password: password)
.then((firebaseUser) {
if (firebaseUser != null) {
print('Login succesfull');
_switchToHomePage();
} else {
throw Exception('FirebaseUser is null');
}
}).catchError((exception) {
//TODO make better error messages
print(exception.message);
ErrorDialog(
context: this.context,
title: 'Error',
message: exception.message);
});
}
void _showRegisterScreen() {
Navigator.push(
context, MaterialPageRoute(builder: (context) => RegisterForm()));
}
#override
Widget build(BuildContext context) {
_checkUserIsLoggedIn();
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: ListView(
padding: EdgeInsets.fromLTRB(15, 30, 15, 0),
children: <Widget>[
Padding(padding: EdgeInsets.only(top: 60)),
Text(
'placeholder',
style: TextStyle(
color: Colors.teal,
fontSize: 40,
),
),
Padding(padding: EdgeInsets.only(top: 60)),
Padding(padding: EdgeInsets.only(top: 40)),
TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
prefixIcon: Icon(Icons.email),
labelText: 'E-Mailadresse',
border: UnderlineInputBorder()),
),
Padding(padding: EdgeInsets.only(top: 20)),
TextField(
controller: passwordController,
obscureText: _hidePassword,
decoration: InputDecoration(
labelText: 'Passwort',
border: UnderlineInputBorder(),
prefixIcon: Icon(Icons.security),
suffixIcon: IconButton(
icon: Icon(Icons.remove_red_eye),
color: _hidePassword
? Theme.of(context).primaryColor
: Colors.grey,
onPressed: () => this.setState(() {
_hidePassword = !_hidePassword;
}),
)),
),
Padding(padding: EdgeInsets.only(top: 5)),
Container(
child: FlatButton(
onPressed: () => print('Forgot password'),
child: Text(
'Passwort vergessen?',
style: TextStyle(color: Theme.of(context).primaryColor),
),
),
width: double.infinity,
alignment: Alignment.centerRight,
),
Padding(padding: EdgeInsets.only(top: 40)),
RaisedButton(
onPressed: _login,
color: Theme.of(context).primaryColor,
child: Text(
'Login',
style: TextStyle(color: Colors.white),
),
),
Padding(padding: EdgeInsets.only(top: 20)),
FlatButton(
onPressed: _showRegisterScreen,
child: Text(
'Neuen Account erstellen',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).accentColor,
),
),
)
],
));
}
}

The reason you get that, is because _checkUserIsLoggedIn(); which contains navigation to the HomePage is the first method inside build method. Inside the build() method you have setState() on the TextField, therefore everytime the state is changing then the build() method is getting called and _checkUserIsLoggedIn() is also getting called. To solve that try the following:
#override
initState() {
super.initState();
_firebaseAuth.currentUser().then((firebaseUser) {
if (firebaseUser == null) {
print('no user logged in');
} else {
print('User logged in');
_switchToHomePage();
}
});
}
Inside the lifecycle method initState() check if the user is logged in and navigate to the HomePage. Also remove the method _checkUserIsLoggedIn(); from the build method

I just solved it. I replaced:
Navigator.push(
context, MaterialPageRoute(builder: (context) => RegisterForm()));
with
Navigator.pushAndRemoveUntil(
context, MaterialPageRoute(builder: (context) => HomePage()), (e) => false);

Related

Flutter and Firebase Authentication problem

Flutter fellow today I have a problem with the Firebase Authentication service.
Well then let me explain the situation. I have a page that let you select a type of user, if you select "Guest" you will push through the app's dashboard page Nav() without sign-in but if select "Sign-in" app will push you to the login page Login().
class SelectUser extends StatelessWidget {
const SelectUser({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF20348F),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
padding: const EdgeInsets.all(15.0),
margin: const EdgeInsets.all(15.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(45),
),
child: TextButton(
child: const Text('Guest'),
onPressed: () {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => const Nav(),
),
(route) => false,
);
},
style: TextButton.styleFrom(
primary: const Color(0xFF20348F),
textStyle: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
)),
),
),
Container(
padding: const EdgeInsets.all(15.0),
margin: const EdgeInsets.all(15.0),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(45),
),
child: TextButton(
child: const Text('Sign-In'),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (contex) {
return const Login();
}));
},
style: TextButton.styleFrom(
primary: const Color(0xFF20348F),
textStyle: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
)),
),
),
],
),
),
);
}
}
In Nav() I use BottomNavigationBarItem and have a several widget included Proflie()
inside.
I already test the sign-up, sign-out and registration function everything is perfectly fine but when I try to be tricky in Proflie() and I face...let's said the weird problem. First let's me show you the code in the Proflie().
class Profile extends StatefulWidget {
const Profile({Key? key}) : super(key: key);
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
String? _email;
DateTime? _creationTime;
DateTime? _lastSignIn;
bool? _status;
#override
Widget build(BuildContext context) {
userStatus();
return userForm();
}
userStatus() {
FirebaseAuth.instance.authStateChanges().listen((event) {
if (event == null) {
_status = false;
} else {
_status = true;
_email = FirebaseAuth.instance.currentUser!.email;
_creationTime =
FirebaseAuth.instance.currentUser!.metadata.creationTime;
_lastSignIn =
FirebaseAuth.instance.currentUser!.metadata.lastSignInTime;
}
});
}
Widget userForm() {
if (_status == true) {
return Scaffold(
appBar: defaultAppBar('Profile'),
body: Center(
child: ListView(
padding: const EdgeInsets.all(20),
children: [
Container(
margin: const EdgeInsets.all(30),
child: Text(
'Email: $_email',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
Container(
margin: const EdgeInsets.all(30),
child: Text(
'Creation Time: $_creationTime',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
Container(
margin: const EdgeInsets.all(30),
child: Text(
'Last Sign-In: $_lastSignIn',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 45),
button('Change password', 1),
const SizedBox(height: 18),
button('Change email', 2),
const SizedBox(height: 18),
button('Sign-out', 0),
],
),
),
);
} else {
return Center(
child: ListView(
padding: const EdgeInsets.all(14),
children: <Widget>[
const SizedBox(height: 100),
Container(
margin: const EdgeInsets.all(30),
child: const Center(
child: Text(
'Please sign-in to use this Feature',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
const SizedBox(height: 50),
button('Login', 3),
const SizedBox(height: 18),
button('Register', 4),
],
),
);
}
}
Widget button(String txt, int _nav) {
Color? _color = const Color(0xFF20348F);
if (_nav == 0) {
_color = Colors.red;
}
return TextButton(
style: TextButton.styleFrom(
primary: _color,
textStyle: const TextStyle(
fontSize: 20,
),
),
child: Text(txt),
onPressed: () {
if (_nav == 0) {
FirebaseAuth.instance.signOut();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => const SelectUser(),
),
(route) => false,
);
} else {
Navigator.push(context, MaterialPageRoute(builder: (contex) {
if (_nav == 1) {
return const EditPass();
} else if (_nav == 2) {
return const EditEmail();
} else if (_nav == 3) {
return const Login();
} else {
return const Register();
}
}));
}
},
);
}
}
As you can see I'm trying to use userStatus() to identify the "User" and "Guest" and that function I use FirebaseAuth.instance.authStateChanges().listen((event) to check user state in Firebase Authentication(Not really sure am I doing the right way if not please teach me how to check user state) if event == null that mean no user sign-in right now. I'm just going to set _status = false so the "Guest" should found the else case in userForm(). Otherwise _status = true this mean user is signed-in and userForm() should go to if (_status == true) case.
Now the problem is when I success to sign-in on the Login(). In Profile() I ended up got the else case of userForm() instead but that not all! When I hot reloaded the IDE it turn out now I'm in the if (_status == true) case. Yeah like said it a weird problem the first time that app loaded the page it go to false case but when hot reloaded it turn to true case. I'm not so sure the error is from Profile() or Login(). I'm just going leave the Login() then.
class Login extends StatefulWidget {
const Login({Key? key}) : super(key: key);
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
final _formKey = GlobalKey<FormState>();
var _email = "";
var _password = "";
final emailController = TextEditingController();
final passwordController = TextEditingController();
bool _passwordVisible = true;
#override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return formLogin();
}
Widget formLogin() {
return Scaffold(
appBar: defaultAppBar('Sign-In'),
body: Center(
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(32),
child: ListView(
children: [
buildEmail(),
const SizedBox(height: 24),
buildPassword('Password', 'Your password...'),
const SizedBox(height: 50),
button('Sign-in', 0),
const SizedBox(height: 24),
button('Sign-up', 1),
],
),
),
),
),
);
}
userLogin() async {
try {
await FirebaseAuth.instance
.signInWithEmailAndPassword(email: _email, password: _password);
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => const Nav(),
),
(route) => false,
);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.amber,
content: Text(
"Email not found",
style: TextStyle(fontSize: 16.0, color: Colors.black),
),
),
);
} else if (e.code == 'wrong-password') {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.amber,
content: Text(
"Password incorrect",
style: TextStyle(fontSize: 16.0, color: Colors.black),
),
),
);
}
}
}
TextButton button(String txt, int nav) {
return TextButton(
style: TextButton.styleFrom(
primary: const Color(0xFF20348F),
textStyle: const TextStyle(
fontSize: 20,
),
),
child: Text(txt),
onPressed: () {
if (nav == 0) {
if (_formKey.currentState!.validate()) {
setState(() {
_email = emailController.text;
_password = passwordController.text;
});
userLogin();
}
} else {
_formKey.currentState!.reset();
Navigator.push(context, MaterialPageRoute(builder: (contex) {
return const Register();
}));
}
},
);
}
Widget buildEmail() {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'name#example.com',
prefixIcon: Icon(Icons.mail_outline),
border: OutlineInputBorder(),
errorStyle: TextStyle(color: Colors.redAccent, fontSize: 15),
),
controller: emailController,
validator: MultiValidator([
RequiredValidator(errorText: "Email is required"),
EmailValidator(errorText: "The format of email is incorrect")
]),
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.done,
);
}
Widget buildPassword(String _txt1, String _txt2) {
return TextFormField(
obscureText: _passwordVisible,
decoration: InputDecoration(
labelText: _txt1,
hintText: _txt2,
suffixIcon: GestureDetector(
onTap: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
child: Icon(
_passwordVisible ? Icons.visibility : Icons.visibility_off,
),
),
prefixIcon: const Icon(Icons.vpn_key_outlined),
border: const OutlineInputBorder(),
errorStyle: const TextStyle(color: Colors.redAccent, fontSize: 15),
),
controller: passwordController,
validator: RequiredValidator(errorText: "Password is required"),
textInputAction: TextInputAction.done,
);
}
}
If you guy could help me I'd gladly appreciated.
If you can create a .gist i would be of help to u, also in your profile page i can see issue's that u r listening to the user changes in the build, its very unpractical to add it in the build because flutter will call the build method multiple times , build should be clean, also potential memory leaks are there since u r not disposing the streams.

The getter 'data' was called on null. Receiver: null Tried calling: data

Tried signing up user with Firebase but got the above error instead, and the code is formatted in such a way that it should redirect the user to homepage after a successful signup.
My main.dart
import 'package:bedc_app/home_page.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:bedc_app/auth/login_page.dart';
void main() async{
//solution study
WidgetsFlutterBinding.ensureInitialized();
FirebaseAuth.instance.currentUser()
.then((FirebaseUser user) {
if(user != null){
Firestore.instance.collection('users').document(user.uid).get().then((DocumentSnapshot doc){
runApp(MyApp(true, doc));
});
return;
}
runApp(MyApp(false, null));
});
}
// void main() =>runApp(MyApp());
class MyApp extends StatelessWidget {
bool isLoggedIn;
DocumentSnapshot doc;
MyApp(this.isLoggedIn, this.doc);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My Multifunctional bedc project App',
debugShowCheckedModeBanner: false,
home: isLoggedIn ? HomePage(userDoc: doc) : loginPage(),
);
}
}
My Signup_page.dart
import 'package:bedc_app/auth/login_page.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:bedc_app/utils/constants.dart';
import '../home_page.dart';
class SignUpPage extends StatefulWidget {
#override
_SignUpPageState createState() => _SignUpPageState();
}
class _SignUpPageState extends State<SignUpPage> {
String email, password;
bool isLoggedIn = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ListView(
children: [
SizedBox(
height: 50,
),
Align(
alignment: Alignment.topCenter,
child: (
Image.asset(
'assets/bedclogo.jpg'
)),
),
SizedBox(
height: 100,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 26.0),
child: TextField(
decoration: InputDecoration(
hintText: 'email',
border: OutlineInputBorder(),
labelText: 'Email',
suffixIcon: Icon(Icons.email, color: Colors.green)
),
keyboardType: TextInputType.emailAddress,
onChanged: (String val){
email = val;
},
),
),
SizedBox(
height: 10,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 26.0),
child: TextField(
decoration: InputDecoration(
hintText: 'Password',
border: OutlineInputBorder(),
labelText: 'Password',
suffixIcon: Icon(Icons.lock, color: Colors.green)
),
obscureText: true,
obscuringCharacter: '!',
keyboardType: TextInputType.emailAddress,
onChanged: (String val){
password = val;
},
),),
SizedBox(
height: 5,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 26),
child: MaterialButton(
color: Color(0xFF88C540),
child: isLoading ? Container(
height: 24,
width: 24,
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
strokeWidth: 2,
)
):Text(
'SignUp'.toUpperCase(),
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: (){
checkUserInput();
},
),
),
SizedBox(
height: 10,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: RichText(
text: TextSpan(
text: 'Already have an Account ?',
style: TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'Login here',
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()..onTap = (){
Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (_) => loginPage()));
},
)
]
)),
)
],
));
}
//SIGNUP FUNCTION USING FIREBASE
bool isLoading = false;
signUpUserWithFirebase(){
FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password
).then((AuthResult result){
//Authresult cant be stored to string.....
storeUserDataToFirestore(result.user.uid);
if(result.user.uid != null){
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => HomePage()));
}
}).catchError((e){
print(e);
isLoading = false;
setState(() { });
});
}
//storeUserDataToFirestore
storeUserDataToFirestore(String userId){
Map<String, dynamic> userData = Map<String, dynamic>();
userData = {
Constants.USERNAME: '#username',
Constants.USER_ID: userId,
Constants.EMAIL : email,
Constants.ACCOUNT_NUM : '00000000',
Constants.IS_ADMIN: false,
Constants.PROFILE_IMAGE: '',
Constants.PROFILE_IMAGE_THUMB: '',
};
CollectionReference usersRef = Firestore.instance.collection(Constants.USERS_COLLECTION);
usersRef.document(userId).setData(userData).then((_){
isLoading = false;
setState(() { });
}).catchError((e){
print(e);
});
}
//FUNCTION TO CHECK USER ENTRY
checkUserInput(){
isLoading = true;
setState(() { });
if(email == null || email.isEmpty){
print('Enter email');
isLoading = false;
setState(() { });
return;
}
if(password == null || email.isEmpty){
print('Enter password');
isLoading = false;
setState(() { });
}
//SIGNUP THE USER
signUpUserWithFirebase();
}
getUserData(String UserId){
Firestore.instance.collection(Constants.USERS_COLLECTION)
.document(UserId)
.get().then((DocumentSnapshot userDoc){
Navigator.of(context).pop();
Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (_) => HomePage(userDoc: userDoc)));
}).catchError((e){
print(e);
});
}
}
My Home_page.dart
import 'dart:ui';
import 'package:bedc_app/auth/login_page.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:bedc_app/auth/signup_page.dart';
import 'model/user.dart';
User currentUser;
class HomePage extends StatefulWidget {
DocumentSnapshot userDoc;
HomePage({this.userDoc,});
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//user cant access this page unless logged in
bool userIsLoggedIn = true;
#override
void initState() {
// TODO: implement initState
super.initState();
currentUser = User.fromSnapshot(widget.userDoc);
}
//HERE IS BUILDING PROFILE IMAGE
Widget buildProfileImage(){
if(currentUser.profileImage == ''){
return CircleAvatar(
backgroundColor: Colors.white70,
radius: 20,
child: Icon(Icons.person, size: 30),
);
}else{
return CircleAvatar(
backgroundImage: NetworkImage(currentUser.profileImage),
);
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: Colors.brown,
title: Text('Homepage'),
),
body: ListView(
children: [
SizedBox(height: 20),
Align(
alignment: Alignment.topRight,
child: (
Image.asset(
'assets/bedclogo.jpg',
scale: 2,
)),
),
ListTile(
title: Text(currentUser.username, style: TextStyle(color: Colors.brown, fontWeight: FontWeight.bold),),
leading: buildProfileImage(),
),
SizedBox(height: 30),
//CAPTURE METER READING UI
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: GestureDetector(
onTap: (){
//print(currentUser.isAdmin);
},
child: Row(
children: [
//CAPTURE METR READING
Container(
width: 100,
height: 100,
color: Colors.green,
child: Align(
alignment: Alignment.center,
child: (
Image.asset(
'assets/meterIcon.png',
scale: 3,
)),
),
),
Container(
width: 100,
height: 100,
child: Align(
alignment: Alignment.center,
child: Text('Capture Meter Reading'.toUpperCase()),
),
),
],
),
),
),
SizedBox(height: 30),
//UPDATE PROFILE UI DESIGN
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: GestureDetector(
onTap:(){
//print(currentUser.isAdmin);
},
child: Row(
children: [
Container(
width: 100,
height: 100,
color: Colors.green,
child: Align(
alignment: Alignment.center,
child: (
Image.asset(
'assets/profileAvatar.png',
scale: 3,
)),
),
),
Container(
width: 100,
height: 100,
child: Align(
alignment: Alignment.center,
child: Text('Update Profile'.toUpperCase()),
),
),
],
),
),
),
// TextButton (
// onPressed: (){
// //just logout, implement pop dialog later
// // FirebaseAuth.instance.signOut();
// // Navigator.of(context).pushReplacement(CupertinoPageRoute(builder: (_)=> loginPage()));
// // currentUser = null;
// // setState(() {
// // });
// // still showing error... try to fix asap
// FirebaseAuth.instance.signOut().then((_){
// userIsLoggedIn = false;
// Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_)=> loginPage()));
// //currentUser = null;
// setState(() {
//
// });
// });
// },
// child: Text('SignOut'))
],
)
);
}
}
User.dart for user Model
import 'package:bedc_app/utils/constants.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class User{
String username;
String email;
String userId;
String accountNum;
bool isAdmin;
String profileImage;
String profileImageThumb;
User({
this.username,
this.email,
this.userId,
this.accountNum,
this.isAdmin,
this.profileImage,
this.profileImageThumb
});
factory User.fromSnapshot(DocumentSnapshot doc){
return User(
username: doc.data[Constants.USERNAME],
email: doc.data[Constants.EMAIL],
userId: doc.data[Constants.USER_ID],
accountNum: doc.data[Constants.ACCOUNT_NUM],
isAdmin: doc.data[Constants.IS_ADMIN],
profileImage: doc.data[Constants.PROFILE_IMAGE],
profileImageThumb: doc.data[Constants.PROFILE_IMAGE_THUMB]
);
}
}
Having signed the user up, im getting error The getter 'data' was called on null. Receiver: null Tried calling: data.
i dont know which part of the code is causing this exception
This error generally show when you try to get some attribute on null object.
In your case, call one of doc.data[..] in file User.dart generate the error that means doc == null is true.
Verify that widget.userDoc is not null at this line currentUser = User.fromSnapshot(widget.userDoc); on initState method of Home_page.dart file
So error provide in one of HomePage call who require a non null userDoc (todo: make sure your data is not null using assert and add #required before required variable on your class exp: HomePage({#required this.userDoc,}): assert(userDoc != null, "BOooh my userDoc may be not null"); that help very much when you debug your app)
finaly your problem provide in signUpUserWithFirebase() method of your Signup_page.dart file... here Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) => HomePage()));
Try to replace by this getUserData(result.user.uid) so your methode should become
signUpUserWithFirebase(){
FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email,
password: password
).then((AuthResult result){
//Authresult cant be stored to string.....
storeUserDataToFirestore(result.user.uid);
if(result.user.uid != null){
getUserData(result.user.uid); // replaced line
}
}).catchError((e){
print(e);
isLoading = false;
setState(() { });
});
}

How to Add the displayName While SigningUp using createUserWithEmailAndPassword

I was trying to add the display name to the user immediately after the signup(using createUserWithEmailAndPassword ) and store it in name field in firestore. But when I see it in firestore it says null, I am completely new to this,
Here is how I trying to do it:
UserSignUp.dart
class UserSignUp{
static FirebaseAuth _auth = FirebaseAuth.instance;
static signupWithEmail({String email, String password, String name}) async {
final res = await _auth.createUserWithEmailAndPassword(
email: email, password: password).then(
(value) async {
await FirebaseAuth.instance.currentUser.updateProfile(
displayName: name,
);
},
);
final User user = res.user;
return user;
}
class UserHelper {
static FirebaseFirestore _db = FirebaseFirestore.instance;
static saveUser(User user) async {
Map<String, dynamic> userData = {
"name": user.displayName,
"email": user.email,
"last_login": user.metadata.lastSignInTime.millisecondsSinceEpoch,
"created_at": user.metadata.creationTime.millisecondsSinceEpoch,
"role": "user",
};
final userRef = _db.collection("users").doc(user.uid);
if ((await userRef.get()).exists) {
await userRef.update({
"last_login": user.metadata.lastSignInTime.millisecondsSinceEpoch,
});
} else {
await _db.collection("users").doc(user.uid).set(userData);
}
await _saveDevice(user);
}
}
}
**
Signup_screen.dart
**
class Body extends StatefulWidget {
#override
_SignupPageState createState() => _SignupPageState();
}
class _SignupPageState extends State<Body> {
TextEditingController _emailController;
TextEditingController _passwordController;
TextEditingController _nameController;
#override
void initState() {
super.initState();
_emailController = TextEditingController(text: "");
_passwordController = TextEditingController(text: "");
_nameController = TextEditingController(text: "");
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Background(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"SIGNUP",
style: TextStyle(fontWeight: FontWeight.bold),
),
width: size.width * 0.8,
decoration: BoxDecoration(
color: kPrimaryLightColor,
borderRadius: BorderRadius.circular(29),
),
child: TextField(
controller: _nameController,
// onChanged: (value) {},
cursorColor: kPrimaryColor,
decoration: InputDecoration(
icon: Icon(
Icons.person,
color: kPrimaryColor,
),
hintText: "Full Name",
border: InputBorder.none,
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
width: size.width * 0.8,
decoration: BoxDecoration(
color: kPrimaryLightColor,
borderRadius: BorderRadius.circular(29),
),
child: TextField(
controller: _emailController,
// onChanged: (value) {},
cursorColor: kPrimaryColor,
decoration: InputDecoration(
icon: Icon(
Icons.person,
color: kPrimaryColor,
),
hintText: "Your Email",
border: InputBorder.none,
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5),
width: size.width * 0.8,
decoration: BoxDecoration(
color: kPrimaryLightColor,
borderRadius: BorderRadius.circular(29),
),
child: TextField(
controller: _passwordController,
obscureText: true,
// onChanged: (value) {},
cursorColor: kPrimaryColor,
decoration: InputDecoration(
hintText: "Password",
icon: Icon(
Icons.lock,
color: kPrimaryColor,
),
suffixIcon: Icon(
Icons.visibility,
color: kPrimaryColor,
),
border: InputBorder.none,
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 10),
width: size.width * 0.8,
child: ClipRRect(
borderRadius: BorderRadius.circular(29),
child: FlatButton(
padding: EdgeInsets.symmetric(vertical: 20, horizontal: 40),
color: kPrimaryColor,
onPressed: () async {
if (_emailController.text.isEmpty ||
_passwordController.text.isEmpty) {
print("Email and password cannot be empty");
return;
}
try {
final user = await UserSignUp.signupWithEmail(
email: _emailController.text,
password: _passwordController.text,
name: _nameController.text);
if (user != null) {
print("signup successful");
// Navigator.pop(context);
}
} catch (e) {
print(e);
}
},
child: Text(
"SIGNUP",
style: TextStyle(color: Colors.white),
),
),
),
),
),
),
);
}
}
Main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Auth',
theme: ThemeData(
primaryColor: kPrimaryColor,
scaffoldBackgroundColor: Colors.white,
),
home: MainScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<User>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if(snapshot.hasData && snapshot.data != null) {
UserHelper.saveUser(snapshot.data);
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance.collection("users").doc(snapshot.data.uid).snapshots() ,
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot){
if(snapshot.hasData && snapshot.data != null) {
final userDoc = snapshot.data;
final user = userDoc.data();
if(user['role'] == 'user') {
return //;
}else{
return //;
}
}else{
return Material(
child: Center(child: CircularProgressIndicator(),),
);
}
},
);
}
return //;
}
);
}
}
but in my firestore the name filled is null
enter image description here
From shared code, I would like to share my understanding and thoughts which might help you to get your solution.
I know, you are already aware about this and that might be the reason you are storing user's info based on section 2's callback event.
Section 1
This part of the code will on save user's name on Firebase Auth only not on Firestore
You can just verify if user's name is updated on firebase auth user's list.
UserSignUp -> signupWithEmail -> currentUser.updateProfile
Section 2
UserHelper.saveUser is called based on authStateChanges not user profile info changes from Firebase.
So, there are higher probability user's display name will be updated on profile info after this callback..
Hence, user's displayName may be null while you are saving data on Firestore.
MainScreen -> StreamBuilder -> UserHelper.saveUser
Suggestion
I would suggest you to save user's info after updating the user's display name on profile to Firestore instead of authStateChanges event.

Read data from Firebase database and display the details as the widget loads

In my flutter project, I want to read data from the Firebase Database of the current user logged in and display the User name from the database and display at the appbar[Line 43:title: Text('$userName'),] of the Page.I had tried by the following code below but it gives error on line 26[Line:final FirebaseUser user = _auth.currentUser();]. The error is on the keyword '_auth.currentUser()'
Error is:A value of type 'Future' can't be assigned to a variable of type 'FirebaseUser'
Here is the code:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:udharibook/Screens/UserProfile.dart';
import 'package:udharibook/services/authservice.dart';
import 'Customer_Support.dart';
import 'package:udharibook/services/UserData.dart';
class DashboardPage extends StatefulWidget {
#override
_DashboardPageState createState() => _DashboardPageState();
}
class _DashboardPageState extends State<DashboardPage> {
List<UserData> userdata = [];
String userName;
#override
void initState()
{
super.initState();
FirebaseAuth _auth = FirebaseAuth.instance;
DatabaseReference DBRef = FirebaseDatabase.instance.reference().child('Users');
final FirebaseUser user = _auth.currentUser();
DBRef.child(user.uid).once().then((DataSnapshot user)
{
userName = user.value['Name'];
setState(() {
print(userName);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$userName'),
backgroundColor: Color.fromRGBO(162, 42, 43, 1.0),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
print("Search Clicked");
}),
IconButton(
icon: Icon(Icons.sort),
onPressed: () {
print("Sort Clicked");
}),
],
),
drawer: Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Text("mehul jain"),
decoration: BoxDecoration(
color: Color.fromRGBO(162, 42, 43, 1.0),
),
),
CustomMenu(
Icons.person,
'Profile',() => {
Navigator.pop(context),
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => UserProfile()))
}),
CustomMenu(Icons.assessment, 'Reports', () => {}),
CustomMenu(Icons.settings, 'Settings', () => {}),
CustomMenu(
Icons.perm_phone_msg,
'Customer Support',
() => {
Navigator.pop(context),
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => CustSupport()))
}),
CustomMenu(Icons.lock, 'Log Out', () => {AuthService().signOut()}),
],
),
),
);
}
}
class CustomMenu extends StatelessWidget {
IconData icon;
String text;
Function onTap;
CustomMenu(this.icon, this.text, this.onTap);
#override
Widget build(BuildContext context) {
// TODO: implement build
return Padding(
padding: EdgeInsets.fromLTRB(8.0, 0.0, 8.0, 0.0),
child: Container(
decoration: BoxDecoration(
border:
Border(bottom: BorderSide(color: Colors.grey.shade400))),
child: InkWell(
splashColor: Colors.redAccent,
onTap: onTap,
child: Container(
height: 60.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Icon(icon),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
text,
style: TextStyle(
fontSize: 17.0, fontFamily: 'Exo2'),
)),
],
),
Icon(Icons.arrow_right),
],
),
))));
}
}
currentUser() returns a Future<FirebaseUser>, therefore you have to do the following:
_auth.currentUser().then((curUser)
{
DBRef.child(curUser.uid).once().then((DataSnapshot user)
{
userName = user.value['Name'];
setState(() {
print(userName);
});
});
});
https://dart.dev/codelabs/async-await

SetState to a String returns null, Firestore with Flutter

I have a splash screen where I want to control the followig:
1. If user is loged out, take the user to OnBoardingScreen.
2. If user is loged in, but has no data in firestore. Take the user to SetupAccountPage
3. If user is loged in and had data to go to HomePage.
My problem is the string value(_controller) returns null. when the user is loged out and works fine when the user is loged in. I'm confused. I gave the code some time to load but doesn't help.
Please help for anyone who has an idea what is wrong here.
Below is my code
import 'dart:async';
import 'package:app/model/user.dart';
import 'package:app/pages/HomePage.dart';
import 'package:app/pages/SetupAccountPage.dart';
import 'package:app/screens/home/home.dart';
import 'package:app/screens/onboarding.dart';
import 'package:app/screens/setup_profile.dart';
import 'package:app/shared/loading.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Wrapper extends StatefulWidget {
#override
_WrapperState createState() => _WrapperState();
}
class _WrapperState extends State<Wrapper> {
//FirebaseUser user;
String onlineUserId;
String _controller;
#override
void initState() {
super.initState();
_checkUser();
Timer(Duration(seconds: 5), () {
print('done');
if (_controller == 'out') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OnBoardingScreen(),
),
);
} else if (_controller == 'info') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SetupAccountPage(),
),
);
} else if (_controller == 'home') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomePage(),
),
);
}
;
});
}
_checkUser() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
final uid = user.uid;
if (await FirebaseAuth.instance.currentUser() == null) {
setState(() {
_controller = 'out';
});
} else {
final snapShot =
await Firestore.instance.collection('Users').document(user.uid).get();
if (snapShot.exists) {
setState(() {
_controller = 'home';
});
// Document with id == docId doesn't exist.
} else {
setState(() {
_controller = 'info';
});
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
decoration: BoxDecoration(color: Colors.redAccent),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 2,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar(
backgroundColor: Colors.white,
radius: 50.0,
child: Icon(
Icons.shopping_cart,
color: Colors.greenAccent,
size: 50.0,
),
),
Padding(
padding: EdgeInsets.only(top: 10.0),
),
Text(
'this is a name',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 24.0),
)
],
),
),
),
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
Padding(
padding: EdgeInsets.only(top: 20.0),
),
Text(
'another name',
softWrap: true,
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Colors.white),
)
],
),
)
],
)
],
),
);
}
}
I finally found the answer. The reson was geting the user was returning a future and this was the missing code.
Future<FirebaseUser> getUser() async {
return await _auth.currentUser();
}

Resources