Flutter await does not await until return of function - flutter

I tried to find an answer but my problem is still there.
In my asynchronous upload function I return at the and the generated name of the image, which I want to use to make my database request.
This is my upload function:
Future<String> upload(File imageFile) async {
var stream =
new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
// get file length
var length = await imageFile.length();
var uri = Uri.parse("http://localhost:8080/upload");
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile('file', stream, length,
filename: basename(imageFile.path));
request.files.add(multipartFile);
var response = await request.send();
print(response.statusCode);
var createdFileName = "";
response.stream.transform(utf8.decoder).listen((value) {
createdFileName = value;
print(createdFileName);
});
return createdFileName;
}
I call it like this:
List createdFileNames = [];
for (var e in imagefiles) {
createdFileNames.add(await upload(File(e)));
}
I don't know why, but the createdFileNames are ["",""], but the upload gives as result the right name. In debug mode I can see, that the loop does not wait until the upload has finished.
Do you have any suggestions?
Thank you very much!

response.stream.transform(utf8.decoder).listen((value) {
createdFileName = value;
print(createdFileName);
});
This part in your function is asynchronous, it uses a callback.
But you don't wait for it to finish in any form. You just continue to return the createdFileName, that by that time most likely has not been filled.
I don't know what your stream looks like, if you only need the first value, you could await that instead of listening:
createdFileName = await response.stream.transform(utf8.decoder).first;

Replace
response.stream.transform(utf8.decoder).listen((value) {
createdFileName = value;
print(createdFileName);
});
with
createdFileName=await response.stream.bytesToString();

change code
for (var e in imagefiles) {
upload(File(e)).then((value) => createdFileNames.add(value));
}

Related

Flutter : get the original link of shorten url

I have to fetch some url link, but the link is shorten like "shorturl.at/cpqCE".
I want to get the original link url in flutter.
How can I do this?
in Phph I found this :
function expandShortUrl($url) {
$headers = get_headers($url, 1);
return $headers['Location'];
}
// will echo https://deluxeblogtips.com
echo expandShortUrl($url);
Thanks to #RandalSchwartz I solved it by using :
getUrl(url) async {
final client = HttpClient();
var uri = Uri.parse(url);
var request = await client.getUrl(uri);
request.followRedirects = false;
var response = await request.close();
while (response.isRedirect) {
response.drain();
final location = response.headers.value(HttpHeaders.locationHeader);
if (location != null) {
uri = uri.resolve(location);
request = await client.getUrl(uri);
// Set the body or headers as desired.
if (location.toString().contains('https://www.xxxxx.com')) {
return location.toString();
}
request.followRedirects = false;
response = await request.close();
}
}
}
you can also do it by just sending a normal post request with the short link and you will then get the response which includes the original/full URL.
e.g
var uri = Uri.parse(shortUrl);
try {
var response = await http.post(
uri,
headers: {"Accept": 'application/json'},
);
// with below code you get the full path of a short url
String fullPath = response.headers.entries.firstWhere((element) => element.key == "location").value;
print(fullPath);
} catch (e) {
print(e);
}
hope this can help your problem.

I'm using flutter to update user in Strapi. While running the following code, I am only able to change other fields details but not image. Thank you

I am trying to do so using Multipartfile class. My field name in the strapi is profile_image. Other details are updated, but neither image is uploaded in the media library, nor image is updated for this particular record.
try {
var url = "${kAPIURL}users/${bodyData["id"]}";
var imageBytes = await image!.readAsBytes();
int length = imageBytes.length;
http.ByteStream byteStream = http.ByteStream(image.openRead());
Stream<List<int>> stream = byteStream.cast();
var request = http.MultipartRequest('PUT', Uri.parse(url));
request.headers['Authorization'] = 'Bearer ${bodyData["jwt"]}';
request.fields['id'] = bodyData['id'];
request.fields['username'] = bodyData['username'];
request.fields['specific_type'] = bodyData['specific_type'];
request.fields['sex'] = bodyData['sex'];
request.fields['phone_number'] = bodyData['phone_number'];
request.fields['email'] = bodyData['email'];
var profileImage = http.MultipartFile(
'files.profile_image', stream, length,
filename: image.path.split('/').last.toString(),
contentType: MediaType('image', 'jpg'));
request.files.add(profileImage);
var response = await request.send();
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
if (response.statusCode == 200) {
Provider.of<MainScreenProvider>(context, listen: false).isDeleteCache =
true;
return response;
} else {
return response;
}
} on Exception {
throw Exception("Unable to update user details.");
}

