FirebaseStorage - how to get all images in a folder - flutter

I'm trying to list all images in a created folder in FirebaseStorage.
Future getImages(String folderName) async {
final docRef = FirebaseStorage.instance.ref().child(folderName);
List imageRef = [];
docRef.listAll().then((result) async {
imageRef = result.items;
});
return imageRef;
}
FutureBuilder:
FutureBuilder(
future: getImages(images.first),
builder: (context, AsyncSnapshot snapshot) {
if(snapshot.hasData){
List list = snapshot.data;
return Image.network(list.first);
} else {
return const Center(child: CircularProgressIndicator(),);
}
}),
I can not return anything from a then() function neither can I use its value outside its body!
I thought about returning a future object and then use it inside my FutureBuilder but again I need to return a widget and I can't do that inside a then() function

Try to use await in getImages to get the list result and return the items like this:
Future<List<Reference>> getImages(String folderName) async {
final docRef = FirebaseStorage.instance.ref().child(folderName);
final listResult = await docRef.listAll();
return Future.value(listResult.items);
}
But if you need the download url of the images, you have to add further code since the above will return a List of Reference, and the getDownloadURL method of Reference is also async. You could try this if you need a list of urls:
Future<List<String>> getImages(String folderName) async {
final docRef = FirebaseStorage.instance.ref().child(folderName);
final listResult = await docRef.listAll();
final urls = <String>[];
for (var item in listResult.items) {
urls.add(await item.getDownloadURL());
}
return Future.value(urls);
}

Related

Extract String From Future<String> In Flutter

I'm using flutter, and I'm loading in a locally stored JSON file like so:
Future<String> loadJson(String file) async {
final jsonData = await rootBundle.loadString("path/to/$file.json");
return jsonData;
}
The problem is that this returns a Future<String> and I'm unable to extract the actual JSON data (as a String) from it.
I call loadJson in the Widget build method like so:
#override
Widget build(BuildContext context) {
final data = ModalRoute.of(context)!.settings.arguments as Map;
final file = data["file"];
String jsonData = loadJson(file); // The issue is here
return Scaffold (/* -- Snip -- */);
}
How would I go about doing this? Any help is appreciated.
loadJson is Future and you need to await for its result:
String jsonData = await loadJson(file);
you also can't run Future function inside build method, you need to use FutureBuilder:
return Scaffold (
body: FutureBuilder<String>(
future: loadJson(file),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
String jsonData = snapshot.data ?? "";
return /* -- Snip -- */;
},
}
}
},
),
);
You are getting data but not decoding it. You need to decode for using the loaded data.
Future<String> loadJson(String file) async {
final jsonData = await rootBundle.loadString("path/to/$file.json");
final data = await jsonDecode(jsonData)
return data;
}
Also, please don't forget to import dart convert library to use jsonDecode.
import 'dart:convert';

Future builder returns null although my list is not empty

I have this future builder which loads a list of movies in my provider class. Whenever I reload my screen, the movies do not get returned. Below is the future builder
FutureBuilder(
future: movieData.getTrendingMovies(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
return Swiper(
itemBuilder: (BuildContext context, i) {
return ChangeNotifierProvider(
create: (context) => Movie(),
child: MovieContainer(
imageUrl: movieData.movies[i].imageUrl,
id: movieData.movies[i].id,
rate: movieData.movies[i].rate,
title: movieData.movies[i].title,
),
);
},
itemCount: movieData.movies.length,
viewportFraction: 0.25,
scale: 0.4,
);
} else {
return Text(snapshot.error.toString()); // it returns null on the screen
}
}),
Also in my homescreen where I display my movies, after the build method, I create a listener(moviesData) to listen to all changes in the movies provider.
final movieData = Provider.of<Movies>(context, listen: false);
Below is also the methos which fetches the movies from a restfulAPI using http get request
Future<void> getTrendingMovies() async {
List<String> movieTitles = [];
List<String> movieImageUrls = [];
List<String> movieDescriptions = [];
List<String> movieReleaseDates = [];
List<String> movieRates = [];
List<String> movieIds = [];
const _apiKey = '******************************';
const url =
'https://api.themoviedb.org/3/trending/all/week?api_key=$_apiKey';
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode >= 400) {
print(response.statusCode);
return;
}
final extractedData = json.decode(response.body);
List moviesList = extractedData['results'] as List;
List<Movie> loadedMovies = [];
for (int i = 0; i < moviesList.length; i++) {
String movieTitle = moviesList[i]['original_title'] ?? '';
String? movieImage =
'https://image.tmdb.org/t/p/w400${moviesList[i]['poster_path']}'; //results[0].poster_path
String movieDescription =
moviesList[i]['overview'] ?? ''; //results[0].overview
String movieReleaseDate = moviesList[i]['release_date'] ?? '';
String? movieRate = moviesList[i]['vote_average'].toString();
String? movieId = moviesList[i]['id'].toString();
movieTitles.add(movieTitle);
movieImageUrls.add(movieImage);
movieDescriptions.add(movieDescription);
movieReleaseDates.add(movieReleaseDate);
movieRates.add(movieRate);
movieIds.add(movieId);
loadedMovies.add(
Movie(
id: movieIds[i],
title: movieTitles[i],
imageUrl: movieImageUrls[i],
description: movieDescriptions[i],
rate: double.parse(movieRates[i]),
releaseDate: movieReleaseDates[i],
),
);
}
_movies = loadedMovies;
notifyListeners();
//print(_movies.last.title); //This prints the name of the last movie perfectly....This gets called unlimited times whenever I set the listen of the **moviesData** to true
} catch (error) {
print(error);
}
}
There's a couple of things to unpack here.
Instead of a ChangeNotifierProvider, I believe you should use a Consumer widget that listens to your Movies provided service when you call the notifyListeners call, so make it Consumer<Movie>.
You can still call it using the Provider.of above for the sake of making the async call via the FutureBuilder, but I believe because you're not returning anything out of the getTrendingMovies and is just a Future<void> and you're querying the snapshot.hasData, well there is no data coming through the snapshot. Maybe instead you should call snapshot.connectionState == ConnectionState.done as opposed to querying for whether it has data.
Make sure that the response.body is truly returning a JSON value, but I believe your issue is in one of the points above.

