In a FutureBuilder i'm trying to use multiple methods with different types, all of them fetch data from the api, the main problem that i'm having is that all of the functions have different types, so i'm having problem on putting methods because of their types.
Please try the code below:
Future? _future;
Future<dynamic> getData() async {
//you can have more functions here, for explanation purpose, i'll have 2
final data1 = await getData1();
final data2 = await getData2();
return [data1, data2];
}
#override
void initState() {
_future = getData()();
super.initState();
}
///
FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CupertinoActivityIndicator();
}
if (snapshot.hasError) {
return SomethingWentWrong();
}
final data1= snapshot.data[0] as YourData1Model;
final data2 = snapshot.data[1] as YourData2Model;
});
Related
I have a longList where I need each element in the list to be sent to a database to return some a sub-list.
I then display each value in it's own PageView using PageView.builder. and FutureBuilder.
At first I was setting future: sql(longlist[pageIndex]) to give the future for each element in the PageView. However this is bad because it means the database is queried every time the widget is rebuilt.
So what I want is in the list for each element, call await sql(element) in initState(), and store this in a way the futurebuilder can use it.
Here is the simplified layout of my code:
final List<Map<String, dynamic>> longList; // <-- eg ["cat", "dog", "mouse", ...]
late Future<List<Map<String, dynamic>>> exampleFuture;
Future<List<Map<String, dynamic>>> getUnits(int index) async {
final data = await SQLHelper.getExamples(longList[0]["foo"]);
return data;
}
#override
void initState() {
super.initState();
exampleFuture = getUnits(0);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: PageView.builder(
itemCount: widget.longList.length,
itemBuilder: (context, pageIndex) {
print(pageIndex);
return FutureBuilder<List<Map<String, dynamic>>>(
future: exampleFuture[pageIndex],
builder: (BuildContext context,
AsyncSnapshot<List<Map<String, dynamic>>> snapshot) {
if (snapshot.hasData) {
List<Map<String, dynamic>>? exampleList = snapshot.data;
return Column(
children:[Text(
"this is page $pageIndex with future data "+exampleList[pageIndex];
),]
);
} else {
return const Center(child: CircularProgressIndicator());
}
}
);
},
),
);
}
}
I tried instead to create a list of futures in the initState but if the list is itself a future I can't add elements to it and if it's not a future (ie. it only holds a reference to each future) it doesn't work with the futurebuilder as a future: parameter. For example I tried:
late List<List<Map<String, dynamic>>> futureList;
void createList() async {
for (int i = 0; i<widget.longList.length; i++){
futureList.add(await SQLHelper.getExamples(widget.longList[i]["foo"]));
}
}
void initState() {
super.initState();
createList()
}
but trying to use it like
return FutureBuilder<List<Map<String, dynamic>>>(
future: futureList[pageIndex]
didn't work and the futurebuilder thinks the list is empty.
Ideally I'd rather not use Future.wait because I'd like to show the first page immediately without waiting for the rest of the db queries. If that's the only solution I'd like to understand how to use it. I don't get how I would use Future.await here.
You can have a list of futures. It just looks like perhaps you didn't do it correctly. Here's what it might look like:
final List<Map<String, dynamic>> longList; // <-- eg ["cat", "dog", "mouse", ...]
late List<Future<List<Map<String, dynamic>>>> futureList;
Future<List<Map<String, dynamic>>> getUnits(int index) async {
final data = await SQLHelper.getExamples(longList[0]["foo"]);
return data;
}
#override
void initState() {
super.initState();
futureList = List.generate(widget.longList.length, (i) => getUnits(i));
}
Note the declaration for futureList: List<Future<...>>, which is a list of futures.
You can also use typedef Unit = List<Map<String, dynamic>>, to make things easier to understand.
typedef Unit = List<Map<String, dynamic>>;
final Unit longList;
//instead of List<List<Unit>>;
late List<Future<Unit>> futureList;
I am fetching my data from my json API with a Future function. The data is properly displayed in screen but is not in real time. I am using my Future function as a stream with the .asStream() method. Do I have to add a listener to this stream? In that case, how can I do that?
Stream<List<dynamic>>? _allMessagesContainedInTheStream;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<List<dynamic>>(
stream: fetchMessagesFromBack4App(widget.usermail, widget.usermailTo,
widget.title)
.asStream(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
....
....
this is my future function.
Future<List<dynamic>> fetchMessagesFromBack4App(
String usermail,
String usermailTo,
String dogName,
) async {
final queryBuilder = QueryBuilder(ParseObject('MessagesSender'))
..whereEqualTo('sender', usermail)
..whereEqualTo('receiver', usermailTo)
..whereEqualTo('dogname', dogName)
..orderByAscending('date');
final response = await queryBuilder.query();
if (response.success && response.results != null) {
return response.results!.toList() as List<ParseObject>;
} else {
return [];
}
}
This is the future function I want to call:
Future<List<Marker>> getMarkers() async {
List<Marker> markerTemp = [];
List<String> friendsList = [];
QuerySnapshot snapshot = FireStoreUtils.getFriendsList(current.userID);
for (var doc in snapshot.docs) {
friendsList.add(doc.reference.id);
}
for (var friend in friendsList) {
DocumentSnapshot document = await locationRef.doc(friend).get();
MarkerTemp.add(Marker(...))
}
return markerTemp;
}
Now I want it to be called in FutureBuilder widget to save the results in a variable called markerList that is useful for my view. How can I do?
return FutureBuilder<List<Marker>>(
future: getMarkers(),
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
// async call has not finished
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
// getMarkers() throws an exception
return Center(child: Text(snapshot.error.toString()));
}
if (!snapshot.hasData) {
// getMarkers() returns null
return const Center(child: Text("getMarkers() returns null!"));
}
markerList = snapshot.data as List<Marker>; // cast to List<Marker>
return SomeWidget(); // use markerList in this Widget
},
);
Your future builder, when the future finishes in your case, returns a list of markers. Now to use that list, you don't have to store it again, it's already returned and stored in your snapshot in your future builder. You can validate this by printing the length of it:
if(snapshot.hasData) print(snapshot.data.length.toString());
I want to use await inside streambuilder. However, if you use async inside, you get an error. On the code below !!!!!!!! That's the part I want to solve. Thank you very much if I can tell you how.
class _MemoStreamState extends State<MemoStream> {
final _fireStore = Firestore.instance;
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _fireStore
.collection(widget.logInUsrEmail)
.orderBy('id', descending: false)
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
final memos = snapshot.data.documents;
List<MemoMaterial> memoList = [];
for (var memo in memos) {
final memoDocumentID = memo.documentID;
final memoTitle = await PlatformStringCryptor().decrypt(memo.data['title'], _key); !!!!!!!!!!
final memoUsrID = memo.data['usrID'];
final memoUsrPW = memo.data['usrPW'];
final memoText = memo.data['text'];
final memoCreateTime = memo.data['createTime'];
final memoMaterial = MemoMaterial(
logInUsrEmail: widget.logInUsrEmail,
doc: memoDocumentID,
title: memoTitle,
usrID: memoUsrID,
usrPW: memoUsrPW,
text: memoText,
createTime: memoCreateTime,
);
memoList.add(memoMaterial);
}
return Expanded(
child: new ListView.builder(
You should do something like this :
Stream<List<MemoMaterial>> memosStream;
Future<MemoMaterial> generateMemoMaterial(Memo memo) async {
final memoTitle =
await PlatformStringCryptor().decrypt(memo.data['title'], _key);
return MemoMaterial(
logInUsrEmail: widget.logInUsrEmail,
doc: memo.documentID,
title: memoTitle,
usrID: memo.data['usrID'],
usrPW: memo.data['usrPW'],
text: memo.data['text'];,
createTime: memo.data['createTime'],
);
}
#override
void initState() {
memosStream = _fireStore
.collection(widget.logInUsrEmail)
.orderBy('id', descending: false)
.snapshots()
.asyncMap((memos) => Future.wait([for (var memo in memos) generateMemoMaterial(memo)]));
super.initState();
}
#override
Widget build(BuildContext context) {
return StreamBuilder<List<MemoMaterial>>(
stream: memosStream // Use memostream here
asyncMap() will "transform" every new set of Documents into a list of MemoMaterial, and emit this list into the stream when the action is performed.
Future.wait() allows to perform multiple async requests simultaneously.
You can do it using FutureBuilder inside StreamBuilder in following way.
Stream<List<int>> callme() async* {
yield [1, 2, 3, 4, 5, 6];
}
buildwidget() async {
await Future.delayed(Duration(seconds: 1));
return 1;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: StreamBuilder(
stream: callme(),
builder: (_, sna) {
if (sna.hasData) {
return FutureBuilder(
future: buildwidget(),
builder: (_, snap) {
if (snap.hasData) {
return ListView.builder(
itemCount: sna.data.length,
itemBuilder: (_, index) {
return Text("${sna.data[index]} and ${snap.data}");
},
);
} else {
return CircularProgressIndicator();
}
},
);
} else {
return CircularProgressIndicator();
}
}),
),
);
}
I will prefer to use Getx or Provider State management to Handle the UI if it depends on the async function.
Suppose you want to fetch data from firebase using StreamBuilder() which returns some docs which contains image links then you want to download these images and show from storage. Obviously downloading the image is async type of work. Then you will get error if you show the images with the links you get direct from StreamBuilder().
What you can do is set a variable in getx or provider to show or hide the image Widget. If the Image is being downloaded or not downloaded then set the variable to hide/show the image when the async type of function is completed.
My function defination is below
Future<List<Item>> fetchGitUsers() async {
final response = await http.get('https://');
if (response.statusCode == 200) {
GitUsers gitUser = GitUsers.fromJson(json.decode(response.body));
return gitUser.items;
}
} / Function end
class GitUsers {
List<Item> items;
}
class ... extends State<SearchController> {
#override
void initState() {
super.initState();
gitUsers = fetchGitUsers() as List<Item>;
}
}
But I am getting below error on emulator screen..
in type cast.
You didn't add await
Try this
void getUsers() async{
gitUsers = await fetchGitUsers();
}
#override
void initState() {
super.initState();
getUsers();
}
}
If you want to use the git users in a UI (e.g ListView), consider using FutureBuilder.
Like this
FutureBuilder(
future: fetchGitUsers(),
builder: (context, snapshot){
if(!snapshot.hasData) return CircularProgressIndicator();
return ListView();
}
)