Flutter firebase logging in causes NoSuchMethodError - firebase

I was following a tutorial to create an authentication system in flutter using firebase. Registration works as intended and the user is posted to my firebase database, however, when I try to login with the correct credentials, I get:
I/flutter (26424): user A a#a.com added
I/InputMethodManager(26424): showSoftInput
I/InputMethodManager(26424): mServedView =com.waifuhub.app;view =com.waifuhub.app;flags =0
I/HwSecImmHelper(26424): mSecurityInputMethodService is null
W/InputMethodManager(26424): startInputReason = 3
W/IInputConnectionWrapper(26424): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper(26424): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper(26424): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper(26424): requestCursorAnchorInfo on inactive InputConnection
I/InputMethodManager(26424): showSoftInput
I/InputMethodManager(26424): mServedView =com.waifuhub.app;view =com.waifuhub.app;flags =0
I/HwSecImmHelper(26424): mSecurityInputMethodService is null
W/InputMethodManager(26424): startInputReason = 3
V/AudioManager(26424): playSoundEffect effectType: 0
V/AudioManager(26424): querySoundEffectsEnabled...
I/HwSecImmHelper(26424): mSecurityInputMethodService is null
I/ViewRootImpl(26424): jank_removeInvalidNode all the node in jank list is out of time
I/flutter (26424): Sign In Error: NoSuchMethodError: The getter 'data' was called on null.
I/flutter (26424): Receiver: null
I/flutter (26424): Tried calling: data
Application finished.
Exited (sigterm)
The code responsible for signing in is as follows:
void _emailLogin(
{String email, String password, BuildContext context}) async {
if (_formKey.currentState.validate()) {
try {
SystemChannels.textInput.invokeMethod('TextInput.hide');
await _changeLoadingVisible();
//need await so it has chance to go through error if found.
await StateWidget.of(context).logInUser(email, password);
await Navigator.pushNamed(context, '/');
} catch (e) {
_changeLoadingVisible();
print("Sign In Error: $e");
String exception = Auth.getExceptionText(e);
Flushbar(
title: "Sign In Error",
message: exception,
duration: Duration(seconds: 5),
)..show(context);
}
} else {
setState(() => _autoValidate = true);
}
}
class StateWidget extends StatefulWidget {
final StateModel state;
final Widget child;
StateWidget({
#required this.child,
this.state,
});
// Returns data of the nearest widget _StateDataWidget
// in the widget tree.
static _StateWidgetState of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<_StateDataWidget>().data;
}
#override
_StateWidgetState createState() => new _StateWidgetState();
}
class _StateWidgetState extends State<StateWidget> {
StateModel state;
//GoogleSignInAccount googleAccount;
//final GoogleSignIn googleSignIn = new GoogleSignIn();
#override
void initState() {
super.initState();
if (widget.state != null) {
state = widget.state;
} else {
state = new StateModel(isLoading: true);
initUser();
}
}
Future<Null> initUser() async {
//print('...initUser...');
FirebaseUser firebaseUserAuth = await Auth.getCurrentFirebaseUser();
User user = await Auth.getUserLocal();
Settings settings = await Auth.getSettingsLocal();
setState(() {
state.isLoading = false;
state.firebaseUserAuth = firebaseUserAuth;
state.user = user;
state.settings = settings;
});
}
Future<void> logOutUser() async {
await Auth.signOut();
FirebaseUser firebaseUserAuth = await Auth.getCurrentFirebaseUser();
setState(() {
state.user = null;
state.settings = null;
state.firebaseUserAuth = firebaseUserAuth;
});
}
Future<void> logInUser(email, password) async {
String userId = await Auth.signIn(email, password);
User user = await Auth.getUserFirestore(userId);
await Auth.storeUserLocal(user);
Settings settings = await Auth.getSettingsFirestore(userId);
await Auth.storeSettingsLocal(settings);
await initUser();
}
#override
Widget build(BuildContext context) {
return new _StateDataWidget(
data: this,
child: widget.child,
);
}
}
class _StateDataWidget extends InheritedWidget {
final _StateWidgetState data;
_StateDataWidget({
Key key,
#required Widget child,
#required this.data,
}) : super(key: key, child: child);
// Rebuild the widgets that inherit from this widget
// on every rebuild of _StateDataWidget:
#override
bool updateShouldNotify(_StateDataWidget old) => true;
}
static Future<String> signIn(String email, String password) async {
FirebaseUser user = (await FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password))
.user;
return user.uid;
}
class Validator {
static String validateEmail(String value) {
Pattern pattern = r'^[a-zA-Z0-9.]+#[a-zA-Z0-9]+\.[a-zA-Z]+';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value))
return 'Please enter a valid email address.';
else
return null;
}
static String validatePassword(String value) {
Pattern pattern = r'^.{6,}$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value))
return 'Password must be at least 6 characters.';
else
return null;
}
static String validateName(String value) {
Pattern pattern = r"^[a-zA-Z]+(([',. -][a-zA-Z ])?[a-zA-Z]*)*$";
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value))
return 'Please enter a name.';
else
return null;
}
static String validateNumber(String value) {
Pattern pattern = r'^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$';
RegExp regex = new RegExp(pattern);
if (!regex.hasMatch(value))
return 'Please enter a number.';
else
return null;
}
}
And my dependencies in pubsec.yaml:
dependencies:
flutter:
sdk: flutter
flutter_launcher_icons: ^0.7.2
cupertino_icons: ^0.1.2
firebase_auth: ^0.15.5+3
cloud_firestore: ^0.13.4+2
flushbar: 1.9.1
shared_preferences: ^0.5.6+3
dev_dependencies:
flutter_test:
sdk: flutter
Upon further debugging, I think the error is due to the fact that null context is being passed to
_StateWidgetState, however, I am not sure if this is so, or how to solve this. Any help would be highly appreciated!

