I want to create multiple filter list with dropdown in flutter/dart - flutter

I'm a new flutter developer.
So I want to create multiple filter lists with dropdowns...
This filter has 3 dropdown widgets, the expected result of this is that the search results can be combined with each other.
I'm kinda confused about how to start doing it, can you give me advice/reference/link related to this issue.
So far I just can do a single search (i got it from the search delegate)
Future<List<ModelKost>> getFilter({String? query}) async {
final prefs = await SharedPreferences.getInstance();
const key = 'token';
final value = prefs.get(key) ?? 0;
try {
var response = await http.get(Uri.parse('$serverUrl/home'), headers: {
'Accept': 'application/json',
'Authorization': 'Bearer $value'
});
if (response.statusCode == 200) {
data = json.decode(response.body)['data'];
results = data.map((e) => ModelKost.fromJson(e)).toList();
if (query != null) {
results = results
.where((e) =>
e.kabkot.toLowerCase().contains((query.toLowerCase())))
.toList();
}
} else {
debugPrint('fetch data error');
}
} on Exception catch (e) {
debugPrint('error: $e');
}
sortHarga = results;
return results;
}
How to implement it with multiple filters and with dropdown? thank you!

What you want to do is after the api call, store the result inside a list (for example allKosts). Then provide a getter to get the list, with the filter. Whenever you change a filter, you want to call setState and the getter's value will be updated automatically.
List<ModelKost> allKosts = [];
String kabkotFilter = '';
String tipeKotFilter = '';
List<ModelKost> get filteredKosts =>
results.where((e) => e.kabkot.toLowerCase().contains((kabkotFilter.toLowerCase())) && e.tipeKot.toLowerCase() == tipeKotFilter.toLowerCase())).toList();

Related

Replace "map" method to traditional loops in dart when fetching data from API

I was wondering if there is a different approach more efficient to include data from a json API to a simple list.
As I read in some posts, map method is the most time/resource consuming in comparation with the traditional for/while loop in Dart.
Currently I use this snippet to fetch my data:
Future<List<dynamic>> fetchData(url) async {
var client = http.Client();
final response = await client.get(Uri.parse(url));
await Future.delayed(Duration(seconds:2));
if (response.statusCode == 200) {
var jsonDecoded = json.decode(response.body);
BreedList = jsonDecoded.map((data) => DogClass.fromJson(data)).toList();
glossarList = BreedList;
return BreedList;
} else {
throw Exception('Failed to load data');
}
}
I tried this approach:
Future<List<dynamic>> fetchDataFor(url) async {
var client = http.Client();
final response = await client.get(Uri.parse(url));
await Future.delayed(Duration(seconds:2));
if (response.statusCode == 200) {
var jsonDecoded = json.decode(response.body);
for (var k in jsonDecoded.keys){
BreedList.add({jsonDecoded[k]});
}
return BreedList;
} else {
throw Exception('Failed to load data');
}
}
But it returns the error: Class List has no instance getter 'keys'.
So, what would be the equivalent for the "map" method ?
You can use collection-for to perform a straightforward transformation of .map calls.
var result = iterable.map((element) => transform(element)).toList();
can be replaced with:
var result = [for (var element in iterable) transform(element)];
So in your case:
BreedList = jsonDecoded.map((data) => DogClass.fromJson(data)).toList();
can become:
BreedList = [for (var data in jsonDecoded) DogClass.fromJson(data)];

How to await a Map.forEach() in dart

