While working with firebase authentication weird error without any notes happen to me.
This time application stops after I press set name button. Instantly in VScode I am redirected to this page:
As I said there is no error in debug console, no notes. No expections to see.
I guess there is something wrong with setting displayName but not clearly what.
This is full code of the class:
class Verified extends StatefulWidget {
#override
_VerifiedState createState() => _VerifiedState();
}
class _VerifiedState extends State<Verified> {
final formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final _auth = FirebaseAuth.instance;
validate(displayName) {
if (formKey.currentState.validate()) {
setName(displayName);
}
}
setName(displayName) async {
try {
await _auth.currentUser.updateProfile(displayName: displayName);
} catch (e) {
log(e.code.toString());
}
log(_auth.currentUser.displayName.toString());
}
#override
Widget build(BuildContext context) {
return Material(
child: Padding(
padding: const EdgeInsets.all(100.0),
child: Column(
children: [
Text('choose your username'),
Form(
key: formKey,
child: TextFormField(
controller: nameController,
decoration: InputDecoration(hintText: 'name'),
),
),
RaisedButton(
child: Text('set name'),
onPressed: () => validate(nameController))
],
),
),
);
}
}
Thank you in advance
SOLUTION
When I remove from function actions with _auth.currentUser everything works, I also moved this function to the place where the user was logged in/registered and it also worked.
So as I think the error was because firebase saw no user and the solution is to use .currentUser in the same class/function as registering/logging in or saving user after those actions.
Related
I have an admin sign in page where admins can add an id and password to gain access to the Admin area. There is an admin collection in the Firebase Database that stores the id and password. The admin collection is separate from the user sign in page and separate from the user collection which uses Firebase Authentication before allowing access. The user access continues to work correctly. When I fill in the two input boxes on the admin sign in screen and click the button to gain access my error dialog message appears indicating that there isn't any data in the two input fields even though there is data. If I do nothing to the code and then hot reload and click the button again I am able to access the admin but I get the following error message in the console.
The following _TypeError was thrown building ShoppingAdminSignInPage(dirty, dependencies: [_LocalizationsScope-[GlobalKey#2c797]], state: _ShoppingAdminSignInPageState#e3b3d):
type 'Null' is not a subtype of type '() => void'
I have obviously written something or several things incorrectly in my code. It appears the error is in the ShoppingAdminSignInButton. Thank you in advance for any help.
class ShoppingAdminSignInPage extends StatefulWidget {
const ShoppingAdminSignInPage({Key? key}) : super(key: key);
#override
State<ShoppingAdminSignInPage> createState() =>
_ShoppingAdminSignInPageState();
}
class _ShoppingAdminSignInPageState extends State<ShoppingAdminSignInPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _adminIDController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return AdaptiveLayoutScaffold(
appBar: const ShoppingAdminSignInPageAppBar(),
landscapeBodyWidget: Container(),
portraitBodyWidget: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
const ShoppingAdminSignInHeader(),
Form(
key: _formKey,
child: Column(
children: [
const SizedBox(
height: 50.0,
),
AdminSignInTextField(
controller: _adminIDController,
labelText: TextFieldLabel.adminID,
prefixIcon: Icons.person,
textInputAction: TextInputAction.next,
),
AdminSignInTextField(
controller: _passwordController,
labelText: TextFieldLabel.password,
prefixIcon: Icons.password,
textInputAction: TextInputAction.done,
),
ShoppingAdminSignInButton(
onPressed: _adminIDController.text.isNotEmpty &&
_passwordController.text.isNotEmpty
? logInAdmin()
: () => showDialog(
context: context,
builder: (ctx) {
return const ErrorAlertDialog(
message: DialogString.addAdminIDAndPassword,
);
}),
),
const NotAnAdminButton(),
],
),
),
],
),
),
),
);
}
logInAdmin() {
FirebaseFirestore.instance.collection('admins').get().then((snapshot) {
snapshot.docs.forEach((result) {
if (result.data()['id'] != _adminIDController.text.trim()) {
SnackBarUtil.showSnackBar(
context,
SnackBarString.idNotCorrect,
);
} else if (result.data()['password'] !=
_passwordController.text.trim()) {
SnackBarUtil.showSnackBar(
context,
SnackBarString.passwordNotCorrect,
);
} else {
SnackBarUtil.showSnackBar(
context,
'Welcome ${result.data()['name']}',
);
setState(() {
_adminIDController.text = '';
_passwordController.text = '';
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const UploadItemsPage(),
),
);
}
});
});
}
}
onPressed: _adminIDController.text.isNotEmpty && _passwordController.text.isNotEmpty
? logInAdmin()
: () => showDialog(
above, what you are saying is if a condition is true (in this case the condition is both _adminIDController and _passwordController to not be empty) then it should run logInAdmin and wait for it to finish and then run whatever logInAdmin returned.
Dart thinks logInAdmin will return a function and it should run that function. This is not the case, you want the button to directly run logInAdmin.
To fix this, remove the parenthesis:
onPressed: _adminIDController.text.isNotEmpty && _passwordController.text.isNotEmpty
? logInAdmin
: () => showDialog(
This way, you are not assigning the result of the function, you are assigning the function itself.
Also as a general recommendation, you should always declare a return type on your functions so dart can tell you whenever this happens
void logInAdmin() {
...
I would like to display the uid and email from Firebase Auth (just used for auth only!) in my Drawer.
I have this method to return the uid :
static Future<String> getUID() async {
User? user = await FirebaseAuth.instance.currentUser;
return user!.uid;
}
And this is my Drawer :
class DrawerMenu extends StatefulWidget {
final String pageName;
const DrawerMenu({Key? key, required this.pageName}) : super(key: key);
#override
_DrawerMenuState createState() => _DrawerMenuState();
}
class _DrawerMenuState extends State<DrawerMenu> {
#override
Widget build(BuildContext context) {
// print(MaterialLocalizations.of(context));
return Scaffold(
appBar: AppBar(title: Text('Title')),
body: Center(
child: DashboardPage(),
),
drawer: Drawer(
child: ListView(padding: EdgeInsets.all(0.0), children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text(LoginPageService.getUID().toString()), // HERE HERE HERE HERE
accountEmail: Text('random#gmail.com'),
currentAccountPicture: CircleAvatar(
backgroundImage: ExactAssetImage('assets/random.jpg'),
),
otherAccountsPictures: <Widget>[
CircleAvatar(
child: Text('A'),
backgroundColor: Colors.white60,
),
CircleAvatar(
child: Text('R'),
),
],
onDetailsPressed: () {},
...
...
...
I know that LoginPageService.getUID() will return a Future, so yes it should not be used like that.
But i don't know what's the best way for doing it and where to put the code.. in the widget ? or elsewhere ?
Should i use .then((value).... to get the uid..
Let me know if you have experience with it, and how you did it
Thanks for any help !
To complete the answer of #Youri you can do this :
static Future<User?> getCurrentUser() async {
return await auth.currentUser;
}
And in your drawer :
String? uid, name;
#override
void initState() {
super.initState();
LoginPageService.getCurrentUser().then((user) {
setState(() {
uid = user?.uid;
email = user?.email;
});
});
}
And then you can use it like that :
accountName: Text(name.toString()),
You could make use of the FutureBuilder for any async code. This widget automatically rebuilds whenever the status of the provided Future changes.
Please checkout this widget of the week video from Flutter for more information on this Widget.
FutureBuilder (Flutter Widget of the Week)
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();
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"];
)};
I want to save a value from my Cloud Firestore as a string. I am using Flutter with Dart. I have been able to save it when building the page using MaterialepageRoute:
MaterialPageRoute(
builder: (context) => MainScreen(
currentUserId: firebaseUser.uid,
currentUserGender: document['gender'],
currentUserPreference: document['preference'],
)),
But this isn't an option with all of my pages, so I have to look for something else. I want to get the value from my Firestore Database, and then save it as a string, since I want to:
if (currentUserGender == 'male') {
//then do something
}
I have no idea how to do this, I have thought about using a Class, maybe the "get"-function with Firebase, but none have worked. I am not really sure how to do this, so any help is appreciated. I am able to get the currentUser. Here is a picture of my database:
https://imgur.com/KL7HX6P
Thanks in advance.
A Minimal Example: To fetch a Single Document Fields. Swap Collection & Document name in the code with your Own Names.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class GetUser extends StatefulWidget {
#override
_GetUserState createState() => _GetUserState();
}
class _GetUserState extends State<GetUser> {
Map<String, dynamic> userDetails = {};
Future<Null> getUser() async {
await Firestore.instance
.collection('users') // Your Collections Name
.document('eMAE4XF9cTYS12MpfOuWBW4P2WH3') // Your user Document Name
.get()
.then((val) {
userDetails.addAll(val.data);
}).whenComplete(() {
print('Data Fetched');
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
textColor: Colors.white,
color: Theme.of(context).accentColor,
onPressed: () async {
await getUser();
},
child: Text('Get User Detail from Cloud'),
),
userDetails.length > 0
? Column(
children: <Widget>[
Text('${userDetails['gender']}'),
Text('${userDetails['id']}'),
Text('${userDetails['nickname']}'),
userDetails['gender'] == 'male'
? Text('Its Boy')
: Text('Girl'),
],
)
: Text('No user Data, Please Fetch'),
],
),
),
);
}
}