Can't assign a data value to a string - returns null - flutter - firebase

In my code, am trying a assign a string value to an empty string and display on the page but it keeps showing null but when I print it out, it shows the value.
String fName = '';
#override
void initState() {
super.initState();
getData();
}
getData() async {
FirebaseAuth _auth = FirebaseAuth.instance;
User _firebaseUser = _auth.currentUser;
print("============ MyHome ================");
print(_firebaseUser.uid);
_currentUser = await Database().getUserData(_firebaseUser.uid);
if (_currentUser != null) {
fName = _currentUser.firstName;
print(_currentUser.firstName);
}
}
database
Future<UserData> getUserData(String uid) async {
UserData returnValue = UserData();
try {
DocumentSnapshot _docSnapshot =
await _firestore.collection("users").doc(uid).get();
returnValue.uid = uid;
returnValue.firstName = _docSnapshot.data()["firstName"];
returnValue.lastName = _docSnapshot.data()["lastName"];
returnValue.userMail = _docSnapshot.data()["userMail"];
returnValue.userType = _docSnapshot.data()["userType"];
print("====================== on getData =============");
print(returnValue.firstName);
} catch (e) {
print(e);
}
return returnValue;
}
And whenever I try displaying the data it gives me null
Text("Hello, $fName"),
Please how do I do this or am I missing something

use setState to rebuild the widget tree with the value:
setState(() {
fName = _currentUser.firstName;
});

Since the getData function is async, flutter has already built the widget tree before getData finished. You'll now have to update the state using setstate.
setState(() {
fName = _currentUser.firstName;
});

You need to set the new state since we have made changes to the previous state (since your getData function is async.
setState(() {
fName = _currentUser.firstName;
});

Related

The argumnet type 'Null Funcion(DataSnapshot)' cnt be assigned to the parameter type 'Future Or <dynamic> Function(DataBaseEvent)'

I have this function that is giving me an error.
the getCurrentOnLineUserInfo function is trying to get read data from the Firebase Database of the current user that is logged in.
The argument type 'Null Funcion(DataSnapshot)' can't be assigned to the parameter of type 'Future Or Function(DataBaseEvent)'
I am following a year-old tutorial, so the issue might be the code is old. I might need new syntax or something.
static void getCurrentOnLineUserInfo() async {
firebaseUser = await FirebaseAuth.instance.currentUser;
String userId = firebaseUser!.uid;
DatabaseReference reference =
FirebaseDatabase.instance.ref().child("user").child(userId);
print("getCurrentOnLineUser info executed!");
print('${firebaseUser!.email}${firebaseUser!.displayName}');
// errors below this
reference.once().then((DataSnapshot dataSnapshot) {
if (dataSnapShot!.value != null) {
userCurrentInfo = Users.fromSnapshot(dataSnapshot);
}
});
}
}
and here is my class that is assigning data. This class is giving no errors
class Users {
String? id;
String? email;
String? phone;
String? name;
Users({this.id, this.email, this.phone, this.name});
Users.fromSnapshot(DataSnapshot dataSnapshot) {
id = dataSnapshot.key!;
var data = dataSnapshot.value as Map?;
if (data != null) {
email = data?["email"];
name = data?["name"];
phone = data?["phone"];
}
}
}
The once method returns a DatabaseEvent, not a DataSnapshot. DatabaseEvent is a class that encapsulates a DataSnapshot AND a DatabaseEventType, to extract the snapshot, you must use DatabaseEvent.snapshot:
reference.once().then((event) {
final dataSnapshot = event.snapshot;
if (dataSnapShot!.value != null) {
userCurrentInfo = Users.fromSnapshot(dataSnapshot);
}
});
Here is another solution I think might do what you want:
// async methods should return a future
static Future<void> getCurrentOnLineUserInfo() async {
firebaseUser = await FirebaseAuth.instance.currentUser;
String userId = firebaseUser!.uid;
DatabaseReference reference =
FirebaseDatabase.instance.ref().child("user").child(userId);
final snapshot = await reference.get(); // you should use await on async methods
if (snapshot!.value != null) {
userCurrentInfo = Users.fromSnapshot(snapshot);
}
}
}
I was following the same old tutorial you mentioned, the #mobdev991 answer is correct and i think the reason why you don't receive data is the class where you are assigning data try this
class Users {
String? id;
String? email;
String? name;
String? phone;
Users({this.id, this.email, this.name, this.phone});
Users.fromSnapshot(DataSnapshot dataSnapshot) {
id = dataSnapshot.key;
email = (dataSnapshot.child("email").value.toString());
name = (dataSnapshot.child("name").value.toString());
phone = (dataSnapshot.child("phone").value.toString());
}
}

A value of type 'Future<String>' can't be assigned to a variable of type 'String'

I have this code where am supposed to upload an image and get the downloaded url but whenever i do that I get this error
my url is String url;. So please why is this not working as it is supposed to
PS
I checked other website to learn how to properly upload but it keeps giving me an error or is there a better way to do this.
My code image
uploadTask.whenComplete(()async{
url = await refs.getDownLoadURL();
....
});
Since it returns a Future you need to wait for it to be accessed
Example :
Future<String> createFolder(String folderName) async {
final dir = Directory(
'${(io.Platform.isAndroid ? await getExternalStorageDirectory() //FOR ANDROID
: await getApplicationSupportDirectory() //FOR IOS
)!.path}/$folderName');
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if ((await dir.exists())) {
return dir.path;
} else {
dir.create();
return dir.path;
}
}
Future<String> getIslamiSahittoBookFilePath(String savename) async {
Future<String> s = createFolder("Islami_Sahitto");
String filePath = await s;
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
//add more permission to request here.
].request();
io.File? f = null;
if (statuses[Permission.storage]!.isGranted) {
Directory? dir = await DownloadsPath.downloadsDirectory();
if (dir != null) {
String savePath = "${dir.path}/$filePath/$savename";
f = new io.File(savePath);
if (await f.exists()) {}
}
}
return f.toString();
}
Now this block You can use AnyWhere : Future String, to String :
bool isPreviousDownloaded = false;
String previousFilePath = "null";
getIslamiSahittoBookFilePath(fileNameToDownload).then((value) {
if (value != null) {
setState(() {
isPreviousDownloaded = true;
previousFilePath = value;
});
}
});

