Flutter async method slowdown app performance - asynchronous

I'm a newbie in flutter and I can't understand what I might be doing wrong. In my App there is an option for the user to change their avatar's image. He can choose an image from the gallery or take a photo. The new avatar image is saved in the _avatarImage field, and within the setState method the _newImage field is set to true, like this:
Future getNewAvatarImage() async {
Image _image = .... // Take a photo or a image from Gallery
// ...
_avatarImage = _image;
setState(
() => _newImage = true;
);
}
In one part of the code I have the compressAndUpload method which when called compresses an image and sends it to the remote server. This method is asynchronous and is called within the build method whenever the _newImage field is true. Like this
#override
Widget build(BuildContext context) {
if (_newImage) {
_newImage = false;
compressAndUpload(_avatarImage);
}
return Container(
//...
child: _avatarImage,
//
);
The problem is that the new avatar image will take a long time to appear if the compressAndUpload method is called. If this method is commented out the new avatar image comes up quickly.
if (_newImage) {
_newImage = false;
// New image show quickly
// compressAndUpload(_avatarImage);
}
***********
if (_newImage) {
_newImage = false;
// Image takes too long to appear
compressAndUpload(_avatarImage);
}
Where is the problem? The compressAndUpload method is asynchronous and so should not cause delay for the new image to be displayed:
Future<void> compressAndUpload(var image) async {
// Compress image
// upload image
}
UPDATE:
For further clarification I show the complete code of the compressAndUpload method:
Future<void> compressAndUpload(var image) async {
var imageBytes = imagem.readAsBytesSync();
saveImageToPreferences(base64String(imageBytes));
var tempDir = await getTemporaryDirectory();
var path = tempDir.path;
// Reduce size
img.Image image = img.decodeImage(imageBytes);
img.Image smallerImage = img.copyResize(image, width: 1000);
File compressedFileImage =
File('$path\${Explika.getAluno().id}.jpg')
..writeAsBytesSync(img.encodeJpg(smallerImage, quality: 50));
String _urlsegment = Explika.producaoFlag ?
'https://www.remoteserver.pt' : 'http://10.0.2.2';
var stream = http.ByteStream(DelegatingStream.typed(
compressedFileImage.openRead()));
var length = await compressedFileImage.length();
var uri = Uri.parse('$_urlsegment/explika/api/upload');
var request = http.MultipartRequest("POST", uri);
var multipartFile = http.MultipartFile('fotoaluno', stream, length,
filename: '${Explika.getAluno().id}.jpg');
request.files.add(multipartFile);
var response;
try {
response = await request.send();
} catch (e) {
// mostrar falha de rede
//_uploadingImagem = false;
print(e);
return;
}
//Get the response from the server
var responseData = await response.stream.toBytes();
var responseString = String.fromCharCodes(responseData);
print(responseString);
}

Thanks so much for all the comments.
The problem was solved when I found that in the pickImage method it was possible to set the maximum width and the maximum height of the image being selected. Thus, the compression step of the selected image was not required.

Related

Download multiple files (50mb) blazor server-side

i can't really find a way to download a 100mb zip file from the server to the client and also show the progress while downloading. So how will this look for a normal api controller i can add to my server-side project? if lets say i have 3 files i want to download at 50mb each.
i have tried using JSInterop like this, but this is not showing the progress of the file download, and how will i do if i want to download 3 seperate files at the same time?
try
{
//converting file into bytes array
var dataBytes = System.IO.File.ReadAllBytes(file);
await JSRuntime.InvokeVoidAsync(
"downloadFromByteArray",
new
{
ByteArray = dataBytes,
FileName = "download.zip",
ContentType = "application/force-download"
});
}
catch (Exception)
{
//throw;
}
JS:
function downloadFromByteArray(options: {
byteArray: string,
fileName: string,
contentType: string
}): void {
// Convert base64 string to numbers array.
const numArray = atob(options.byteArray).split('').map(c => c.charCodeAt(0));
// Convert numbers array to Uint8Array object.
const uint8Array = new Uint8Array(numArray);
// Wrap it by Blob object.
const blob = new Blob([uint8Array], { type: options.contentType });
// Create "object URL" that is linked to the Blob object.
const url = URL.createObjectURL(blob);
// Invoke download helper function that implemented in
// the earlier section of this article.
downloadFromUrl({ url: url, fileName: options.fileName });
// At last, release unused resources.
URL.revokeObjectURL(url);
}
UPDATE:
if im using this code, it will show me the progress of the file. But how can i trigger it from my code? This way does not do it. But typing the url does.
await Http.GetAsync($"Download/Model/{JobId}");
Controller
[HttpGet("download/model/{JobId}")]
public IActionResult DownloadFile([FromRoute] string JobId)
{
if (JobId == null)
{
return BadRequest();
}
var FolderPath = $"xxxx";
var FileName = $"Model_{JobId}.zip";
var filePath = Path.Combine(environment.WebRootPath, FolderPath, FileName);
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, "application/force-download", FileName);
}
UPDATE 2!
i have got it download with progress and click with using JSInterop.
public async void DownloadFiles()
{
//download all selectedFiles
foreach (var file in selectedFiles)
{
//download these files
await JSRuntime.InvokeAsync<object>("open", $"Download/Model/{JobId}/{file.Name}", "_blank");
}
}
Now the only problem left is.. it only downloads the first file out of 3.