How to extract values from onCall firebase function and load them in future builder

i have a onCall cloud function which is returning
resp.status(200).send(JSON.stringify(entities));
In my flutter app, i have created this future to get values from it.
Future<void> dataDriven(String filename) async {
HttpsCallable callable =
FirebaseFunctions.instance.httpsCallable('fruitsType');
final results = await callable;
final datE = results.call(<String, dynamic>{
'filename': 'filename',
});
final dataF = await datE.then((value) => value.data);
print (dataF);
}
It is successfully printing the response which is as per expectation. but my snapshot is always returning null. It is not even reaching hasData stage. Please help.
Response;
[{"name":"banana","type":"fruit","count":0,"color":"yellow"},{{"name":"apple","type":"fruit","count":2,"color":"red"}]
FutureBuilder(
future: dataDriven('fruits.txt'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: Text('An error has occurred!'),
);
} else {
final data = snapshot.data;
return Text(data.toString());
}
It looks like there are some issues that need to be fixed (See comments in code).
// Set the correct return type (not void because you are returning data)
Future<String> dataDriven(String filename) async {
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('fruitsType');
// You can just call the function here with await
final result = await callable.call({
// Remove the quotes on the filename value
'filename': filename,
});
// Don't forget to return the data
return result;
}
I suggest reading up on the documentation about calling cloud functions from a flutter app and basic dart syntax.

How to get inside data in Future<Map<dynamic, dynamic>>?

Future<Map> returnUserMap() async {
final FirebaseUser currentUser = await _auth.currentUser();
Map userMap = {
"UserName": currentUser.displayName,
"UserEmail": currentUser.email,
"UserUrl": currentUser.photoUrl
};
print("1");
print(userMap);
return userMap;
}
return value type is Instance of 'Future>'.
I want to get a UserName, how can I do it?
Your function returnUserMap() returns a Future<Map>. I suspect that the error you describe is not in the code snippet you copied.
Whenever the task to be performed may take some time, you will receive a future. You can wait for futures in an async function with await.
It is therefore recommended to use a so-called FutureBuilder in your build() function:
FutureBuilder<FirebaseUser>(
future: _auth.currentUser(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
final FirebaseUser user = snapshot.data;
if (user.displayName == null || user.displayName.isEmpty())
return text(currentUser.email); // display email if name isn't set
return text(currentUser.displayName);
}
if (snapshot.hasError) {
return text(snapshot.error);
}
return text('loading...');
},
),
If you want to have the displayName outside your build() function, the following code should do the job when you are inside of an async function:
final FirebaseUser user = await _auth.currentUser();
final String displayName = user.displayName;
print('the displayName of the current user is: $displayName');
And this code when you are in a normal function:
_auth.currentUser().then((FirebaseUser user) {
String displayName = user.displayName;
print('displayName: $displayName');
}).catchError((error) {
print('error: ' + error.toString());
});
I think it's worth watching the following video for further understanding:
Async/Await - Flutter in Focus

How can i assign the current state user id to database reference and then database reference to future builder?

From the below code i cant access the userid of current state user without await.If i need to use await like below i need to enclose the code in a method so that there will be async. But, as the code is enclosed in a method i cant access the userid variable in databaseReference.
Can you help:
somemethod() async{
FirebaseUser userid = await FirebaseAuth.instance.currentUser();
}
final **databaseReference** = FirebaseDatabase.instance.reference().child("UserProfile").child(userid.uid).("Favorites");
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: **databaseReference**.once(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
if (snapshot.hasData) {
List<Map<dynamic, dynamic>> list = [];
for (String key in snapshot.data.value.keys) {
list.add(snapshot.data.value[key]);
}
From this code I want userid enclosed in method to be accessed in databaseReference so that I can use databaseReference in FutureBuilder to retrieve the user information.
Thank You.
If you want to have only 1 future builder, you need to pass function paramether on databaseReference.once() method
class yourDatabase {
Future<dynamic> once(Function somemethod) async{
//Now, you are caling the funcion on yourDatabase class
dynamic returnOfSomethod = await somemethod();
//TODO your somemethod here
}
}
and to call you make this
somemethod() async{
FirebaseUser userid = await FirebaseAuth.instance.currentUser();
}
//Inside the build...
FutureBuilder(
future: **databaseReference**.once(somemethod), // ! Dont place ()
builder: () {}
)
hope it works!
FirebaseUser userId;
Future databaseReference;
somemethod() async{
userid = await FirebaseAuth.instance.currentUser();
databaseReference = FirebaseDatabase.instance.reference().child("UserProfile").child(userid.uid).("Favorites"); }
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: **databaseReference**.once(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
if (snapshot.hasData) {
List<Map<dynamic, dynamic>> list = [];
for (String key in snapshot.data.value.keys) {
list.add(snapshot.data.value[key]);
}