Flutter/Dio Hangs when in an isolate

So I'm trying to sync my database in an isolate, since UI doesn't care about the sync.
Database _backgroundConnection(File dbFile) {
final database = VmDatabase(dbFile);
var connection = DatabaseConnection.fromExecutor(database);
return Database.connect(connection);
}
Dio _getDio(String urlBase, String authToken) {
var dio = Dio();
dio.options.baseUrl = urlBase;
dio.options.headers = {"Authorization": authToken};
dio.options.connectTimeout = 5000; //5s
dio.options.receiveTimeout = 3000;
return dio;
}
Future<void> _syncUser(UserDao dao, DateTime lastSynced, Dio dio) async {
var users = await dao.getUser();
if (users.isEmpty) {
return;
}
var user = users[0];
var userDto = UserDto.fromUser(user);
//if (user.lastUpdated.isAfter(lastSynced)) {
var result = await dio.put("/api/v1/user", data: userDto.toJson());
print(result);
//}
}
Future<void> _startSync(Map<String, dynamic> info) async {
String dbPath = info["dbPath"];
DateTime lastSynced = info["lastSynced"];
String urlBase = info["url"];
String token = info["token"];
var dio = _getDio(urlBase, token);
File dbFile = File(dbPath);
var database = _backgroundConnection(dbFile);
_syncUser(UserDao.fromDatabase(database), lastSynced, dio);
}
class DatabaseSync {
Future<void> syncDatabase() async {
final dbFolder = await getApplicationDocumentsDirectory();
var envVars = GetIt.instance.get<EnvVariables>();
var authRepo = GetIt.instance.get<FirebaseAuthRepository>();
compute(_startSync, {
"lastSynced": DateTime.now(),
"dbPath": p.join(dbFolder.path, 'db.sqlite'),
"url": envVars.url,
"token": await authRepo.getAuthToken(),
});
}
}
I've removed the compute portion, and just ran the code, and it works. It makes the request. However when I put a breakpoint at just before the request, it hits, but when I put it on the print statement, it never hits. Nothing is ever written to the console. This happens on both iOS and android. I tried changing the urlBase from my localhost server to https://google.com to make sure it would hit something https (since iOS gets picky) and still nothing.
So this is kinda strange but calling await _syncUser(...) fixed the problem.

Flutter - Best way to request Multiple APIs simultaneously