Not sure, but it seems you're missing calling
_formKey.currentState.save();
after calling _formKey.currentState.validate().
Otherwise this line is causing the error:
static _StateWidgetState of(BuildContext context) {
return (context.dependOnInheritedWidgetOfExactType() as _StateDataWidget)
.data;
}
This (context.dependOnInheritedWidgetOfExactType() as _StateDataWidget) is returning null
change it to:
return (context.inheritFromWidgetOfExactType(_StateDataWidget) as _StateDataWidget).data;
or
return context.dependOnInheritedWidgetOfExactType<_StateDataWidget>().data;

Related

A document path must be a non-empty string, Flutter - Firebase error?

I have some mistakes with flutter and firebase, if someone can help would be great here is my auth controller
class AuthController extends GetxController {
final FirebaseAuth auth = FirebaseAuth.instance;
final Rxn<User> _firebaseUser = Rxn<User>();
Rx<XFile>? _pickedImage;
XFile? get profilePhoto => _pickedImage?.value;
// final user = FirebaseAuth.instance.currentUser.obs;
Rxn<User> get user => _firebaseUser;
// final user = FirebaseAuth.instance.currentUser;
#override
onInit() {
_firebaseUser.bindStream(auth.authStateChanges());
super.onInit();
}
// void register(
// String name, String email, String password, XFile? image) async {
// try {
// UserCredential _authResult = await auth.createUserWithEmailAndPassword(
// email: email.trim(), password: password);
// //create user in database.dart
// String downloadUrl = await uploadToStorage(image!);
// UserModel _user = UserModel(
// id: _authResult.user?.uid,
// name: name,
// email: _authResult.user?.email,
// profilePic: downloadUrl,
// );
// if (await Database().createNewUser(_user)) {
// Get.find<UserController>().user = _user;
// }
// } catch (e) {
// Get.snackbar(
// "Error creating Account",
// e.toString(),
// snackPosition: SnackPosition.BOTTOM,
// );
// }
// }
void register(
String name, String email, String password, XFile? image) async {
try {
if (name.isNotEmpty &&
email.isNotEmpty &&
password.isNotEmpty &&
image != null) {
// save out user to our ath and firebase firestore
UserCredential _authResult = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
String downloadUrl = await uploadToStorage(image);
UserModel _user = UserModel(
id: _authResult.user?.uid,
name: name,
email: _authResult.user?.email,
profilePic: downloadUrl,
);
if (await Database().createNewUser(_user)) {
Get.find<UserController>().user = _user;
} else {
Get.snackbar(
'Error Creating Account',
'Please enter all the fields',
);
}
}
} catch (e) {
Get.snackbar(
'Error Creating Account',
e.toString(),
);
}
}
void login(String email, password) async {
try {
UserCredential _authResult = await auth.signInWithEmailAndPassword(
email: email.trim(), password: password);
Get.find<UserController>().user =
await Database().getUser(_authResult.user?.uid ?? '');
} catch (e) {
Get.snackbar("About User", "User message",
snackPosition: SnackPosition.BOTTOM,
titleText: Text("Acount creation failed"),
messageText:
Text(e.toString(), style: TextStyle(color: Colors.white)));
}
}
Future<void> signOut() async {
await auth.signOut();
Get.find<UserController>().clear();
}
Future pickImage() async {
print("call on click add photo icon");
final ImagePicker _picker = ImagePicker();
final XFile? pickedImage =
await _picker.pickImage(source: ImageSource.gallery);
print('picked image filled with image from gallery'); //This doesnt print at
if (pickedImage != null) {
Get.snackbar('Profile Picture',
'You have successfully selected your profile picture!');
// print(pickedImage.path);
}
_pickedImage = Rx<XFile>(pickedImage!);
// print(_pickedImage);
// print(profilePhoto);
}
// upload to firebase storage
Future<String> uploadToStorage(XFile? image) async {
Reference ref = FirebaseStorage.instance
.ref('')
.child('profilePics')
.child(auth.currentUser!.uid);
// print(ref);
UploadTask uploadTask = ref.putFile(File(image?.path ?? 'idemo'));
print(uploadTask);
// TaskSnapshot snap = await uploadTask;
String downloadUrl = await (await uploadTask).ref.getDownloadURL();
print(downloadUrl);
return downloadUrl;
}
}
Here is my function to createNewUser
class Database {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<bool> createNewUser(UserModel user) async {
try {
await _firestore.collection("users").doc(user.id).set({
"name": user.name,
"email": user.email,
"profilePhoto": user.profilePic
});
return true;
} catch (e) {
print(e);
return false;
}
}
Here is HomeController
class HomeController extends GetxController {
final Rxn<List<TodoModel>> todoList = Rxn<List<TodoModel>>([]);
var selectedDate = DateTime.now().obs;
List<TodoModel>? get todos => todoList.value;
#override
void onInit() {
super.onInit();
String? uid = Get.find<AuthController>().auth.currentUser?.uid ?? '';
print(uid);
todoList.bindStream(Database().todoStream(uid));
}
chooseDate() async {
DateTime? pickedDate = await showDatePicker(
context: Get.context!,
initialDate: selectedDate.value,
firstDate: DateTime(2000),
lastDate: DateTime(2024),
//initialEntryMode: DatePickerEntryMode.input,
// initialDatePickerMode: DatePickerMode.year,
);
if (pickedDate != null && pickedDate != selectedDate.value) {
selectedDate.value = pickedDate;
}
}
}
and here is View page
GetX<HomeController>(
init: Get.put<HomeController>(HomeController()),
builder: (HomeController todoController) {
if (todoController.todos != null) {
// print(todoController.todos?.done ?? false);
return Expanded(
child: ListView.builder(
itemCount: todoController.todos?.length,
itemBuilder: (_, index) {
return TodoCard(
uid: controller.user.value?.uid ?? '',
todo: todoController.todos![index],
);
},
),
);
} else {
return Text("loading...");
}
},
),
So, I have an error when I register a new user I got this error:
The following assertion was thrown building Builder(dirty):
a document path must be a non-empty string
Failed assertion: line 116 pos 14: ‘path.isNotEmpty’
And here is output from terminal:
The relevant error-causing widget was
GetMaterialApp
lib/main.dart:23
When the exception was thrown, this was the stack
#2 _JsonCollectionReference.doc
#3 Database.todoStream
#4 HomeController.onInit
#5 GetLifeCycleBase._onStart
#6 InternalFinalCallback.call
#7 GetInstance._startController
#8 GetInstance._initDependencies
#9 GetInstance.find
#10 GetInstance.put
#11 Inst.put
So a problem is with this path, and when I reload from the visual studio I god the right user with the right data. So the problem is when I register a user for the first time.
It looks like uid is empty, which you should also be able to see from looking up print(uid); in your output.
When your application or web page loads, Firebase automatically tries to restore the previously signed in user from its local state. This requires that it makes a call to the server however (for example to check if the account has been disabled) and while that call is going on, your main code continues to execute and the currentUser variable is going to be null.
Your code needs to take this into account. The easiest way to do this is to not depend on currentUser, but instead to use an reactively respond to changes in the authentication state as shown in the first example in the documentation on getting the current user:
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user != null) {
print(user.uid);
}
});
The authStateChange method here returns a stream that fires an event whenever the authentication state changes, so when the user signs in or signs out. The common way to use this stream is to either set the user to the state of your widget, or to use the stream directly in a StreamBuilder.

