Use Futurebuilder to create a DropdownFormField Flutter? - flutter

I'm new to flutter and have a array in firestore. It's structure is:
Now i want to fetch that array, and use that array to build a dropdownFormfield.
My fetch Function is following:
fetchSchoolList() async {
List<dynamic> data = [];
Future<DocumentSnapshot<Object?>> result = schoolList.doc('List').get();
return result;
}
and my FutureBuilder is:
FutureBuilder<DocumentSnapshot<Object?>>(
future: fetchSchoolList(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState ==
ConnectionState.active) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
print(snapshot);
}
return Text('Loading List');
}),
But with this i'm even unable to get that array to print. I am getting error:
type 'Future' is not a subtype of type 'Future<DocumentSnapshot<Object?>>?'
Please help, i'm stuck here for days.#

There's a couple of things you could do:
First of all change fetchSchoolList() async {...} to something like this:
Future<DocumentSnapshot<List<String>?>>() async {
Future<DocumentSnapshot<List<String>?>> result =schoolList.doc('List').get();
return result;
}
Object isn't exactly a type, also flutter doesn't really know how to store information in with the type <Object>. Also you don't reference the data variable anywhere so it has been omitted.
Next change your FutureBuilder to something like this:
FutureBuilder<DocumentSnapshot<List<String>?>>(
future: fetchSchoolList(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.active) {
return CircularProgressIndicator();
}
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
print(snapshot);
}
return Text('Loading List');
}),
This will specify what is being returned by the future (Basically the FutureBuilder's type) and because of that, you do not need to specify the snapshot type.

Related

How to find where error come from in snapshot.error?

I try to use StreamBuilder and I handle error by throw snapshot.error
StreamBuilder(
stream: stream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasError) {
throw snapshot.error!; <- This line
}
}
},
);
But It tell that This line is the one making error.
Is there any way to see where actual error come from?

why snapshot.data gives me "Object?", but it don't gives returning of future?

i want to learn flutter step by step, I created UI, now i wanted to create fake firebase, later i will implement real firebase and i will use cubit, and then bloc.
I found this error, and i don't know how to remove it:
"AsyncSnapshot<Object?> snapshot
The argument type 'Object?' can't be assigned to the parameter type 'FirstListModel'."
this is my code before i whas trying to implement fake firebase, as im trying to do now:
itemBuilder: (context, index) {
final oneElement = firstList[index];
return buildPlate(index,await oneElement);
},
this itemBuilder is inside "ReorderableListView". Now im going step further and im trying to implement my fakeFirebase:
itemBuilder: (context, index) {
return FutureBuilder(
future: fakeFirebase.getElement(index.toString()),
builder: (context, snapshot) {
if (snapshot.hasData) {
return buildPlate(index, snapshot.data);
} else {
return const CircularProgressIndicator();
}
},
);
},
in the code above i have error in "snapshot.data"
in order to have better understanding, it is my function from fakeFirebase:
#override
Future<FirstListModel> getElement(String? id) async {
FirstListModel error = FirstListModel(text: "error", id: "404");
if (id != null) {
return firstList[int.parse(id)];
}
return error;
}
and this is my model:
class FirstListModel {
String text;
String id;
FirstListModel({
required this.text,
required this.id,
});
}
this is begining of my "buildPlate":
Widget buildPlate(int index, FirstListModel oneElement) => ListTile(
if i need to post more of my code, fell free to ask, it is my own aplication, just to lear flutter
"firstList" is a list of models, it containes objects that will be added by the user, simulating firebase
Define the type parameter for the future builder and cast the snapshot.data to non null by using the ! operator
return FutureBuilder<FirstListModel>(
future: fakeFirebase.getElement(index.toString()),
builder: (context, snapshot) {
if (snapshot.hasData) {
return buildPlate(index, snapshot.data!);
} else {
return const CircularProgressIndicator();
}
},
);

Getting QuerySnapshot instead of DocumentSnapshot [FLUTTER]

StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
FirestoreUser firestoreUser =
FirestoreUser.fromDocument(snapshot.data); // Here snapshot.data is retrieving QuerySnapshot.
// How I can convert it to DocumentSnapshot
...
Hello StackOverflow users
I need to give to my new firestoreUser variable, which type of DocumentSnapshot. But I can't get it.
After writing snapshot.data it gives me an error called "QuerySnapshot is not subtype of DocumentSnapshot"
P.s I'm using StreamBuilder as you can see. Thank you
Your stream is FirebaseFirestore.instance.collection('users').snapshots() which is a QuerySnapshot, meaning, a List of QueryDocumentSnapshot which Extends DocumentSnapshot.
So if you want the documentSnapshot of every users in your 'users' collection, you will have to iterate over snapshot.data.docs:
But if you want to get the document of a particular user, then you can do:
StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').doc(userID).snapshots(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
FirestoreUser firestoreUser =
FirestoreUser.fromDocument(snapshot.data);
...

Flutter firestore streambuilder with a future

I have a simple question. The reference to my firestore collection is dynamic. In this piece of code, getDocumentReference() gives me a reference to document after checking the user's email.
I use this document reference to get my snapshots.
Future<Stream<QuerySnapshot>> getHabits() async {
DocumentReference document = await getDocumentReference();
var snapshots = document.collection('habits').snapshots();
return snapshots;
}
As you can see, I want to use this Future<Stream<QuerySnapshot>> for a streambuilder. How can I do that? I tried something like this. But it is not taking the future as input to stream
return StreamBuilder(
stream: getHabits(),
);
You can wrap it in a FutureBuilder:
return FutureBuilder<Stream<QuerySnapshot>>(
future: getHabits(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return StreamBuilder(stream: snapshot.data); // Success
} else if (snapshot.hasError) {
return Text('${snapshot.error}'); // Error
} else {
return CircularProgressIndicator(); // Loading
}
},
);