I have two URLs, and I am using the fetchData() function to parse the json.
Future<Iterable> fetchData() async {
var response = await http.get(firstUrl);
var listOne = List<Article>();
if (response.statusCode == 200) {
var notesJson = json.decode(response.body);
var bodyList = notesJson['items'];
for (var i in bodyList) {
listOne.add(Article.fromJson(i));
}
}
var resp = await http.get(secondUrl);
var listTwo = List<Article>();
if (resp.statusCode == 200) {
var notesJson = json.decode(resp.body);
var bodyList = notesJson['items'];
for (var i in bodyList) {
listTwo.add(Article.fromJson(i));
}
}
var newL = [...listOne, ...listTwo];
return newL;
}
I find this redundant. I want to know if this is the right approach, or can I optimize it? Since I am querying two URLs, should I be using compute() instead?
Flutter's compute spawns a whole other Isolate (thread-like things in Dart) and that's pretty resource-intensive for just waiting on a network request.
Gladly, Dart is event-loop-based, so you can wait on both requests simultaneously by simply wrapping both network request Futures in a call to Future.wait.
For more information about Dart's event loop, you might want to check out the explanatory video about Futures on Flutter's YouTube channel.
Future<List<Article>> fetchData() async {
var responses = await Future.wait([
http.get(firstUrl),
http.get(secondUrl),
]);
return <Article>[
..._getArticlesFromResponse(responses[0]),
..._getArticlesFromResponse(responses[1]),
];
}
List<Article> _getArticlesFromResponse(http.Response response) {
return [
if (response.statusCode == 200)
for (var i in json.decode(response.body)['items'])
Article.fromJson(i),
];
}
if you have dynamic list you can use Future.forEach method;
for example:
var list = ["https://first.api.url",
"https://second.api.url",
"https://third.api.url"];
void makeMultipleRequests(){
await Future.forEach(list, (url) async{
await fetchData(url);
});
}
Future<Iterable> fetchData(String url) async {
var response = await http.get(url);
print(response.body);
}
You can use Dio package.
response = await Future.wait([dio.post('/info'), dio.get('/token')]);

how to upload image to rest API in flutter through http post method?

I'm trying to upload an image through the flutter via post method. and I'm using image_picker for pick file from mobile but I can't able to upload
and I have tried to send the file like FormData that also doesn't work
Future<dynamic> uploadLicence(int id ,dynamic obj) async {
FormData formdata = new FormData(); // just like JS
formdata.add("image",obj);
final response = await post('Logistic/driver/LicenceImage?
driverId=$id',
formdata);
print(response);
// return null;
if (response.statusCode == 200) {
final result = json.decode(response.body);
return result;
} else {
return null;
}
}
after that, I just tried with this method but this also not working
Future<dynamic> uploadLicence(int id, File file) async {
final url = Uri.parse('$BASE_URL/Logistic/driver/LicenceImage?
driverId=$id');
final fileName = path.basename(file.path);
final bytes = await compute(compress, file.readAsBytesSync());
var request = http.MultipartRequest('POST', url)
..files.add(new http.MultipartFile.fromBytes(
'image',bytes,filename: fileName,);
var response = await request.send();
var decoded = await
response.stream.bytesToString().then(json.decode);
if (response.statusCode == HttpStatus.OK) {
print("image uploded $decoded");
} else {
print("image uplod failed ");
}
}
List<int> compress(List<int> bytes) {
var image = img.decodeImage(bytes);
var resize = img.copyResize(image);
return img.encodePng(resize, level: 1);
}
It's possible with MultipartRequest. Or you can use simply dio package. It's one command.
With http:
import 'package:http/http.dart' as http;
final Uri uri = Uri.parse(url);
final http.MultipartRequest request = http.MultipartRequest("POST", uri);
// Additional key-values here
request.fields['sample'] = variable;
// Adding the file, field is the key for file and file is the value
request.files.add(http.MultipartFile.fromBytes(
field, await file.readAsBytes(), filename: filename);
// progress track of uploading process
final http.StreamedResponse response = await request.send();
print('statusCode => ${response.statusCode}');
// checking response data
Map<String, dynamic> data;
await for (String s in response.stream.transform(utf8.decoder)) {
data = jsonDecode(s);
print('data: $data');
}
I user this code for my project i hope work for you
Upload(File imageFile) async {
var stream = new http.ByteStream(DelegatingStream.typed(imageFile.openRead()));
var length = await imageFile.length();
var uri = Uri.parse(uploadURL);
var request = new http.MultipartRequest("POST", uri);
var multipartFile = new http.MultipartFile('file', stream, length,
filename: basename(imageFile.path));
//contentType: new MediaType('image', 'png'));
request.files.add(multipartFile);
var response = await request.send();
print(response.statusCode);
response.stream.transform(utf8.decoder).listen((value) {
print(value);
});
}