Flutter firebase auth web not persistent

I have the following auth provider, and I can sign the user in and the app state changes. But when I do a sign in I notice that nothing is stored in the local storage and when I refresh the page the authStateChanges gives null for the user. I also just tried tho get the current user as a Future which also returns null. What am I doing wrong?
const String oneTimePasswordFunctionName = "requestOneTimePassword";
const String signInWithOneTimePasswordFunctionName = "signInWithOneTimePassword";
class FirebaseAuthProvider extends AuthProvider {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final FirebaseFunctions _firebaseFunctions = FirebaseFunctions.instance;
Stream<AuthUser> get user {
return _firebaseAuth.authStateChanges().asyncMap((firebaseUser) {
//firebaseUser is always null on refresh
return firebaseUser == null ? AuthUser.empty : firebaseUser.toUser();
});
}
Future<void> requestOneTimePassword({required String email}) async {
final data = {"email": email};
final cloudFunction = _firebaseFunctions.httpsCallable(oneTimePasswordFunctionName);
await cloudFunction.call(data);
}
Future<void> signInWithOneTimePassword({required String email, required String oneTimePassword}) async {
final data = {"email": email, "oneTimePassword": oneTimePassword};
final cloudFunction = _firebaseFunctions.httpsCallable(signInWithOneTimePasswordFunctionName);
final result = await cloudFunction.call(data);
final jwt = result.data["data"]["jwt"] as String;
if (jwt.isEmpty) return;
await FirebaseAuth.instance.setPersistence(Persistence.LOCAL);
await _firebaseAuth.signInWithCustomToken(jwt);
}
Future<void> signOut() async {
try {
await _firebaseAuth.signOut();
} on Exception {
throw SignOutException();
}
}
}
extension on User {
FutureOr<AuthUser> toUser() async {
final tokenResult = await this.getIdTokenResult(true);
final claims = tokenResult.claims?.map<String, String>((key, value) => MapEntry(key, value.toString()));
return AuthUser(
id: uid,
email: Email.dirty(email ?? ''),
name: displayName ?? '',
photoURL: Uri.parse(photoURL ?? ''),
claims: claims ?? const {},
);
}
}
I really need it to work with the authStateChanges stream, because I want it also to work when the user does a sign out.
I am using the firebase auth emulator. This is my main
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FirebaseAuth.instance.useAuthEmulator("localhost", 9099);
FirebaseFirestore.instance.settings = Settings(
host: "localhost:8080",
sslEnabled: false,
persistenceEnabled: false,
);
FirebaseFunctions.instance.useFunctionsEmulator("localhost", 5001);
Future<dynamic> Function() initializeFirebase = () async {
if (Firebase.apps.length == 0) await Firebase.initializeApp();
};
await Future.wait([
initializeFirebase(),
EasyLocalization.ensureInitialized(),
]);
runApp(_buildAppWithDependencies());
}
I am testing in the latest version of google chrome
Versions
flutter: 2.5.3 (stable)
firebase_core: ^1.10.0
firebase_auth: ^3.2.0