is it possible to create a function to use a FutureBuilder, and not have to repeat code?

Basically I use this syntax to use a FutureBuilder:
retur FutureBuilder(
future: future,// http request
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Text("success done");
}
if (snapshot.hasData) {
if (snapshot.data["error"] == "111") {
rerurn Text("not access server")
}
if (snapshot.data["data"]["ok"] == false) {
return Text("problem");
}
return Container();
} else {
return Text("Loading");
}
});
Every time I want to make a web request, I have to write all this code again.
I want to optimize this, so I'm thinking of converting the above code into a method where I simply pass a future (http request) parameter and return what my FutureBuilder would return.
I'm trying something like this:
Future generateFutureBuilder(Future<dynamic> future, Widget widget) {
//widget is a Widget to show when it finishes
FutureBuilder(
future: future,// http request
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Text("success done");
}
if (snapshot.hasData) {
if (snapshot.data["error"] == "111") {
}
if (snapshot.data["data"]["ok"] == false) {
return Text("problem");
}
return widget;
} else {
return Text("Loading");
}
});
}
generateFutureBuilder(http.get(myurl), Container(child:Text("finished")))
but it doesn't behave like it normally does.
What I can do?
Although using a method to abstract widgets is OK, it typically is better to create a class that extends StatelessWidget. To fix your current method, you need to make it return FutureBuilder, not Future, and actually add a return statement. Another good change would be changing the use of dynamics to generics for better type safety.
But another plausible solution that will give you a little more control when building your widget is to not abstract away the FutureBuilder itself, but just the snapshot it gives you. Example:
/// Returns a widget to show if needed (either error or loading), or null if success
Widget getFutureSnapshotWidget(AsyncSnapshot snapshot) {
// if has data that is ok:
// return null;
// else:
// return either an error as a Text or a CircularProgressIndicator
}
Then you can call the following in your builder of FutureBuilder:
final widgetToShow = getFutureSnapshotWidget(snapshot);
if (widgetToShow != null) {
return widgetToShow;
}
// Show regular widget here