Flutter: Streambuilder stuck on ConnectionState.waiting (BLOC) - flutter

I am trying to load images into a StreamBuilder using BLOC, but the Streambuilder hangs indefinitely on ConnectionState.waiting. This only happens if the document doesn't exist - though I'm using an Initial Value (empty list), so I'm not sure why it still hangs?
galleryBloc.dart:
class GalleryBloc {
final _multipleImageController = StreamController<List<File>>.broadcast();
Stream<List<File>> get multipleImageStream => _multipleImageController.stream;
// -----------------------------------------------------------------------------
// Load existing gallery images
// -----------------------------------------------------------------------------
Future<void> getGalleryImages({DocumentReference docRef, List<File> tmpGalleryImages, Function callback}) async {
try {
DocumentSnapshot snap = await docRef.get();
if (snap.exists) {
for (var img in snap.data['gallery_images']) {
File fetchedFile = await DefaultCacheManager().getSingleFile(img);
tmpGalleryImages.add(fetchedFile);
}
}
} catch (e) {
print(e.toString());
}
callback(tmpGalleryImages);
_multipleImageController.sink.add(tmpGalleryImages);
}
gallery.dart:
#override
Widget build(BuildContext context) {
GalleryBloc _galleryBloc = GalleryBloc();
return StreamBuilder<List<File>>(
stream: _galleryBloc.multipleImageStream,
initialData: <File>[],
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) print(snapshot.connectionState);
if (!snapshot.hasData) print("No Data");
return Column(....

Related

How can i have multiple function to fetch data in a FutureBuilder

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;
});

FutureBuilder with lists

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());

how to use method in Widgets in call log in flutter?

How to make call logs flutter
I tried several ways call this method in text widget in Listview.builder()
But it print in Debug Console
I try to shown in Listview()
I'm using call_log: ^3.0.3
void _callLogs() async {
Iterable<CallLogEntry> entries = await CallLog.get();
for (var log in entries) {
print(log.name);
}
}
what about this?
FutureBuilder<Iterable<CallLogEntry>>(
future: _callLogs,
builder: (BuildContext context, AsyncSnapshot<Iterable<CallLogEntry>> snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
... /// fill your listview with snapshot.data...
}
}
)
future<Iterable<CallLogEntry>> _callLogs() async => CallLog.get();

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
}
},
);

Flutter - How do I use await inside the streambuilder?

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.