How to create a FirebaseAuth User from a query from his UID? - flutter

I am a bit puzzled about how the Users are managed.
My app successfully creates user accounts via FirebaseAuth whom are creating classified ads.
I need to fetch the owner of the ad from the User UID and so far my code is as follows:
Future<Map<String, dynamic>?> getUser(String uid) async {
final d = await FirebaseFirestore.instance
.collection("User")
.where("id", isEqualTo: uid)
.get();
if (d.docs.isEmpty) return null;
return d.docs.first.data();
}
This code is expected to fetch the given user as a map.
And now, I'd like to convert the Map<String,dynamic> as an actual User instance. But how I should do that?
And is it the right way to go? Because I am wondering whether the User should only be dedicated to the 'authenticated self'.

If I'm right, do you mean the User of the firebase_auth package? You cannot convert Map<String, dynamic> to User. My opinion is to convert it to a model. Take a look at the example below:
class UserModel {
UserModel(
this.id,
this.name,
this.email,
…
);
final String id;
final String name;
final String email;
…
factory UserModel.fromMap(Map<String, dynamic> data, String documentId) {
final String? name = data["name"];
final String? email = data["email"];
…
return UserModel(
documentId,
name ?? "",
email ?? "",
…
);
}
Map<String, dynamic> toMap() => {
"name": name,
"email": email,
…
};
}
UserModel.fromMap(d.docs.first.data(), d.docs.first.id),
UserModel(
"Name",
"Email",
…
).toMap(),

Related

is there a way to help me fix the type null is not a subtype of type Map<string, dynamic>?

I have been working on flutter for few months now, and I got to understand how to fix null safety issues but this one is really complicated for me and i would like to get helped.
Here is my problem:
Whenever I am trying to sign in, I am getting this error: 'Null' type is not a subtype of type Map<string, dynamic> I tried my best to figure out what goes wrong but I am unable,
any help will be a life saving...
import 'package:cloud_firestore/cloud_firestore.dart';
class User {
final String email;
final String uid;
final String photoUrl;
final String username;
final String bio;
final List followers;
final List following;
const User({
required this.email,
required this.uid,
required this.photoUrl,
required this.username,
required this.bio,
required this.followers,
required this.following,
});
then i continued here
Map<String, dynamic> toJson() => {
"username": username,
"uid": uid,
"email": email,
"photoUrl": photoUrl,
"bio": bio,
"followers": followers,
"following": following,
};
static User fromSnap(DocumentSnapshot snap) {
var snapshot = snap.data() as Map<String, dynamic>;
return User(
username: snapshot['username'],
uid: snapshot['uid'],
email: snapshot['email'],
photoUrl: snapshot['photoUrl'],
bio: snapshot['bio'],
followers: snapshot['followers'],
following: snapshot['following'],
);
}
}
now I am getting the error on this line: null type is not a subtype of Map<string, dynamic>
var snapshot = snap.data() as Map<String, dynamic>;
this is all my problem,
thanks in advance.
Your snapshot value is nullable and you try to cast it to map, instead of this:
var snapshot = snap.data() as Map<String, dynamic>;
try this:
var snapshot = snap.data() != null ? snap.data() as Map<String, dynamic> : {};
Also change your return to this:
return User(
username: snapshot['username']??””,
uid: snapshot['uid'] ??””,
email: snapshot['email'] ??””,
photoUrl: snapshot['photoUrl'] ??””,
bio: snapshot['bio'] ??””,
followers: snapshot['followers'] ??[],
following: snapshot['following'] ??[],
);

The getter "nameController" isn't defined for the type "User" - flutter