Wait for stream inside a Future: Flutter

I want to check if the Firebase DB is connected or not, so I have to use a Future to return a boolean
Have a check at my code..
#override
Future<bool> isAvailable() async {
bool ret = false;
await firebaseInstance.reference().child('.info/connected').onValue.listen((event) {
ret = event.snapshot.value;
});
return ret;
}
the firebaseInstace.reference is a StreamSubscription type and does not wait for the future to return me a result.
please help.
If you only need to know the current value, use once().then instead of onValue.listen
#override
Future<bool> isAvailable() async {
var snapshot = await firebaseInstance.reference().child('.info/connected').once();
return snapshot.value;
}
Instead of awaiting the end of the stream subscription (it never ends), just take the first value:
#override
Future<bool> isAvailable() => firebaseInstance.reference().child('.info/connected').onValue.first;
You can put the StreamSubcription in a variable
StreamSubscription subscription = someDOMElement.onSubmit.listen((data) {
// you code here
if (someCondition == true) {
subscription.cancel();
}
});
More can be found here is there any way to cancel a dart Future?
You can do the following:
#override
Future<bool> isAvailable() async {
bool ret = false;
Stream<Event> events =
FirebaseDatabase.instance.reference().child('.info/connected').onValue;
await for (var value in events) {
ret = value.snapshot.value;
}
return ret;
}
onValue returns a Stream<Event> and then you can use await for to iterate inside the Stream and get the data, and then it will return.

Getting output as "Instance of 'Future<dynamic>'" in flutter

I am trying get sum of a column in sqlite table using Bloc pattern.
debt_bloc.dart
getTotalAmount() async {
return await _debtRepository.getTotalAmt();
}
debt_dao.dart
Future<int> getTotalAmount() async {
final db = await dbProvider.database;
var result = await db.rawQuery("SELECT SUM(amount) FROM $debtDetailsTable");
int value = result[0]["SUM(amount)"];
return value;
}
debt_repositary.dart
Future getTotalAmount() => debtDao.getTotalAmount();
When i try to print like below
var total;
#override
void initState () {
super.initState();
_asyncMethod();
}
_asyncMethod() async {
var t = await debtBloc.getTotalAmount();
setState(() {
total = t;
});
}
print(total);
Output not updating when add new data. But if go back to home screen and come to respective screen value is updating.
Please guide me in right way. Thanks in advance
Await on your method first before printing it.
var total = await debtBloc.getTotalAmount(); // await here
print(total); // should now print some int value

How do I write tests for a transformed stream in Flutter?

I have a Provider which has a method which takes data from Firebase as a stream, transforms it to a list and returns a Stream<List<Model>> . I'm trying to write a test where I want to check if the items in the List are the same as I expect them to be. How can I do that?
My Current Code:
test('getContacts returns a empty list when there is no contact',() async{
when(sharedPreferencesMock.get(any)).thenReturn('uid'); //mock the sharedprefs
documentSnapshot = DocumentSnapshotMock(); //mock documentsnapshot
when(documentSnapshot.exists).thenReturn(true); // this is done to pass the getUidByUsername method
documentReference = DocumentReferenceMock(documentSnapshotMock: documentSnapshot);
documentReference.setData({
'uid':'uid',
'contacts':[] // setting the usename in the data already so that duplicate contact exception is thrown
});
userDataProvider.getContacts().asBroadcastStream().listen((data){
expect(data.length,0);
});
});
And the provider method
#override
Stream<List<Contact>> getContacts() {
CollectionReference userRef = fireStoreDb.collection(Paths.usersPath);
DocumentReference ref =
userRef.document(SharedObjects.prefs.get(Constants.sessionUid));
return ref.snapshots().transform(StreamTransformer<DocumentSnapshot, List<Contact>>.fromHandlers(handleData: (documentSnapshot, sink) async{
List<String> contacts;
if (documentSnapshot.data['contacts'] == null) {
ref.updateData({'contacts': []});
contacts = List();
} else {
contacts = List.from(documentSnapshot.data['contacts']);
}
List<Contact> contactList = List();
for (String username in contacts) {
print(username);
String uid = await getUidByUsername(username);
DocumentSnapshot contactSnapshot = await userRef.document(uid).get();
contactList.add(Contact.fromFirestore(contactSnapshot));
}
sink.add(contactList);
}));
}
Update:
StreamController streamController = StreamController<List<Contact>>();
StreamSink<List<Contact>> sink = streamController.sink;
Stream<List<Contact>> stream = streamController.stream;
stream.listen((List<Contact> list){
expect(list.length,1);
});
userDataProvider.mapDocumentToContact(userCollection, userRef, documentSnapshot, sink);
streamController.close();
Make the lambda function that you currently pass to the StreamTansformer a separate function and test that.
If you want to test the full function there is a Firebase mock package on pub.

Resources