Firebase Realtime Databae adds another layer of data - firebase

For some reason, my Firebase Realtime Database adds another layer when encoding my data. I am new to using Firebase services, so maybe I entered an incorrect link or smh. -N-1sGl-7VrhyIG7PdDa should not appear. I have a slight idea of why it's happening, but I don't know how to access that last part. Thanks in advance!
Future<void> AddUserGoals(
String userId, String kcal, String p, String c, String f, BuildContext context) async {
final url = Uri.parse(
'https://recipier-e1139-default-rtdb.europe-west1.firebasedatabase.app/usersData/$userId/userGoals.json');
try {
print(kcal);
final response = await http.post(
url,
body: json.encode(
{
'currentBalance': kcal,
'protein': p,
'carbs': c,
'fats': f,
},
),
);
var decodedData = json.decode(response.body) as Map<String, dynamic>;
print(decodedData['currentBalance']);
if (decodedData['error'] == null) {
balance = decodedData['currentBalance'];
} else {
showDialog(
context: context,
builder: (ctx) => const AlertDialog(
title: Text('An error accured'),
content: Text('Please try again later.'),
),
);
}
notifyListeners();
} catch (err) {
rethrow;
}
}
void didChangeDependencies() {
if (_runsForFirstTime == true) {
setState(() {
_isLoading = true;
});
User? user = FirebaseAuth.instance.currentUser;
Provider.of<RecipeProvider>(context).fetchProducts();
Map<String, dynamic> initialData =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>;
Provider.of<DiaryProvider>(context, listen: false)
.AddUserGoals(user!.uid, initialData['kcal']!, initialData['p']!,
initialData['c']!, initialData['f']!, context)
.then((_) {
setState(() {
_isLoading = false;
});
});
}
_runsForFirstTime = false;
super.didChangeDependencies();
}

When you call http.post() you tell the REST server to create a new resource (with a unique ID) under the path, so that's what Firebase does.
If you want the server to write the data you pass at the path, use http.put().
Also see:
What is the difference between POST and PUT in HTTP?

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 How to model any class instead of FutureBuilder?