I have a Future which returns a map. I then need to use the values of that map to await another future and then return the entire result at the end. The problem is that dart can't await async Map.forEach() methods (see this: https://stackoverflow.com/a/42467822/15782390).
Here is my code:
the debug console shows that the items printed are in the following order:
flutter: getting journal entries
flutter: about to loop through pictures
flutter: getting picture
flutter: returning entries
flutter: [[....]] (Uint8List)
Future<List<JournalEntryData>> getJournalEntries() async {
List<JournalEntryData> entries = [];
print('getting journal entries');
EncryptService encryptService = EncryptService(uid);
await journal.get().then((document) {
Map data = (document.data() as Map);
print('about to loop through pictures');
data.forEach((key, value) async {
print('getting picture');
dynamic pictures = await StorageService(uid).getPictures(key);
print('done getting image');
entries.add(JournalEntryData(
date: key,
entryText: encryptService.decrypt(value['entryText']),
feeling: value['feeling'],
pictures: pictures,
));
});
});
print('returning entries');
return entries;
}
Future getPictures(String entryID) async {
try {
final ref = storage.ref(uid).child(entryID);
List<Uint8List> pictures = [];
await ref.listAll().then((result) async {
for (var picReference in result.items) {
Uint8List? pic = await ref.child(picReference.name).getData();
if (pic == null) {
// TODO make no picture found picture
var url = Uri.parse(
'https://www.salonlfc.com/wp-content/uploads/2018/01/image-not-found-scaled-1150x647.png');
var response = await http.get(url);
pic = response.bodyBytes;
}
pictures.add(pic);
}
});
return pictures;
} catch (e) {
print(e.toString());
return e;
}
}
It's quite annoying to have to use for-loops when you need async behaviour, specially on Maps, because as the other answer shows, that requires you to iterate over entries and then take the key and value out of it like this:
for (final mapEntry in data.entries) {
final key = mapEntry.key;
final value = mapEntry.value;
...
}
Instead of that, you can write a utility extension that does the work for you:
extension AsyncMap<K, V> on Map<K, V> {
Future<void> forEachAsync(FutureOr<void> Function(K, V) fun) async {
for (var value in entries) {
final k = value.key;
final v = value.value;
await fun(k, v);
}
}
}
Then, you can use that like this:
await data.forEachAsync((key, value) async {
...
});
Much better.
Don't mix the use of then and await since it get rather confusing and things are no longer being executed as you think.
Also, the use of forEach method should really not be used for complicated logic like what you are doing. Instead, use the for-each loop. I have tried rewrite getJournalEntries here:
Future<List<JournalEntryData>> getJournalEntries() async {
List<JournalEntryData> entries = [];
print('getting journal entries');
EncryptService encryptService = EncryptService(uid);
final document = await journal.get();
Map data = (document.data() as Map);
print('about to loop through pictures');
for (final mapEntry in data.entries) {
final key = mapEntry.key;
final value = mapEntry.value;
print('getting picture');
dynamic pictures = await StorageService(uid).getPictures(key);
print('done getting image');
entries.add(JournalEntryData(
date: key,
entryText: encryptService.decrypt(value['entryText']),
feeling: value['feeling'],
pictures: pictures,
));
}
print('returning entries');
return entries;
}
And getPictures here. I have only removed the use of then here.
Future getPictures(String entryID) async {
try {
final ref = storage.ref(uid).child(entryID);
List<Uint8List> pictures = [];
final result = await ref.listAll();
for (var picReference in result.items) {
Uint8List? pic = await ref.child(picReference.name).getData();
if (pic == null) {
// TODO make no picture found picture
var url = Uri.parse(
'https://www.salonlfc.com/wp-content/uploads/2018/01/image-not-found-scaled-1150x647.png');
var response = await http.get(url);
pic = response.bodyBytes;
}
pictures.add(pic);
}
return pictures;
} catch (e) {
print(e.toString());
return e;
}
}

Unable to use a Future value - Flutter/Dart