i am trying to send data to firestore and am following a tutorial on YouTube, this tutorial approach of posting data to the server is by creating "UserModel" class as follows:
class UserModel {
String? uid;
String? name;
String? email;
String? phone;
String? province;
String? dateOfBirth;
//parsing data to JSON
UserModel(
{this.uid,
this.name,
this.email,
this.phone,
this.province,
this.dateOfBirth});
//Access and fetching data from the server (cloud firestore)
factory UserModel.fromMap(map) {
return UserModel(
uid: map['uid'],
name: map['name'],
email: map['email'],
phone: map['phone'],
province: map['province'],
dateOfBirth: map['dateOfBirth'],
);
}
//Sending data to server (cloud firestore)
Map<String, dynamic> toMap() {
return {
'uid': uid,
'name': name,
'email': email,
'phone': phone,
'province': province,
'dateOfBirth': dateOfBirth,
};
}
}
Then in my registration form screen we create a method and declare an instance of the UserModel class as follows:
//Sign up method (when user clicks on sign up this method will be invoked)
void signUp(String email, String password) async {
if (_formKey.currentState!.validate()) {
await _auth
.createUserWithEmailAndPassword(email: email, password: password)
.then((value) => { postDataToFirestore();})
.catchError((e) {
Fluttertoast.showToast(msg: e!.message);
});
}
}
postDataToFirestore() async {
//Creating Instance of firestore from firebase
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
//Creating an instance of UserModel Class
UserModel userModel = UserModel();
//providing the fields values to the user model class
userModel.name = user!.nameController.text; // Error Here
await firebaseFirestore
.collection("users")
.doc(user.uid)
.set(userModel.toMap());
Fluttertoast.showToast(msg: "Account created successfully :) ");
Navigator.pushAndRemoveUntil(
(context),
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}
}
in the line where comment says "providing the fields values to the user model class" of this code the "user" is not accessing any "Controller" i created for the form fields my last search told me that this way is not used anymore, i will be thankful if anyone could provide the right way of posting data
Use
userModel.name = nameController.text;
You wrote user.namecontroller and the error says there is no name controller in user that you created

Assertion failed: field is String||field is FieldPath||field==FieldPath.documentId "Supported [field] types are [String] and [FieldPath]." Flutter

I am trying to read from my firestore database. I am getting this error:
Error: Assertion failed: file:///C:/Users/Desktop/flutter/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-3.1.14/lib/src/query.dart:311:7
field is String || field is FieldPath || field == FieldPath.documentId
"Supported [field] types are [String] and [FieldPath]."
This is the method ran when trying to read the database:
Future ReadUserinDatabase(String email) async {
DatabaseUser Users = FirebaseFirestore.instance
.collection('Users')
.where('Email' == email) as DatabaseUser;
print(Users.UID);
}
The DatabaseUser class:
class DatabaseUser {
final String CustomerID;
final String Email;
final String Role;
final String Activity;
final String UID;
DatabaseUser(
{required this.Email,
required this.CustomerID,
required this.Role,
required this.Activity,
required this.UID});
Map<String, dynamic> toJson() => {
'CustomerID': CustomerID,
'Email': Email,
'Role': Role,
'Activity': Activity,
'UID': UID
};
static DatabaseUser fromJson(Map<String, dynamic> json) => DatabaseUser(
Email: json['Email'],
CustomerID: json['CustomerID'],
Role: json['Role'],
Activity: json['Activity'],
UID: json['UID']);
}
I need to keep the user to pass the Role and UID so I can display specific things depending on their Role when they log in. Please Help!
You cannot use == inside the where() method, instead use isEqualTo
Future ReadUserinDatabase(String email) async {
DatabaseUser Users = FirebaseFirestore.instance
.collection('Users')
.where('Email', isEqualTo: email) as DatabaseUser;
print(Users.UID);
}

Flutter - null safety and models

I have returned to Flutter after while (in the meantime null safety was introduced). I ended up with a need to update a project. Let's say in that project I have a User model. I managed to update most of the project but the part that gives me a headache is logout action. There is a need to clear the user (after logout) which I need to set up as null or empty it otherwise, but of course I am getting an error:
Unhandled Exception: type 'Null' is not a subtype of type 'User'
So my question here is what is the best strategy to clear not only user but any other models I have for a redux state without hitting this problem with models not being able to be null?
User model:
class User {
String id;
String username;
String email;
String jwt;
User({ required this.id, required this.username, required this.email, required this.jwt });
factory User.fromJson(Map<String, dynamic> json) {
return User (
id: json['id'],
username: json['username'],
email: json['email'],
jwt: json['jwt']
);
}
}
User actions:
/* User actions */
ThunkAction<AppState> getUserAction = (Store<AppState> store) async {
final prefs = await SharedPreferences.getInstance();
final String? storedUser = prefs.getString('user');
final user = storedUser != null ? User.fromJson(json.decode(storedUser)) : null;
if(user != null) {
store.dispatch(GetUserAction(user));
}
};
ThunkAction<AppState> logoutUserAction = (Store<AppState> store) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('user');
var user;
store.dispatch(LogoutUserAction(user));
};
class GetUserAction {
final User _user;
User get user => this._user;
GetUserAction(this._user);
}
class LogoutUserAction {
final User _user;
User get user => this._user;
LogoutUserAction(this._user);
}
NOTE: see how I managed to go about the null in the getUserAction (login) part. I just don't dispatch the action if it is null, however I can not do this in the logout as I need explicitly to set the user to null (clear it) and that way log it out from the app.
Can I make a model accept null values? How would I go about this? Or is there any other way I should go about this? Thank you for your kind answer.
Change your model to:
class User {
String? id;
String? username;
String? email;
String? jwt;
User({ required this.id, required this.username, required this.email, required this.jwt });
factory User.fromJson(Map<String, dynamic> json) {
return User (
id: json['id'] ?? "",
username: json['username'] ?? "",
email: json['email'] ?? "",
jwt: json['jwt'] ?? ""
);
}
}
And you need check null_safety for all variable with operation ??
After logout you can check user null with user.id == "" or user == User()
ThunkAction<AppState> logoutUserAction = (Store<AppState> store) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('user');
var user = User();
store.dispatch(LogoutUserAction(user));
};

