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();
Related
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 search for a word in a big text file that is local (not calling any HTTP or API).
I'm using it with FutureBuilder because only the opening of the text file is async (the rest isn't)
but the opening itself is very fast.
I want to render CircularProgressIndicator, while searching, but it seems that the moment it finishes opening the file, the CircularProgressIndicator stops, and I have a blank screen for the long searching time.
What can I do to present a loading screen also while doing the regular for loop?
What I have is something like this pseudocode:
Future<Array> searchData() async{
results = [];
someBigTextFile= await getTextFile();
for(row in someBigTextFile){ // this loop takes a lot of time
if(row contains this.query) results.add(row);
}
return results;
}
Widget buildResults(BuildContext context) {
return FutureBuilder(
future: searchData(),
builder: (BuildContext context, AsyncSnapshot<Array> snapshot){
if (snapshot.connectionState != ConnectionState.done) {
print("not done yet");
return CircularProgressIndicator();
} else {
return snapshot.data;
}
}
}
The Future call should not be in your build function. Make the call to your future and use the result in your build function.
Future<Array> result = searchData();
Widget buildResults(BuildContext context) {
return FutureBuilder(
future: result,
builder: (BuildContext context, AsyncSnapshot<Array> snapshot){
if (snapshot.connectionState != ConnectionState.done) {
print("not done yet");
return CircularProgressIndicator();
} else {
return snapshot.data;
}
}
}
Where you make the call for result will depend on the structure of the rest of your code.
Give this a try. With the .then() syntax you can specifically stop sync code from running before a future returns.
Future<Array> searchData() async{
results = [];
await getTextFile().then((someBigTextFile){
for(row in someBigTextFile){ // this loop takes a lot of time
if(row contains this.query) results.add(row);
}
return results;
});
}
You should use isolates to spawn totally new working thread. Something like:
Array searchData( String someBigTextFileContents) {
results = [];
for(row in someBigTextFile){ // this loop takes a lot of time
if(row contains this.query) results.add(row);
}
return results;
}
Future<Array> searchData() async {
someBigTextFileContents= await getTextFile();
return compute( searchData, someBigTextFileContents )
}
Better example on here: https://flutter.dev/docs/cookbook/networking/background-parsing
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
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(....
My requirement is to make that StreamBuilder connection state to waiting.
I'm using publish subject, whenever I want to load data in stream builder I'm just adding data to the sink by calling postStudentsToAssign() method, here this method making an API call which takes some time, in that time I to want make that streamBuilder connection state to waiting
Stream Builder:
StreamBuilder(
stream: studentsBloc.studentsToAssign,
// initialData: [],
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
// While waiting for the data to load, show a loading spinner.
return getLoader();
default:
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
return _getDrawer(snapshot.data);
}
}),
Initializing Observable:
final _assignStudentSetter = PublishSubject<dynamic>();
Observable<List<AssignMilestoneModel>> get studentsToAssign =>
_studentsToAssignFetcher.stream;
Method that add's data to Stream:
postStudentsToAssign(int studyingClass, String milestoneId, String subject,
List studentList) async {
var response = await provider.postAssignedStudents(
studyingClass, milestoneId, subject, studentList);
_assignStudentSetter.sink.add(response);
}
You can send null to the stream, so the snapshot.connectionState changes to active. I don't know why and whether it's official solution, but it works (at least now). I found this accidentally.
I would like the Flutter team to explain how to set snapshot's connectionState. It's not clear from StreamBuilder documentation. It seems you should replace the stream with a new one to have snapshot in waiting state. But it's agains the logic you want to implement.
I checked StreamBuilder source to find out that the AsyncSnapshot.connectionState starts as waiting (after stream is connected), after receiving data changes to active. snapshot.hasData returns true if snapshot.data != null. That's how following code works.
class SearchScreen extends StatelessWidget {
final StreamController<SearchResult> _searchStreamController = StreamController<SearchResult>();
final SearchService _service = SearchService();
void _doSearch(String text) async {
if (text?.isNotEmpty ?? false) {
_searchStreamController.add(null);
_searchService.search(text)
.then((SearchResult result) => _searchStreamController.add(result))
.catchError((e) => _searchStreamController.addError(e));
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(children: <Widget>[
SearchBar(
onChanged: (text) => _doSearch(text),
),
StreamBuilder<SearchResult>(
stream: _searchStreamController.stream,
builder: (BuildContext context, AsyncSnapshot<SearchResult> snapshot) {
Widget widget;
if (snapshot.hasData) {
widget = Expanded(
// show search result
);
}
else if (snapshot.hasError) {
widget = Expanded(
// show error
);
}
else if(snapshot.connectionState == ConnectionState.active){
widget = Expanded(
// show loading
);
}
else {
// empty
widget = Container();
}
return widget;
},
),
]),
);
}
}