Cannot get a field on DocumentSnapshotPlatform which does not exist - flutter

I am trying to display the name to the appbar but userid part seems like giving an error. iam a beginner to flutter and firestore can someone help me
class Profile extends StatefulWidget {
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
//To retrieve the Userid
User? user;
Future<void> getUserData() async {
User userData = await FirebaseAuth.instance.currentUser!;
setState(() {
user = userData;
print(userData.uid);
});
}
///////////////////////////////////////
Future<String>? _title;
#override
void initState() {
getUserData().then((value) => _title = _getAppBarNameWidget());
// _title = _getAppBarNameWidget();
super.initState();
}
//To retrieve the name from firestore
Future<String> _getAppBarNameWidget() async =>
await FirebaseFirestore.instance
.collection('customer')
.doc(user!.uid)
.get()
.then((DocumentSnapshot ds) async {
var name = ds['name'];
return name;
});

DocumentSnapshot ds does not directly contain document data, only document id, you have to use data() function to get data. It is also a good idea to check whether the document really exists, because you will get a snapshot event if the document is not found.
Examples:
ds.exists // will return true if document is found
ds.id // will return document reference
ds.data()!['name'] // will return 'name' field of document

Related

How to handle _Future<dynamic> value in flutter?

I am trying to get value from firebase in flutter. during that time, I am receiving _Flutter value returning from the Future<> type returning function. please help someone
I am having a code for fetching values from firebase.. the function gets a value from firebase by querying with an attribute
class FirebaseMethods {
Future<List> findEvents(dynamic attribute, dynamic value) async {
CollectionReference eventCollection =
FirebaseFirestore.instance.collection('events');
return eventCollection
.where(attribute, isEqualTo: value)
.get()
.then((QuerySnapshot querySnapshot) {
List events = [];
querySnapshot.docs.forEach((doc) {
events.add(doc.data());
});
return events;
}).catchError((error) {
print("Failed to retrieve events: $error");
});
}
Future<List> findUsers(dynamic attribute, dynamic value) async {
CollectionReference userCollection =
FirebaseFirestore.instance.collection('profile');
return userCollection
.where(attribute, isEqualTo: value)
.get()
.then((QuerySnapshot querySnapshot) {
List users = [];
querySnapshot.docs.forEach((doc) {
users.add(doc.data());
});
return users;
}).catchError((error) {
print("Failed to retrieve users: $error");
});
}
}
And I am calling the above function 'findUsers' in the following way:
dynamic database_functions = FirebaseMethods();
class RenderProfileView extends StatefulWidget {
String email;
RenderProfileView(this.email, {super.key});
#override
State<RenderProfileView> createState() => _RenderProfileViewState();
}
class _RenderProfileViewState extends State<RenderProfileView> {
TextEditingController name_controller = TextEditingController();
TextEditingController phone_number_controller = TextEditingController();
late dynamic user_json = database_functions.findUser('email', widget.email); // without late I am getting error and getting values with attribute 'email' = widget.email
dynamic get_name() {
print(user_json);
return 'some_value';
}
}
When the 'findUser' function is called, the printing message is -> Instance of '_Future'
Someone please help.. if any other way to solve the issue please mention it.
Future describes async operations in flutter. you must await all Futures results. Either by using the await keyword or .then property.
You could try adding initState to your stateful widget or go with a FutureBuilder depending on your use case.
Below is an edited version of your code.
dynamic database_functions = FirebaseMethods();
class RenderProfileView extends StatefulWidget {
String email;
RenderProfileView(this.email, {super.key});
#override
State<RenderProfileView> createState() => _RenderProfileViewState();
}
class _RenderProfileViewState extends State<RenderProfileView> {
TextEditingController name_controller = TextEditingController();
TextEditingController phone_number_controller = TextEditingController();
late dynamic user_json;
#override
void initState() {
super.initState();
database_functions.findUser('email', widget.email).then((data) {
user_json = data
});
}
String get name => 'some_value';
}

Receiving data as null in provider

This is My Repository
class DB {
final db = FirebaseFirestore.instance;
Stream<QuerySnapshot> init(UserModel user) {
return db
.collection('CollectionName')
.doc(user.email) //this is a unique value which i want to retrieve the value from main after successful login
.collection('New Collection')
.snapshots();
}
void readData(String id, UserModel user) async {
DocumentSnapshot snapshot = await db
.collection('Collection Name')
.doc(user.email)
.collection('New Collection')
.doc(id)
.get();
// ignore: avoid_print
print(snapshot['name']);
}
}
DB db = DB();
This is My BlocFile
class IncidentBloc implements BlocBase {
IncidentBloc(UserModel user) {
db.init(user).listen((data) => _inFirestore.add(data));
}
final _idController = BehaviorSubject<String>();
Stream<String> get outId => _idController.stream;
Sink<String> get _inId => _idController.sink;
final _firestoreController = BehaviorSubject<QuerySnapshot>();
Stream<QuerySnapshot> get outFirestore => _firestoreController.stream;
Sink<QuerySnapshot> get _inFirestore => _firestoreController.sink;
void readData(UserModel user) async {
db.readData(id, user);
}
#override
void dispose() {
_firestoreController.close();
_idController.close();
}
}
And This is my main
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
BlocOverrides.runZoned(
() => runApp(
BlocProviderr(bloc: IncidentBloc(UserModel()), child: const App())
),
blocObserver: AppBlocObserver(),
);
}
It seems that the UserModel is null or empty how do i pass value to my IncidentBloc? And this is after a successful login/authentication. If I do it like this in main: "IncidentBloc(UserModel(email: 'abcde.t#gmail.com'))" It is working, but i want it to dynamically retrieve data based on the user's email not the hardcoded 'abcde.t#gmail.com'
Based on your code, you will need to get the user's email from Firebase and pass it into Incident Bloc. This StackOverflow answer explains how to do that; so does this one.
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
<FirebaseUser> user = await _auth.currentUser();
final mailID = user.email;
BlocOverrides.runZoned(
() => runApp(
BlocProviderr(bloc: IncidentBloc(UserModel(email: mailID)), child: const App())
),
blocObserver: AppBlocObserver(),
);
}