Save nested objects in shared preferences

I have an object that contains a json array , which am trying to store in shared preferences but i don't know how to do so .
This is my model :
import 'dart:convert';
import 'package:deepnrise/models/settings/perimeter.dart';
import 'package:deepnrise/models/user/user_perims.dart';
UserWithPerim user(String str) => UserWithPerim.fromJson(json.decode(str));
class UserWithPerim {
// ignore: non_constant_identifier_names
UserWithPerim({
required this.identifier,
required this.firstName,
required this.lastName,
required this.email,
required this.role,
required this.perimeters,
});
String identifier;
String firstName;
String lastName;
String email;
String role;
List<UserPerimeter> perimeters;
factory UserWithPerim.fromJson(Map<String, dynamic> json) {
return UserWithPerim(
identifier: json['identifier'] ?? "",
firstName: json['firstName'] ?? "",
lastName: json['lastName'] ?? "",
email: json['email'] ?? "",
role: json['role'] ?? "",
perimeters: (json['perimeters'] as List)
.map((p) => UserPerimeter.fromJson(p))
.toList(),
);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['identifier'] = identifier;
data['firstName'] = firstName;
data['lastName'] = lastName;
data['role'] = role;
data['email'] = email;
data['perimeters'] = perimeters;
return data;
}
}
This the perimeters model :
import 'dart:convert';
Userperimeters(String str) => UserPerimeter.fromJson(json.decode(str));
String UserPerimToJson(UserPerimeter data) => json.encode(data.tojson());
class UserPerimeter {
// ignore: non_constant_identifier_names
UserPerimeter(
{required this.id, required this.label, required this.perimeterId});
// ignore: non_constant_identifier_names
int id;
String label;
int perimeterId;
factory UserPerimeter.fromJson(Map<String, dynamic> json) {
return UserPerimeter(
id: json['id'] ?? "",
label: json['label'] ?? "",
perimeterId: json["perimeterId"] ?? "");
}
Map<String, dynamic> tojson() => {
"id": id,
"label": label,
"perimeterId": perimeterId,
};
}
For now I've two models of my user object , one that contains the perils list and one that doesn't because whenever I try to store my user in shared prefs I get this exception thrown :
Unhandled Exception: type 'UserPerimeter' is not a subtype of type 'Map<String, dynamic>'
This is how am saving and reading my user:
saveUser(value) async {
final prefs = await SharedPreferences.getInstance();
String user = jsonEncode(User.fromJson(value));
prefs.setString(Preferences.USER_KEY, user);
}
Future<User?> getUser() async {
final prefs = await SharedPreferences.getInstance();
if (prefs.containsKey(Preferences.USER_KEY)) {
Map<String, dynamic> userMap =
jsonDecode(prefs.getString(Preferences.USER_KEY) ?? "");
User user = User.fromJson(userMap);
return user;
}
}
Is there a way with which I can store the whole user model with the perils object list without making two models of the user object ? thank you so much in advance.
The work around here to convert your whole json response to string.
save that string into sharedprefs, then you can call it back and decode it using:
var response = json.decode(prefs.getString("response");
So, the full idea:
prefs.setString("response",json.encode(response.body));
using that String as json format again:
MyModel model = MyModel.fromJson(json.decode(prefs.getString("response")));
I hope you find what you need from this idea.
Convert the list of perimeters to list of Json like this:
if (this.perimeters != null) {
data['perimeters'] = this.perimeters!.map((v) => v.toJson()).toList();
}