Future<dynamic> is not a subtype of List<dynamic> - flutter

So I am trying to pass a list of String values from firestore table, but I am getting an exception type 'Future<dynamic>' is not a subtype of type 'List<dynamic>'
This is the function
getLectureList(String userId) async {
var collection = FirebaseFirestore.instance.collection('students');
var docSnapshot = await collection.doc(userId).get();
Map<String, dynamic>? data = docSnapshot.data();
List<String> _lectureList =
await data!['attendance']; //This line is kinda giving me trouble
userInfo = FirestoreWrapper()
.getStudentFromData(docId: currentUser(), rawData: data);
return _lectureList;
}
And this is the function where I am getting the exception thrown
#override
void initState() {
lectureList = getLectureList(currentUser()); // Getting an exception here
NearbyConn(context).searchDevices(devices: deviceList);
super.initState();
}
tried using await in the getLectureList() method but still getting the same problem

Why do you await your data? You already got it.
List<String> _lectureList = data!['attendance'];
Please note that I don't know what your data structure looks like, so I cannot tell you if this is correct, I can only tell you that it is more correct than before, because the await did not belong there.

You are getting an exception here lectureList = getLectureList(currentUser()); because the the parameter required by the getLectureList() method is the userId which is a string. I do not know what currentUser() return but I'm assuming it's the userId that you need when calling the getLectureList() method. Based on the error, it looks like currentUser() is an async method that returns a future after some time.
You're not awaiting that future. You shouldn't make the initState() method async so move the code block out of it into a separate method and then call it from initState().
Something like this,
#override
void initState() {
super.initState();
_getData();
}
void _getData() async {
lectureList =
getLectureList(await currentUser());
NearbyConn(context).searchDevices(devices: deviceList);
}
or
#override
void initState() {
super.initState();
_getData();
}
void _getData() async {
String _userID = await currentUser();
lectureList = getLectureList(_userID);
NearbyConn(context).searchDevices(devices: deviceList);
}
Which I recommend so you can see all the parts.
Making your method parameters required named parameters also help you to easily see what is needed to pass to a function/class/.
Eg.
getLectureList({required String userId}){
...
}
Your IDE will alert you on the type of object the function requires and it makes things clearer.
Ultimately, I think typing your classes makes it so much more easier to fetch data from fireStore Typing CollectionReference and DocumentReference
This way you can easily do this,
final moviesRef = FirebaseFirestore.instance.collection('movies').withConverter<Movie>(
fromFirestore: (snapshot, _) => Movie.fromJson(snapshot.data()!),
toFirestore: (movie, _) => movie.toJson(),
);
and get your data this way,
Future<void> main() async {
// Obtain science-fiction movies
List<QueryDocumentSnapshot<Movie>> movies = await moviesRef
.where('genre', isEqualTo: 'Sci-fi')
.get()
.then((snapshot) => snapshot.docs);
// Add a movie
await moviesRef.add(
Movie(
title: 'Star Wars: A New Hope (Episode IV)',
genre: 'Sci-fi'
),
);
// Get a movie with the id 42
Movie movie42 = await moviesRef.doc('42').get().then((snapshot) => snapshot.data()!);
}
Keeps everything dry and tidy.

< The data comes to list format thats why showing the exception of datatype >
List<String> lectureList = await getLectureList(currentUser()); // use
Future<List<String>> getLectureList(String userId) async {
- your code -
}

Instead of
List _lectureList =
await data!['attendance'];
Try this
_lectureList = await data![] As List

Related

Instance of _Future<int> is all I get when I try to get the total 'document' of my 'collection' in Firestore

Following is my code. I'm trying to get all the 'Babies' which are in documents:
class _HomePageeState extends State<HomePagee> {
String t_babies = getCount().toString();
}
Future getCount() async {
return FirebaseFirestore.instance.collection('Babies').snapshots().length;
}
Instead I get this error: instance of \_future\<int\>
Here is my Database. I expect to get 2 counts:
You need to use await when getting Future values and also you should pass Future and the type Future<int>:
Future<int> getCount() async {
return await FirebaseFirestore.instance.collection('Babies').snapshots().length;
}
and also get the method using await but inside and async function:
void example() async { // <---- here the async you need to add to use await
int babiesLength = await getCount(); // here use await
}
You should use setState to update the string , because the fetch takes time as it involves network.
String t_babies = '';
Future<void> _getCount() async {
setState((){
t_babies = FirebaseFirestore.instance.collection('Babies').snapshots().length.toString();
});
}
#override
void initState() {
super.initState();
_getCount();
}