I need to process a DocumentSnapShot single document

I am new to flutter and am writing a new app. In the app I want to retrieve the user data from a firebase document and populate TextFields on the user screen. I can pull the data fine using DocumentSnapShot. I want to use TextEditingController and populate it with the data but I am not able to do it. I know the data is there since I can see it in debug mode. Below are snippets of my code.
Here is where I get the data and populate the TextEditingControllers:
final agentsRef = FirebaseFirestore.instance.collection(('agents'));
getCurrentAgent() async {
final DocumentSnapshot currentAgent =
await agentsRef.doc(globals.currentUid).get();
if (currentAgent == null) {
emailController.text = "";
new Future.delayed(Duration.zero, () {
final agentProvider =
Provider.of<AgentProvider>(context, listen: false);
agentProvider.loadValues(Agents());
});
} else {
// existing record
// Updates Controllers
**emailController.text = currentAgent.data[index].toString(); // THIS LINE IS WHERE I AM HAVING TROUBLE**
// Updates State
new Future.delayed(Duration.zero, () {
final agentProvider =
Provider.of<AgentProvider>(context, listen: false);
agentProvider.loadValues(widget.agents);
});
}
#override
void initState() {
getCurrentAgent();
super.initState();
}
How do I access each data element in the DocumentSnapShot?
You'll typically get the fields by their name and not by their index. So:
**emailController.text = currentAgent.data()["email"].toString();

retrieve image from firebase storage and view the image in the app

im using firebase storage to store a profile image of the users
the upload working good,
when i want to view the image as an imagesource i use this code:
public async Task<string> LoadImages()
{
var stroageImage = await new FirebaseStorage("foodymood-ba7aa.appspot.com")
.Child("Users/" + Settings.LastUserID)
.Child("ProfileImage.jpg")
.GetDownloadUrlAsync();
string imgurl = stroageImage;
return imgurl;
}
it returns the download url:
"https://firebasestorage.googleapis.com/v0/b/foodymood-ba7aa.appspot.com/o/Users%2F0BCAruzb4xP1chAeaLhmnwfTue53%2FProfileImage.jpg?alt=media&token=b69e1de0-1bac-4ceb-ad7d-0c3b1d313a2c"
now i dont know how to use this url for viewing the image..
fixed with this code:
public async Task<ImageSource> LoadImages()
{
var webClient = new WebClient();
var stroageImage = await new FirebaseStorage("foodymood-ba7aa.appspot.com")
.Child("Users/" + Settings.LastUserID)
.Child("ProfileImage.jpg")
.GetDownloadUrlAsync();
string imgurl = stroageImage;
byte[] imgBytes = webClient.DownloadData(imgurl);
//string img = Convert.ToBase64String(imgBytes);
var img = ImageSource.FromStream(() => new MemoryStream(imgBytes));
return img;
}

Problem with getting the random photo from Firebase by usage of async Tasks and async UnityWebRequest

I've created a call to Firebase to get the random photo (since we have categories of photos, first I'm trying to get random category, then random photo from it). After that I want to make async UnityWebRequest to get the photo and add it as a texture. The code gets to the inside of the Task but the call to database is never executed. I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.
I tried the code to get the image separately and it worked just fine. I also tried using delegate and action, but didn't help much. I'm still pretty newbie to C# and Unity, so my code isn't that good. Will appreciate all the feedback.
//Getting the random photo
async Task GetRandomPhoto(){
await photosDbReference.GetValueAsync().ContinueWith(task =>{
List<string> snapshotList = new List<string>();
List<string> snapsnotList2 = new List<string>();
if(task.IsCompleted){
int catNumber = Convert.ToInt32(task.Result.ChildrenCount);
System.Random rnd = new System.Random();
int randCat = rnd.Next(0, catNumber);
foreach (DataSnapshot snapshot in task.Result.Children)
{
snapshotList.Add(snapshot.Key.ToString());
}
photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWith(task2 =>{
if(task2.IsCompleted){
int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
System.Random rnd2 = new System.Random();
int randPhoto = rnd.Next(0, photosNumber);
foreach(DataSnapshot snap2 in task2.Result.Children){
snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
}
string photoLink = snapsnotList2[randPhoto];
}
});
}
});
}
//Trying to set the photo as a texture
public async void PutTheTexture(string url){
Texture2D texture = await GetTexture(url);
myImage.texture = texture;
}
public async Task<Texture2D> GetTexture(string url){
Debug.Log("Started");
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
Debug.Log("Sending request: " + url);
var asyncOp = www.SendWebRequest();
Debug.Log("Request sent");
while( asyncOp.isDone==false )
{
await Task.Delay( 1000/30 );
}
if( www.isNetworkError || www.isHttpError )
{
#if DEBUG
Debug.Log( $"{ www.error }, URL:{ www.url }" );
#endif
return null;
}
else
{
return DownloadHandlerTexture.GetContent( www );
}
}
The code gets to the Debug.Log("Started"); inside the Task but apparently the request is never send.
I can't quite tell how your two blocks of code go together, but what I will point out is that .ContinueWith will not continue in Unity's main thread. My suspicion is that the continuation is kicking off the GetTexture via a mechanism I'm not seeing.
As far as I can tell, async/await should always stay in your current execution context but perhaps the Continuations are causing your logic to execute outside of the Unity main thread.
Since you're using Firebase, this would be super easy to test by replacing ContinueWith with the extension method ContinueWithOnMainThread. If this doesn't help, you can generally swap out async/await logic with continuations on tasks or fairly easily convert the above example to use purely coroutines:
//Getting the random photo
void GetRandomPhoto(){
photosDbReference.GetValueAsync().ContinueWithOnMainThread(task =>
{
List<string> snapshotList = new List<string>();
List<string> snapsnotList2 = new List<string>();
if(task.IsCompleted){
int catNumber = Convert.ToInt32(task.Result.ChildrenCount);
System.Random rnd = new System.Random();
int randCat = rnd.Next(0, catNumber);
foreach (DataSnapshot snapshot in task.Result.Children)
{
snapshotList.Add(snapshot.Key.ToString());
}
photosDbReference.Child(snapshotList[randCat]).GetValueAsync().ContinueWithOnMainThread(task2 =>{
if(task2.IsCompleted){
int photosNumber = Convert.ToInt32(task2.Result.ChildrenCount);
System.Random rnd2 = new System.Random();
int randPhoto = rnd.Next(0, photosNumber);
foreach(DataSnapshot snap2 in task2.Result.Children){
snapsnotList2.Add(snap2.Child("Dblink").Value.ToString());
}
string photoLink = snapsnotList2[randPhoto];
}
});
}
});
}
public delegate void GetTextureComplete(Texture2D texture);
private void Completion(Texture2D texture) {
myImage.texture = texture;
}
//Trying to set the photo as a texture
public void PutTheTexture(string url){
GetTexture(url, Completion);
}
public IEnumerator GetTexture(string url, GetTextureComplete completion){
Debug.Log("Started");
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
Debug.Log("Sending request: " + url);
var asyncOp = www.SendWebRequest();
Debug.Log("Request sent");
yield return asyncOp;
if( www.isNetworkError || www.isHttpError )
{
#if DEBUG
Debug.Log( $"{ www.error }, URL:{ www.url }" );
#endif
completion(null);
}
else
{
completion(DownloadHandlerTexture.GetContent(www));
}
}
(you can do better than my example, and I haven't verified that it runs. Just a quick pass)
Big Thank You to everybody who tried to help! I finally found the way to solve the issue. I changed my async Task to async Task < "Dictionary" > and made it return the dict wilth all the data of the random photo (label, link, user). Then I created async void in which I wrote:
Dictionary photoData = await GetRandomPhoto();
From there it was very easy.

Q:Flutter How to get value from parameter inside class

I'm newbie for android and flutter development, I want to ask something about push notification, I build firebase_messaging by own self because its not support big notification image
The problem is I cant get the _url
int _selectedTab = 1;
static String _url = "";
static const platform =
const MethodChannel('com.example.loop_app_flutter_v2');
#override
Future<void> _firebaseMessageListener() async {
String getRedirect;
String url;
try {
final String result =
await platform.invokeMethod('getFirebaseMessagePayload');
getRedirect = '$result';
Map<String, dynamic> notif = jsonDecode(getRedirect);
url = notif["url"]; //payload success and url show up
} on PlatformException catch (e) {
getRedirect = "Failed : '${e.message}'.";
}
this.loadNotificationData(getRedirect);
setState(() {
_url = url; // i want to assign url to _url and put in List Widget
print(url); //print show up url
});
}
#override
void initState() {
_firebaseMessageListener();
}
final List<Widget> _children = [
new MainArtikel(),
new Webpage(url: _url),
new FeedBack(),
new Setting()
];

Resources