I try to use compute in Flutter. Here I try to pass multiple parameters inside a Map. But the code in my function myFunction does not work. I get no errors or something else. My code seems to be ignored. Do you find an error here?
Compute function:
Map map = Map();
map['resultList'] = resultList;
map['_getImageFileFromAssets'] = _getImageFileFromAssets;
map["picturesData"] = picturesData;
map["albumID"] = albumID;
await compute(myFunction, map);
Calls the following function:
Future<bool> myFunction(map) async {
var resultList = map["resultList"];
var _getImageFileFromAssets = map["_getImageFileFromAssets"];
var picturesData = map["picturesData"];
var albumID = map["albumID"];
print("Starten");
for (var i = 0; i < resultList.length; i++) {
print(i);
File imageFile = await _getImageFileFromAssets(resultList[i]);
final appDir = await syspath.getApplicationDocumentsDirectory();
final fileName = path.basename(imageFile.path);
final savedImage =
await File(imageFile.path).copy('${appDir.path}/$fileName');
// Creating thumbnails
final thumb = image.decodeImage(await File(savedImage.path).readAsBytes());
final thumbImage = image.copyResize(thumb, width: 500);
new File('${appDir.path}/$fileName-thumb-500.jpg')
.writeAsBytes(image.encodeJpg(thumbImage));
final finalThumbImage = File('${appDir.path}/$fileName-thumb-500.jpg');
picturesData.add(Picture(
album: albumID,
path: savedImage.path,
thumbPath: finalThumbImage.path,
timestamp: Timestamp.now()));
}
return true;
}
Ok, some code - I put this in dartpad.dev:
import 'package:flutter/foundation.dart';
void main() {
Map map = Map();
compute(myFunction, map).then((result) => print(result));
}
Future<bool> myFunction(Map map) async {
print("Starten");
// fake long process
await Future.delayed(Duration(seconds: 5));
return true;
}
and get this as a console result:
Starten
true
Also: is there a reason you need the "map" parameter in your function to be dynamic? If not, I'd declare it as type Map (like I did now).
Related
My goal is to do a simple BitcoinApp. I am trying to get a method that is in the MyHomePageState class to call a method that I have in another class. When I compile and click on the button to give me the bitcoin info of USD I get the error of setState() callback argument returned to Future. Any advice or alternative that you can suggest me? I'm new to Flutter and adjusting.Here is my code:
///This piece of code is located in MyHomePageState Class
BitcoinCurrency _bitcoin = BitcoinCurrency();
void _getUSDBitcoin(){
setState(() async{
_bitcoin.usdBitcoin();
});
}
///This is the class I have outside of MyHomePageState Class.
class BitcoinCurrency {
///Variables we want for the information
String _disclaimer = "N/A";
String _time = "N/A";
String _currencyBitcoin = "N/A";
///Getters for our variables
get disclaimer => _disclaimer;
get time => _time;
get currencyBitcoin => _currencyBitcoin;
///Methods()
void usdBitcoin() async{
var url = Uri.https('api.coindesk.com', '/v1/bpi/currentprice.json');
var response = await http.get(url);
var httpBody = response.body;
var decoded = json.decode(httpBody);
_disclaimer = decoded['disclaimer'];
_time = decoded['time']['updated'];
_currencyBitcoin = decoded['bpi']['USD']['rate'];
}
}
You can convert usdBitcoin void method to Future<void>
Future<void>? usdBitcoin() async{
var url = Uri.https('api.coindesk.com', '/v1/bpi/currentprice.json');
var response = await http.get(url);
var httpBody = response.body;
var decoded = json.decode(httpBody);
_disclaimer = decoded['disclaimer'];
_time = decoded['time']['updated'];
_currencyBitcoin = decoded['bpi']['USD']['rate'];
}
And call setState like
usdBitcoin().then((value) => setState(() {}));
setState can't be an async function. usdBitcoin has to be a Future method, so you have to call it before the setState starts.
usdBitcoin method:
Future usdBitcoin() async{
var url = Uri.https('api.coindesk.com', '/v1/bpi/currentprice.json');
var response = await http.get(url);
var httpBody = response.body;
var decoded = json.decode(httpBody);
_disclaimer = decoded['disclaimer'];
_time = decoded['time']['updated'];
_currencyBitcoin = decoded['bpi']['USD']['rate'];
}
In initState:
usdBitcoin().then(
(value) => setState(
() {
},
),
)
Hello all at first I want to mention that I've tried a lot of solutions here but it didn't work for me.
I bring the list from the database through the following code:
var listCat = [];
Future getdata() async {
apiURL = '***************.php';
var response = await http.post(Uri.parse(apiURL));
var responsebody = jsonDecode(response.body);
if (responsebody.length >0){
for (int i = 0; i < responsebody.length; i++) {
listCat.add(responsebody[i]['name']+ ':' + responsebody[i]['image'].toString());
}
return responsebody;
}else{
}
}
As is obvious in the code above I am trying to get the name and image and this is not a problem right now I want to store this listCat in SharedPreferences until I recall it from all pages of the app
I have the following class to save SharedPreferences:
class APIPreferences {
static SharedPreferences ? _preferences;
static const _keyMuinCat = 'MuinCat';
static Future init() async => _preferences = await SharedPreferences.getInstance();
static Future setMuinCat(String MuinCat) async => await _preferences!.setString(_keyMuinCat, MuinCat);
static String? getMuinCat() => _preferences!.getString(_keyMuinCat);
}
Then I save what I need to save by the following line:
APIPreferences.setMuinCat(listCat.toString());
Then I can bring pre-stored data from any location where I need it through the following code:
CatList = APIPreferences.getMuinCat() ?? '';
I tried to do the following thing now to save the list in the first code above:
var listCat = [];
Future getdata() async {
apiURL = '***************.php';
var response = await http.post(Uri.parse(apiURL));
var responsebody = jsonDecode(response.body);
if (responsebody.length >0){
for (int i = 0; i < responsebody.length; i++) {
listCat.add(responsebody[i]['name']+ ':' + responsebody[i]['image'].toString());
APIPreferences.setMuinCat(listCat.toString());
}
return responsebody;
}else{
}
}
But it didn't work. I don't really know how to deal with it.
How can I save it and then bring it to use with ListView.
instead of:
_preferences!.setString(_keyMuinCat, "some string");
use:
_preferences!.setStringList(_keyMuinCat, ["some", "strings", "in", "list"]);
So in your code, the setMuinCat method needs to be:
static Future setMuinCat(List<String> muinCat) async => await _preferences!.setStringList(_keyMuinCat, muinCat);
and then you call it like this:
APIPreferences.setMuinCat((listCat as List).map((v) => v.toString()).toList());
To save the list in shared preferences you need to pass as jsonEncode(yourList data) and when you will fecth the shared list you will again jsonDecode(your list)
await prefs.setString('YOUR KEY', json.encode(YOURMAP()));
This is an example code, here i find the row by key. I need something like this, but that delete a row if i know the key. (I don't know the index)
import 'package:gsheets/gsheets.dart';
...
Future deleteUser() async {
final gsheets = GSheets(_credentials);
final ss = await gsheets.spreadsheet(_spreadsheetId);
var sheet = ss.worksheetByTitle('game');
row = await sheet.values.rowByKey("key");
}
Thank you!
I resolved with this:
void deleteUser() async {
final gsheets = GSheets(_credentials);
final ss = await gsheets.spreadsheet(_spreadsheetId);
var sheet = ss.worksheetByTitle('game');
final lista = await sheet.values.allRows();
for(var i=0; i<lista.length; i++){
var riga=lista[i];
if (riga[0] == dati[0]){ //dati[0] contain the key
sheet.deleteRow(i+1);
break;
}
}
}
I would compress a list of image file before uploading, but i have some doubts to convert type after using map a list of file, it return type List<Set<Future>>, not type List as I expect. How can I convert/cast to List of File in this situation, the compressImage function worked well with only one file. Thank you so much.
import 'package:image/image.dart' as Im;
import 'package:uuid/uuid.dart';
List<File> compressImageList(List<File> tempfileList) async {
List<Future<File>> compressedFileList =
tempfileList.map((file) => {compressImage(file)}).toList();
return compressedFileList ;
}
Future<File> compressImage(File tempFile) async {
String postId = Uuid().v4();
final tempDir = await getTemporaryDirectory();
final path = tempDir.path;
Im.Image? imageFile = Im.decodeImage(tempFile.readAsBytesSync());
final compressedImageFile = File('$path/img_$postId.jpg')
..writeAsBytesSync(Im.encodeJpg(imageFile!, quality: 85));
tempFile = compressedImageFile;
return tempFile;
}
i edit my function as below and it works , thank you.
Future<List<File>> compressImageList(List<File> tempfileList) async {
List<File> compressedFileList = [];
await Future.wait(tempfileList.map((file) async {
var compressImage2 = await compressImage(file);
compressedFileList.add(compressImage2);
}).toList());
return compressedFileList;
}
Not sure, haven't tested it, but try removing the {} on the 6th line (as shown above). These brackets signify that it is a type set, which is being returned by =>. If using => for a single statement you don't need to use brackets.
Do it like this;
List<File> compressImageList(List<File> tempfileList) {
List<File> compressedFiles = [];
tempfileList.forEach((file) async {
final compressedFile = await compressImage(file);
compressedFiles = [...compressedFiles, compressedFile];
});
return compressedFiles;
}
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;
}
}