I've fetched a json object and deserialized it and then returned it too.
I want to use this in another file.
I'm unable to assign the values that I'm getting in the first step.
Here are all the codes...
Service
Future getGeoPoints(String accessToken, String tripId) async {
String requestUrl;
var response = await get(
Uri.parse(requestUrl),
headers: {
'Authorization': "Bearer $accessToken",
},
);
if (response.statusCode == 200) {
Map<String, dynamic> responseBody = json.decode(response.body);
GetGeoPoints geoPoints = GetGeoPoints.fromJson(responseBody);
List listOfGeoPoints = [];
for (var geoPoint in geoPoints.geoPoints) {
listOfGeoPoints.add(
{
'latitude': geoPoint.latitude,
'longitude': geoPoint.longitude,
'timestamp': geoPoint.timeStamp,
},
);
}
// print('List of geo points: ' + '$listOfGeoPoints');
return listOfGeoPoints;
} else {
throw Exception('Failed to load data from server');
}
}
File where I need the above values
List routeCoordinates;
Future<void> getValues() async {
getGeoPoints(widget.accessToken, widget.tripId)
.then((value) => routeCoordinates = value);
}
When I run the app, routeCoordinates is null but when I hotreload, it contains the value.
I want to have the values as soon as the screen starts. What is the right way to assign the values here?
I've also tried this:
routeCoordinates = getGeoPoints...
It throws error..
Please help.. Thanks..
The function getGeoPoints() is an asynchronous one. But on the other file, you are not using the await keyword, instead you are using then(). So your code is not waiting for that function to return value.
Try using below code,
List routeCoordinates;
Future<void> getValues() async {
routeCoordinates = await getGeoPoints(widget.accessToken, widget.tripId);
}
Let us know how it went.
You need to use a FutureBuilder to define a behaviour depending on the state of the request. You'll be able to tell the widget what to return while your app is waiting for the response to your request. You can also return a specific widget if you get an error(if your user is offline, for example).
Edit: I've linked the official docs but give this article a read if it's not clear enough.

Flutter : How to add more json data to existing Model Class?

I have a scenario where the following function is called again and again whenever the user hits the "Load More" button.
The problem I'm facing is, that it replaces previously loaded data with a new one. Instead, it should add to the bottom of the Listview.Builder
Future fetchData() async{
var url = "url_goes_here";
final response = await http.get(url);
if (response.statusCode == 200) {
var resBody = jsonDecode(response.body);
var data = resBody['data'] as List;
if (data.isNotEmpty) {
setState(() {
listVariable = data
.map<ModelClass>((json) => ModelClass.fromJson(json))
.toList();
});
}
}
}
List<ModelClass> listVariable =List<ModelClass>(); //describe the object that way.
--------and---------
data.map<ModelClass>((json) {
listVariable.add(ModelClass.fromJson(jsonn));
} )).toList();
You should add received data to your listVariable, not assign a new value. Try this code:
final listVariable = <ModelClass>[];
...
Future fetchData() async {
var url = "url_goes_here";
final response = await http.get(url);
if (response.statusCode == 200) {
var resBody = jsonDecode(response.body);
var data = resBody['data'] as List;
if (data.isNotEmpty) {
final list = data.map<ModelClass>((json) => ModelClass.fromJson(json));
setState(() {
listVariable.addAll(list); // HERE: addAll() instead of assignment
});
}
}
}
I was able to figure out answer myself.
setState(() {
listVariable.addAll(data
.map<ModelClass>((json) => ModelClass.fromJson(json))
.toList();
}));
#Mol0ko and #hasan karaman both are right but #Mol0ko
Makes better sense when you have a set of data to addAll to existing data.

Flutter - Give Index for dynamic http.post json response

I already have a button to fetch the API with function to ++increment index and set the new parameter on every click. My question is, how to set 'like a cache' for json response as index?
here my http.post request =
List<dynamic> _myResponse = [];
Future<void> trytoFetch(myIndex, parameter) async {
var url =
"https://jsonplaceholder.typicode.com/posts/$parameter";
Map<String, String> headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
final response = await http.post(url, headers: headers);
final responseJson = json.decode(response.body);
if (response.statusCode == 200) {
setState(() {
_myResponse[myIndex] = responseJson; // ITS DOESNT WORKS
});
} else {
setState(() {});
throw Exception('Failed to load internet');
}
}
My goal is like
if (response.statusCode == 200) {
setState(() {
_myResponse[0] = responseJson; // return responseJson from parameter
// Then I click the button with new parameter value and increment index
_myResponse[1] = responseJson; // return responseJson from new parameter
// Then I click the button with another new parameter value and increment index
_myResponse[2] = responseJson; // return responseJson from new parameter again
});
} else {
setState(() {});
throw Exception('Failed to load internet');
}
and in the end, I can simply print the returned json
print(_myResponse[0]);
print(_myResponse[1]);
print(_myResponse[2]);
How to achieve this? is it possible? Thanks
First of, you shouldn't pass index as a parameter to your method.
responseJson variable is a Map, you should convert that map to the object you need.
I suggest taking a look at this.