How to load data stored in SharedPreference in flutter

I have a code for getting current logged in username and save it to a shared preference. The issue am facing is that whenever a user logs in for the first time, the username is never displayed, but when I do ahot reload on the app, the username is displayed on the screen . How can I have it in such a way the username is loaded on the first load without doing a hot reload.
How am getting the username on SharedPreference
/// Gets the current and prior accounts.
Future<dynamic> handleGetAccount() async { // <-- Replace dynamic with type of currentAccount
final result = await msal.getAccount();
if (result.currentAccount != null) {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString("username", result.currentAccount.username);
//print(result.currentAccount.username);
return result.currentAccount;
} else {
print('no account found');
return null;
}
}
My navigation to NavScreen ->redirects to Home screen
/// Updates the signed in state
refreshSignedInStatus() async {
bool loggedIn = await msal.getSignedIn();
if (loggedIn) {
isSignedIn = loggedIn;
if(isSignedIn) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NavScreen(
),
),
);
}
// Remaining code for navigation
}
}
how I am getting the username to show on home screen and show the username
class Home extends StatefulWidget {
const Home({Key key}) : super(key: key);
#override
HomeState createState() => new HomeState();
}
class HomeState extends State<Home> {
final TrackingScrollController _trackingScrollController =
TrackingScrollController();
String username = "";
#override
void initState() {
getName();
}
Future<String> getName() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
username = prefs.getString("username");
return username;
}
Because getName() is a async method, you should call setState((){}) after username got.
void getName() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
username = prefs.getString("username");
setState((){});
}

Global variable in Dart/Flutter

i wrote an function to login user by rest api in flutter. I want to use response from post but i don't know how to export my variable into another file.
I want use userID, but i dont know how,
can somebody help me?
class LoginScreenState extends State<LoginScreen>{
makeLoginRequest(String email, password) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'email':email,
'password':password
};
var jsonResponse;
var url = 'http://10.0.2.2:80/user/login';
var response = await http.post(url, body:data);
if(response.statusCode == 200){
jsonResponse = json.decode(response.body);
int userID = jsonResponse['id'];//HERE
if(jsonResponse != null){
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['token']);
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => UserPage()), (Route<dynamic> route) => false);
}
}
In your UserPage use a variable to get the ID.
Example:
class UserPage extends StatefulWidget {
final int userId;
UserPage({#required this.userId});
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
#override
Widget build(BuildContext context) {
return Container();
}
}
While navigating to UserPage after login pass the userID:
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => UserPage(userId: userID)), (Route<dynamic> route) => false);
When you want to get the value of userId in UserPage, you can use it in following way: widget.userId

Streambuilder only showing data from local cache on first launch of app

I'm using StreamBuilder to stream data from firestore.
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('meals').where('email', isEqualTo: loggedInUser.email).orderBy('date', descending: true).snapshots(),
If i take out the .where section of the stream, it returns all data to the device. Once this has been done, I can then put the .where section back in and it works fine. However, it doesn't work straight away. This would suggest the .where section only works once the cache already has data. Also, if I add a document using firestore console, it doesn't update the app with the new data. But for some reason it will show all of the updated documents if i remove the .where part.
I'm really confused. Any ideas?
Thanks Jason
UPDATE: I've now figured out how to solve this problem. Please see my answer below for how I solved it.
I finally figured out the answer to my problem.
I added queryUserData(); to the initState(). Here's how it looks in the code:
class HomeScreen extends StatefulWidget {
static const String id = 'home_screen';
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final _firestore = Firestore.instance;
final _auth = FirebaseAuth.instance;
FirebaseUser loggedInUser;
#override
void initState() {
super.initState();
getCurrentUser();
queryUserData();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
print('this is a test${loggedInUser.email}');
}
} catch (e) {
print(e);
}
}
void queryUserData() async {
final user = await _auth.currentUser();
loggedInUser = user;
final query = await _firestore.collection('meals').orderBy('date', descending: true).where('email', isEqualTo: '${loggedInUser.email}').getDocuments(source: Source.cache);
var totalEquals = query.documents.length;
print('$totalEquals records found for this user');
if (totalEquals >= 1) {
print(query);
print('cache has data. Therefore data will now only be read from cache');
} else {
print('data will be read from firestore until you have at least 1 meal');
getFirestoreInitialData();
}
}
void getFirestoreInitialData() async {
final query = await _firestore.collection('meals').getDocuments();
print(query);
print('data still being read from firestore');
}