Flutter - How to add and retrieve data to/from hive?

I know it sounds simple and I went through the example given in the documentation. Yet somehow I am unable to get it right.
This is what I have:
void main() async {
await Hive.initFlutter();
//Hive.openBox('workoutBox');
runApp(const MyApp());
}
...
Next Screen:
var box;
...
Trying to add to the box
Future<void> _save() async{
// save doc id somewhere
final Id = doc.id;
//box = await Hive.openBox('workoutBox');
box.put("Id", Id);
}
Trying to retrieve in another function:
var someId = box.get("Id");
Current error: get was called on null
My confusion is, where/how do you declare, open and retrieve from the box in this situation?
It seems you are forgetting to initialize a Box param and assign the value returned by the openBox function to it.
After Hive initialization you should have something like this:
Box<myValue> boxValue = await Hive.openBox("myKey");
Important: the retrieval method will dependend based on what you need to do and, more importantly, how you saved your data in the first place.
Let's say you saved data like this:
await boxValue.add(value);
By adding data like this, the key assigned to the value will be an auto-incremented one, so that trying to retrieve it with a specific key that never was assigned in the first place will fail.
If you did add the data like this:
await boxValue.put("myKey", value);
then you will be able to successfully fetch it using the intended key.
You can do the following:
void main() async {
await Hive.initFlutter();
await Hive.openBox('workoutBox'); //<- make sure you await this
runApp(const MyApp());
}
...
_save() { // <- can be a synchronous function
final box = Hive.box('workoutBox'); //<- get an already opened box, no await necessary here
// save doc id somewhere
final Id = doc.id;
box.put("Id", Id);
}
I have written an example app and a Flutter Cubits + Hooks + Hive DB tutorial. I have the following AppDatabase class there:
const String _bookBox = 'book';
#Singleton()
class AppDatabase {
AppDatabase._constructor();
static final AppDatabase _instance = AppDatabase._constructor();
factory AppDatabase() => _instance;
late Box<BookDb> _booksBox;
Future<void> initialize() async {
await Hive.initFlutter();
Hive.registerAdapter<BookDb>(BookDbAdapter());
_booksBox = await Hive.openBox<BookDb>(_bookBox);
}
Future<void> saveBook(Book book) async {
await _booksBox.put(
book.id,
BookDb(
book.id,
book.title,
book.author,
book.publicationDate,
book.about,
book.readAlready,
));
}
Future<void> deleteBook(int id) async {
await _booksBox.delete(id);
}
...

Why am I getting 'Future<dynamic>' instead of the return value in the function?

I'm trying to get the return value in my function but the output is 'Instance of Future' instead of the value of school field name in the database
#override
void initState() {
userId = _auth.currentUser!.uid;
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
super.initState();
}
Future getName(String publisherUid, String fieldname) async {
DocumentSnapshot publisherSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(publisherUid)
.get();
print(publisherSnapshot.get(fieldname));
return publisherSnapshot.get(fieldname);
}
but whenever i'm printing the publisherSnapshop.get(fieldname) i'm getting the correct value from the database
There are 2 ways to do it, you can create a Future method and call it inside the initState like below:
#override
void initState() {
initial();
super.initState();
}
Future<void> initial() async {
userId = _auth.currentUser!.uid;
// Remember using `()` to wrap the `await` to get it result
publisherSchool = (await getName(widget.postInfo['publisher-Id'], 'school')).toString();
}
Or you can use .then to call it directly inside the initState:
#override
void initState() {
userId = _auth.currentUser!.uid;
getName(widget.postInfo['publisher-Id'], 'school').then((value) {
publisherSchool = value.toString();
});
super.initState();
}
When you declare the getName() function, specify the return type as Future<String>, and then when you call getName(), you need to await the result e.g. publisherSchool = await getName(widget.postInfo['publisher-Id'], 'school').toString();
The reason why you are not getting the correct response is because whenever you are working with Futures it takes some time to finish and return the results. Meanwhile it is fetching the result you have to make it await so that the program will continue once that future function is complete since await/then is nowhere to be found in your code hence the issues.
To solve this make this change:
Change
publisherSchool =
getName(widget.postInfo['publisher-Id'], 'school').toString();
To
getName(widget.postInfo['publisher-Id'],
'school').then((value){
publisherSchool=value.toString()});

Flutter - await/async on a List - why does this only work when not using declarations?

Still new to Flutter :(. Can anyone help...
I have a class that stores a bunch of project information. One part of this is a list of topics (for push notification), which it grabs from a JSON file.
I apply a getter for the list of topics, and when getting it it calls an async function which will return a List
Future<List<String>> _pntopics() async{
final _json = await http.get(Uri.parse(_topicsUrl));
if (_json.statusCode == 200) {
return (jsonDecode(_json.body));
}else {
return [""];
}
}
Future<List<String>> get topics => _pntopics();
In my main.dart file, it calls this value like so...
Future<List<String>> _topiclist = await projectvalues.topics;
The response is however empty, pressumably because it is a Future - so it is grabbing the empty value before it is filled.
But I can't remove the "Future" part from the async method, because asnc methods require a Future definition.
Then I decided to remove the declarations entirely:
_pntopics() async{
final _json = await http.get(Uri.parse(_topicsUrl));
if (_json.statusCode == 200) {
return (jsonDecode(_json.body));
}else {
return [""];
}
}
get topics => _pntopics();
and in main.dart, a general declaration...
var _topiclist = await projectvalues.topics;
...and this works!
So what declaration should I actually be using for this to work? I'm happy to not use declarations but we're always to declare everthing.
You should return back Future<List<String>> return types to the function and the getter but for _topicslist you must use var, final or List<String> declaration because:
(await Future<T>) == T
i.e.
var _topiclist = await projectvalues.topics; // The type of _topiclist is List<String>
final _topiclist = await projectvalues.topics; // The type of _topiclist is List<String>
UPDATE
Your code should be:
Future<List<String>> _pntopics() async{
final _json = await http.get(Uri.parse(_topicsUrl));
if (_json.statusCode == 200) {
return List<String>.from(jsonDecode(_json.body));
}else {
return <String>[""];
}
}
Doing this you force _pnptopics returns List<String> as jsonDecode returns List<dynamic>.
P.S. It is good practice do not use dynamic types where they can be changed to specified types.

Flutter Firestore getDocuments() not working on initState

I'm having an issue trying to call a document from Firestore in initState. I can pull data from a StreamBuilder just fine, but I just want to call the data once on initState.
Here is what I'm doing:
class _PainLevelState extends State<PainLevel> {
final FirebaseAuth _auth = FirebaseAuth.instance;
final CollectionReference userCollection =
Firestore.instance.collection('users');
static final now = DateTime.now();
String _uid;
double myPainLevel = 0;
Future<void> getCurrentUser() async {
final FirebaseUser user = await _auth.currentUser();
if (mounted) {
setState(() {
_uid = user.uid;
});
}
}
Future<void> getCurrentPainLevel() async {
await userCollection
.document(_uid)
.collection('pain')
.where('time',
isGreaterThanOrEqualTo: DateTime(now.year, now.month, now.day))
.getDocuments()
.then((QuerySnapshot docs) {
if (docs.documents.isNotEmpty) {
print('yes');
} else {
print('no');
}
});
}
#override
void initState() {
super.initState();
getCurrentUser();
getCurrentPainLevel();
}
...
I just get a "no" every time I print to console. It's not get any documents when there is one. If I take the same code inside the future and put it somewhere else, like in the build method, it works, but it constantly builds and I don't want that. Any suggestion as to why it is not working? Thanks in advance.
I'm guessing here that your code will not always work, because getCurrentPainLevel might get called before getCurrentUser is completed, which will cause _uid to be null and therefore, not work as expected. Try to put then keyword after getCurrentUser method, like this:
#override
void initState() {
super.initState();
getCurrentUser().then((_) {
getCurrentPainLevel();
});
}
By the way, you should NOT be calling async methods in initState. Try to put the code somewhere else, like WidgetsBinding.instance.addPostFrameCallback(...).