I have a model and I want to use my services file to fill it from Firebase but I don't know how to do that ?
I am filling it with FutureBuilder that's okey. But it is exhausting me.
Here is my model:
class ReviewModel {
String? uid;
String? userID;
String? comment;
dynamic rate;
ReviewModel({
this.uid,
this.userID,
this.comment,
this.rate,
});
Map<String, dynamic> toMap() {
return {
'uid': uid,
'userID': userID,
'comment': comment,
'rate': rate,
};
}
factory ReviewModel.fromMap(Map<String, dynamic> map) {
return ReviewModel(
uid: map['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
factory ReviewModel.fromDatabase(
DocumentSnapshot snapshot, Map<String, dynamic> map) {
return ReviewModel(
uid: snapshot['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
}
Code is Following below,
Future<ReviewModel> getSalonReviews(String salonUID) async {
CollectionReference aRef = FirebaseFirestore.instance
.collection("salons")
.doc(salonUID)
.collection('bucket')
.doc('reviewbox')
.collection('reviews');
dynamic _doc;
var snapshot;
try {
await aRef.get().then((querySnapshot) => {
for (var dummyDoc in querySnapshot.docs)
{
_doc = dummyDoc.data(),
print(_doc),
}
});
return ReviewModel.fromMap(_doc);
} on FirebaseException catch (e) {
Get.snackbar("Hata", e.code);
rethrow;
}
}
This code is not returning my ReviewModel.
Also I am using GetX and this is my GetX code:
final Rx<ReviewModel> _reviewModel = ReviewModel().obs;
ReviewModel get reviewModel => _reviewModel.value;
set reviewModel(ReviewModel value) => _reviewModel.value;
Future fillReviewModel(String uid) async {
SalonController.instance.reviewModel =
await FakeService().getSalonReviews(uid);
}
it return me this:
And this is my Firebase docs:
How do I achive my ReviewModel with Obx. If I try it, it returns null.
You don't have to return a model you'll do something like this in your prvoider file:
List _reviews = [];
List get reviews => [..._reviews];
// IN your future void function
Future<void> myFunction () async{
myReviews = ...result of forEach;
// now update _reviews
_reviews = [...myReviews];
//And then notify listeners
notifylisteners;
}
And then in your futurebuilder
FutureBuilder(future: Provider.of<myClass>(context, listen:false).myFunction(),
builder:(context, snapshot){
// check the state like the following
if(snapshot.connectionState == ConnectionState.done){
final myValues = Provider.of<myClass>(context, listen:false).reviews;
...do something
return your_values}
if(snapshot.connectionState == ConnectionState.waiting){return progressIndicator}
})

How to pass an image url to firebase Firestore?

I am able to pass the name, address, password, and email, but the url is "", how can I pass the image url next to the email and address data? If anyone can help, I would appreciate it very much !! I have a class called User Model and a SignUp.
String userImageUrl = "";
static final String userAvatarUrl = 'url';
final picker = ImagePicker();
PickedFile _imageFile;
if (_formkey.currentState.validate()) {
Map<String, dynamic> userData = {
// "uid": model.firebaseUser.uid,
"name": _nameController.text,
"email": _emailController.text,
"address": _addressController.text,
"url": userImageUrl,
};
model.signUp(
userData: userData,
pass: _passController.text,
onSucess: _onSucess,
onFail: _onFail,
);
}
uploadAndSaveImage();
uploadToStorage();
Future uploadToStorage() async {
showDialog(
context: context,
builder: (c) {
return LoadingAlertDialog(
message: "Autentificando, Por favor espere...",
);
});
String imageFileName = DateTime.now().millisecondsSinceEpoch.toString();
Reference reference = FirebaseStorage.instance.ref().child(imageFileName);
UploadTask task = reference.putFile(File(_imageFile.path));
await (await task).ref.getDownloadURL().then((urlImage) {
urlImage = userImageUrl;
class UserModel extends Model {
Future<Null> _saveUserData(Map<String, dynamic> userData) async {
this.userData = userData;
await FirebaseFirestore.instance
.collection('users')
.doc(firebaseUser.uid)
.set(userData);
}
}

DateTime not a subtype of type TimeStamp/Unhandled Exception: Invalid argument: Instance of 'Future<LocationData>

So I am using the nearby connections API to discover devices around me and store their data in firestore however I keep getting 2 warnings about the location I am getting from the user that I came in contact with and the time i came in contact with them
These are the 2 warnings:
1)DateTime not a subtype of type TimeStamp
2)Unhandled Exception: Invalid argument: Instance of Future<.LocationData.>
as I try to add these values to firestore
here is my discovery method:
void discovery() async {
try {
bool a = await Nearby().startDiscovery(loggedInUser.email, strategy,
onEndpointFound: (id, name, serviceId) async {
print('I saw id:$id with name:$name'); // the name here is an email
var docRef =
_firestore.collection('users').document(loggedInUser.email);
// When I discover someone I will see their email
docRef.collection('met_with').document(name).setData({
'email': await getUsernameOfEmail(email: name),
'contact time': DateTime.now() as Timestamp ,
'contact location': location.getLocation(),
});
}, onEndpointLost: (id) {
print(id);
});
print('DISCOVERING: ${a.toString()}');
} catch (e) {
print(e);
}
}
This is another method where I retrieve the info I discovered from firestore:
void addContactsToList() async {
await getCurrentUser();
_firestore
.collection('users')
.document(loggedInUser.email)
.collection('met_with')
.snapshots()
.listen((snapshot) {
for (var doc in snapshot.documents) {
String currEmail = doc.data['email'];
DateTime currTime = doc.data.containsKey('contact time')
? (doc.data['contact time'] as Timestamp).toDate()
: null;
String currLocation = doc.data.containsKey('contact location')
? doc.data['contact location']
: null;
String _infection = doc.data['infected'];
if (!contactTraces.contains(currEmail)) {
contactTraces.add(currEmail);
contactTimes.add(currTime);
contactLocations.add(currLocation);
infection.add(_infection);
}
}
setState(() {});
print(loggedInUser.email);
});
}
Any fix for this please?
Use an async function to convert the Future<.LocationData.> to LocationData.
var data;
void convertData() async{
var futuredata = await FutureLocationData;
setState(() {
data = futuredata });
}

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