Flutter GetX package Firebase Auth and FireStore "null check operator used on a null value"

First of all, I'm asking a question for the first time, I'm sorry if I'm asking incorrectly and incompletely, I will give you the information you want immediately.
Hey, I was following the tutorial in this video but I am getting an error like this.
video tutorial: https://www.youtube.com/watch?v=BiV0DcXgk58&t=314s&ab_channel=TadasPetra
Error is this:
Error simulator picture
I am getting this error, but the account is created in firebase Auth and the data I want is saved in FireStore database.
Flutter version: 2.2.3
pubspec.yaml
cloud_firestore: ^0.13.5
firebase_core: ^0.4.5
firebase_storage: ^3.1.6
get: ^4.3.8
Error debug console text
[GETX] Instance "AuthController" has been created
[GETX] Instance "AuthController" has been initialized
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
════════ Exception caught by widgets library ═══════════════════════════════════
The following _CastError was thrown building Root:
Null check operator used on a null value
The relevant error-causing widget was
Root
lib/main.dart:20
When the exception was thrown, this was the stack
#0 GetXState.initState
package:get/…/rx_flutter/rx_getx_widget.dart:78
#1 StatefulElement._firstBuild
package:flutter/…/widgets/framework.dart:4711
#2 ComponentElement.mount
package:flutter/…/widgets/framework.dart:4548
#3 Element.inflateWidget
package:flutter/…/widgets/framework.dart:3611
#4 Element.updateChild
package:flutter/…/widgets/framework.dart:3363
All files
Main.dart code
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
initialBinding: AuthBinding(),
theme: ThemeData(scaffoldBackgroundColor: bgColor),
home: Root(),
);
}
}
Root.dart code
class Root extends GetWidget<AuthController> {
#override
Widget build(BuildContext context) {
return GetX(
initState: (_) async {
Get.put<UserController>(UserController());
},
builder: (_) {
if (Get.find().user?.uid != null) {
return SMBottomNavBar();
} else {
return SignInScreen();
}
},
);
}
}
userController.dart
class UserController extends GetxController {
Rx<UserModel> _userModel = UserModel().obs;
UserModel get user => _userModel.value;
set user(UserModel value) => this._userModel.value = value;
void clear() {
_userModel.value = UserModel();
}
}
authController.dart
class AuthController extends GetxController {
FirebaseAuth _auth = FirebaseAuth.instance;
Rxn<FirebaseUser> _firebaseUser = Rxn<FirebaseUser>();
FirebaseUser get user => _firebaseUser.value;
#override
onInit() {
_firebaseUser.bindStream(_auth.onAuthStateChanged);
}
void createUserAccount(String userName, String email, String password) async {
try {
AuthResult _authResult = await _auth.createUserWithEmailAndPassword(
email: email.trim(), password: password);
//create user in database.dart
UserModel _user = UserModel(
userID: _authResult.user.uid, userName: userName, userEmail: email
);
if (await DatabaseServices().createNewUser(_user)) {
Get.find<UserController>().user = _user;
Get.back();
}
} catch (e) {
Get.snackbar(
"Error creating Account",
e.message,
);
}
}
void logInUser(String email, String password) async {
try {
AuthResult _authResult = await _auth.signInWithEmailAndPassword(
email: email.trim(), password: password);
Get.find<UserController>().user =
await DatabaseServices().getUser(_authResult.user.uid);
} catch (e) {
Get.snackbar(
"Error signing in",
e.message,
);
}
}
void logOutUser() async {
try {
await _auth.signOut();
Get.find<UserController>().clear();
} catch (e) {
Get.snackbar(
"Error signing out",
e.message,
);
}
}
}
authBinding.dart
class AuthBinding extends Bindings {
#override
void dependencies() {
Get.put<AuthController>(AuthController(), permanent: true);
}
}
user.dart(Model)
class UserModel {
String userID;
String userEmail;
String userName;
String userDisplayName;
String userProfilePhotoURL;
String userBioText;
int userScore;
UserModel(
{this.userID,
this.userEmail,
this.userName,
this.userDisplayName,
this.userProfilePhotoURL,
this.userBioText,
this.userScore});
UserModel.fromDocumentSnapshot(DocumentSnapshot doc) {
userID = doc.documentID;
userEmail = doc['userEmail'];
userName = doc['userName'];
userDisplayName = doc['userDisplayName'];
userProfilePhotoURL = doc['userProfilePhotoURL'];
userBioText = doc['userBioText'];
userScore = doc['userScore'];
}
}
You need to insert the type into your GetX widget.
return GetX<AuthController>( // add AuthController here
initState: (_) async {
Get.put<UserController>(UserController());
},
...
Your other issue is here
if (Get.find().user?.uid != null) {
return SMBottomNavBar();
}
If you use Get.find, same thing, always provide the type.
Get.find<AuthController>().user?.uid
But since you're using GetWidget<AuthController> you can do this
if (controller.user?.uid != null) {
return SMBottomNavBar();
}
...

Endless loop in firestore

I'm trying to update my documents in firestore, so when I'm trying to update it keeps updating without stopping. The first time it updates using the data from the signup dart file, then the second time it updates using the data from another dart file.
Here is the code for the signup:
FirebaseAuth auth = FirebaseAuth.instance;
await auth.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((value) => {
Navigator.pushNamed(context, 'DialogFlow'),
user=auth.currentUser,
user.sendEmailVerification(),
DatabaseService(uid:user.uid).UpdateUserData("", emailController.text, ChatScreenState().mess)
Here is the code for the other dart file:
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToEnd());
FirebaseAuth auth = FirebaseAuth.instance;
user=auth.currentUser;
DatabaseService db = DatabaseService(uid: user.uid);
return StreamBuilder(
stream: FirebaseFirestore.instance.collection("users").doc(user.uid).snapshots(),
builder: (context , snapshot){
print("====================================");
print(snapshot.data);
print("====================================");
if (snapshot.data != null) {
this.userTestMessage = "";
shhh = pressed ? true : false;
flag = true;
print(Retrieved_messages);
if (Retrieved_messages==false) {
this.messsages = snapshot.data['messsages'];
Retrieved_messages=true;
}
db.UpdateUserData(
user.displayName, user.email, this.messsages);
print(mess);
print(Retrieved_messages);
print("==============================");
print(snapshot.data);
print("==============================");
}
if (db.getUserMessages() == null) {
if (user != null) {
db.UpdateUserData(
user.displayName, user.email, this.messsages);
}
}
And the code for the database which sets and updates the documents is:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:satoshi/models/Userdata.dart';
import 'package:satoshi/widgets/dialog_flow.dart';
class DatabaseService {
//collection reference
final String uid;
List messsages=[];
DatabaseService({this.uid, this.messsages});
final CollectionReference userCollection = FirebaseFirestore.instance
.collection('users');
SetUserData(String Username, String Email,
List messsages) async
{ try {
return await FirebaseFirestore.instance.collection("users").doc(uid).set({
'Username': Username,
'Email': Email,
'messsages': messsages,
}
);
}catch(e){
print(e+" this is the error");
}
}
UpdateUserData(String Username, String Email,
List messsages) async
{ try {
return await FirebaseFirestore.instance.collection("users").doc(uid).update({
'Username': Username,
'Email': Email,
'messsages': messsages,
}
);
}catch(e){
print(e+" this is the error");
}
}
Future getUserMessages() async
{
DocumentSnapshot UserDetail = await userCollection.doc(uid).get();
var msg = UserDetail.data()['messsages'];
return await msg;
}
Stream<QuerySnapshot> get users {
return userCollection.snapshots();
}
Userdata userDataFromSnapshot(DocumentSnapshot snapshot) {
return Userdata(uid: uid,
name: snapshot.get('Username'),
email: snapshot.get('Email'),
messsages: snapshot.get('messsages'),
);
}
Stream<Userdata> get userData {
return userCollection.doc(uid).snapshots().asyncMap(userDataFromSnapshot);
}
}
Note: it keeps adding the data in the signup code, then adds the data in the other dart file, which results in an endless loop, also the snapshot isn't updating, it remains the same data as the signup
You are calling the Update function inside the stream builder so what it basically does is once the update function is called firebase gets notified of the document change and rebuilds the widget so again the update function is called and it turns into an infinite loop. what you can do is add any condition such that it won't get called again once the data is updated.
Example
if (<Somecondition>){
db.UpdateUserData(
user.displayName, user.email, this.messsages);
}

Unable to update data in firebase database while using phone authentication in Flutter

I am trying to add user registering form data to firebase database. But have tough time doing it effectivily. I am new to the flutter. I successfully register the user via phone number but unable to add corresponding details. I tried using getter and setter but it obviously is not working. I don't know what is idealic method of doing that. I tried googling it but didn't got any help. I am just stuck. I will really appreciate it.
Here is my code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:home_crisp/models/user.dart';
import 'package:home_crisp/services/auth.dart';
import 'package:provider/provider.dart';
class ChefRegisterScreen extends StatefulWidget {
ChefRegisterScreen();
#override
_ChefRegisterScreenState createState() => _ChefRegisterScreenState();
}
class _ChefRegisterScreenState extends State<ChefRegisterScreen> {
final AuthService _auth = AuthService();
bool loading = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String chefName = "", phoneNo = "";
String smsCode, verificationID;
String phnCode = "+92";
DateTime dateOfBirth;
bool codeSent = false;
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
final deviceSize = MediaQuery.of(context).size;
return loading
? Loading()
: Material(
type: MaterialType.card,
color: Color(0xffD4EBD3),
child: Stack(
children: <Widget>[
// --> Here is the code of getting "chefname, phone and dateOfBirth" and setting them using setState() method
//
// >>>>>>>>> Textfeild for getting OTP code
//
codeSent
? // Here is the code for textfeild which get OTP code
: Container(),
// ------------------------------------------------------ F I N I S H B U T T O N
FlatButton(
onPressed: () async {
if (user != null) {
print(
"TheRe IS uSer already logging in so signing out logging in new user");
AuthService().signOut();
}
if (codeSent) {
AuthService().signInWithOTP(smsCode, verificationID);
} else {
verifyPhone(phoneNo);
}
// ----> Here I tried to several methods to sent register form data to the seperate class named
// ----> "user.dart" from where I tried to extract that info in "auth" signinWithPhoneNumber method.
// ----> I first tried to send the info via constructor
ChefData(chefName: chefName,
chefPhNo: phoneNo,
chefDateOfBirth: dateOfBirth);
});
// ----> Then I tried "setter" but in vain
// ChefData().setChefName(chefName);
// ChefData().setChefPhNo(phoneNo);
// ChefData().setChefDateOfBirth(dateOfBirth);
child: ClipRRect(
child: Text(
"FINISH",
style: TextStyle(
color: Colors.white,
fontFamily: 'Montserrat',
fontSize: 20,
),
),
),
),
],
),
),
)
],
),
),
],
),
);
}
//
// >>>>>>>>> S I G N I N W I T H P H O M E N U M B E R P R O C E S S
//
Future<void> verifyPhone(phoneNo) async {
final PhoneVerificationCompleted verificationComplete =
(AuthCredential authResult) {
print('1. Auto retrieving verification code');
}
};
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verID) {
verificationID = verID;
print("\n2. Auto retrieval time out");
};
final PhoneCodeSent smsCodeSent =
(String verID, [int forceCodeResend]) async {
verificationID = verID;
setState(() {
this.codeSent = true;
});
print("\n 3. Code Sent to " + phoneNo);
};
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
print('${AuthException(smsCode, "message")}');
if (authException.message.contains('not authorized'))
print('App not authroized');
else if (authException.message.contains('Network'))
print('Please check your internet connection and try again');
else
print('Something has gone wrong, please try later ' +
authException.message);
};
await FirebaseAuth.instance
.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: Duration(seconds: 50),
verificationCompleted: verificationComplete,
verificationFailed: verificationFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
)
.then((value) {})
.catchError((error) {
print(error.toString());
});
}
}
Here is the "auth.dart" class
import 'package:firebase_auth/firebase_auth.dart';
import 'package:home_crisp/models/user.dart';
import 'package:home_crisp/services/database.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on FirebaseUser
User _userFormFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
// auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged.map(_userFormFirebaseUser);
}
// ------------------------------------------------------ S I G N I N W I T H P H O M E N U M B E R
signInWithPhoneNumber(AuthCredential authCreds) async {
try {
AuthResult result = await _auth.signInWithCredential(authCreds);
FirebaseUser user = result.user;
if (user != null) {
print('AUTHENTICATONI SUCCESSFULL. Id: ' + user.uid);
// ---->> Now here I tried create a new document for the chef with the uid by extracting the chef data // // ---->> from "user.dart" class
// ---->> I used getter method. I know there is going to be some better way to get that data
await DatabaseService(uid: user.uid).updateChefData(
ChefData().getChefName(),
ChefData().getChefPhNo(),
ChefData().getChefDateOfBirth());
return _userFormFirebaseUser(user);
} else {
print('Invalid code/invalid authentication');
}
} catch (e) {
print(e.toString());
return null;
}
}
signInWithOTP(smsCode, verId) {
AuthCredential authCreds = PhoneAuthProvider.getCredential(
verificationId: verId, smsCode: smsCode);
signInWithPhoneNumber(authCreds);
}
}
Here is "user.dart" file containing "ChefData" class acting as intermediary between "auth.dart" and "chefRegisterScreen.dart"
class User {
final String uid;
User({this.uid});
}
class ChefData {
String chefId;
String chefName;
String chefPhNo;
DateTime chefDateOfBirth;
ChefData({this.chefId, this.chefName, this.chefPhNo, this.chefDateOfBirth});
// void setChefId(String _chefId) {
// this.chefId = _chefId;
// }
// void setChefName(String _chefName) {
// this.chefName = _chefName;
// }
// void setChefPhNo(String _chefPhNo) {
// this.chefPhNo = chefPhNo;
// }
// DateTime setChefDateOfBirth(DateTime _chefDateOfBirth) {
// this.chefDateOfBirth = _chefDateOfBirth;
// }
String getChefId() {
return chefId;
}
String getChefName() {
return chefName;
}
String getChefPhNo() {
return chefPhNo;
}
DateTime getChefDateOfBirth() {
return chefDateOfBirth;
}
}
I had a similar problem and I used shared preferences to store the user details during registration then uploaded the details after phone authentication was successful.
Shared preferences is a flutter plugin that allows you to store simple data in key-value pair form.Here is a more detailed article about shared preferences and how to use it.
In your case, I suggest you create a class for shared preferences.
class SharedPreference{
static Future<String> storeChefData(ChefData chefData) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String storeUser = userToJson(chefData);
await prefs.setString('user', storeUser);
return storeUser;
}
static Future<ChefData> getChefData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getString('user') != null) {
ChefData chefData = userFromJson(prefs.getString('user'));
return chefData;
} else {
return null;
}
}
}
The storeChefData() function is for storing users details during registration. While the getChefData() function is for retrieving the users details in order to upload to the database after authentication.
In your user.dart file you will be required to add functions that convert the chefData to jsonData for storage and from jsonData to ChefData for retrieval as shown in the above functions.
ChefData userFromJson(String str) {
final jsonData = json.decode(str);
return ChefData.fromJson(jsonData);
}
String userToJson(ChefData data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class ChefData{
final String chefName;
final String chefPhNo;
final DateTime chefDateOfBirth;
ChefData({this.chefName,this.chefPhNo,this.chefDateOfBirth});
factory ChefData.fromJson(Map<String, dynamic> json) => ChefData(
chefName: json["chefName"],
chefPhNo: json["chefPhNo"]
chefDateOfBirth: json["chefDateOfBirth"]
);
Map<String, dynamic> toJson() => {
"chefName": chefName,
"chefPhNo": chefPhNo
"chefDateOfBirth": chefDateOfBirth
};
}
In your ChefRegistrationScreen you will add:
await SharedPreference.storeChefData(ChefData(chefName: chefName,chefPhNo: phoneNo,chefDateOfBirth: dateOfBirth));
at the point where you would like to insert the chefData.
Then in your auth.dart file you will add:
ChefData getChefData = await SharedPreference.getChefData();
await DatabaseService(uid: user.uid).updateUserData(getChefData);
in order to update your database.

Resources