Show upload progress indicator on top using getx - flutter

I am working on video uploading using the flutter_uploader package. So a progress needs to be shown on the top, during video uploading. How to achieve this in flutter using getx package? Or any other alternative methods are available?

Call api fun and as well as show uploading percentage on a custom dialog,
Like this-
() async {
_storiesController.createStory(widget.path);
showDialog(
context: context,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
title: Row(
children:[
CircularProgressIndicator(),
Obx(()=> Text(' Uploading ${(_storiesController.progressBar.value*100).toInt()}%'))
]
),
actions: <Widget>[
FlatButton(
child: Text("SKIP"),
onPressed: () {
Get.back();
Get.back();
},
),
],
),
);
},
);
},
**get percentage from api **
onSendProgress: (int sent, int total) {
final progress = sent / total;
print('progress: $progress ($sent/$total)');
// setState(() {
progressBar.value = sent / total;
test.value = progressBar.value.toString();
print('actual uploading :${progressBar.value}');
// });
},

Related

Returning data from .pop() to use it anywhere

I have written a code that return bool type variables. Whether you like the movie. If you like the movie then it returns true, if you don't like the movie then it returns false. But since the .pop() method works in ElevatedButton, I cannot reach it from another class. How can I reach the value?
ElevatedButton(
child: Text(
"Go to new page"
),
onPressed: () async {
final answer = await Navigator.of(context).push<bool>(
MaterialPageRoute(
builder: (context) {
return VideoScreen("Did you like the video?");
},
)
);
},
),
However, I cannot say like:
ElevatedButton(
child: Text(
"Go to new page"
),
onPressed: () async {
final answer = await Navigator.of(context).push<bool>(
MaterialPageRoute(
builder: (context) {
return VideoScreen("Did you like the video?");
},
)
);
},
),
Text(answer)
],
);
So how can I reach that value? Callback or something? Thanks in advance
Use can pass value to parent route while pop like
Navigator.of(context).pop(YourValue);
While you push make sure to await.
final result = await Navigator.of(context)....;
print(result);

How to render image from Firebase without rebuilding?

I'm trying to implement profile image picking in my flutter app using Firebase Storage. I use image_picker to get the image and upload it to Firebase, get the download link and add the download link to the imgsrc field in the cloud firestore, from where I can render the NetworkImage.
Center(
child: Stack(
children: [
buildImage(),
Positioned(
bottom: 5,
right: 5,
child: GestureDetector(
onTap: showPhotoAlertDialog,
child: buildEditIcon(Color(0xff407bff))),
),
],
),
),
How can I get the default Icons.person kind image for when the user has no profile image, and get the image from the database otherwise?
The code I'm using right now is as follows:
Widget buildImage() {
return CircleAvatar(
backgroundImage: NetworkImage(loggedInUser.imgsrc ??
'https://th.bing.com/th/id/R.945f33b643f2ceffcdae90fb57c61854?rik=XcI0SYBgSefoCA&riu=http%3a%2f%2fgetdrawings.com%2ffree-icon-bw%2fanonymous-avatar-icon-19.png&ehk=5n%2buJG66CeLQZsmhaMt8gag5rXuM3TdebAL6W35K1E4%3d&risl=&pid=ImgRaw&r=0'),
backgroundColor: Colors.grey[350],
radius: 100,
);
}
I created an Alert Dialog widget to choose whether to choose the image from camera or from the gallery.
showPhotoAlertDialog() {
AlertDialog alert = AlertDialog(
title: Text("Upload from"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
imageFromCamera()
.then((value) => uploadFile())
.whenComplete(() => postSource());
setState(() {}); ----->
},
child: Text("Upload from camera"),
),
TextButton(
onPressed: () {
imageFromGallery().then((value) => uploadFile());
postSource();
setState(() {});
},
child: Text("Upload from gallery"),
)
],
),
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
To upload the image to storage and post the source to cloud firestore, I use the following methods:
Future uploadFile() async {
if (file == null) return;
final fileName = path.basename(file!.path);
final destination = 'files/$fileName';
task = FirebaseApi.uploadFile(destination, file!);
setState(() {});
if (task == null) return;
final snapshot = await task!.whenComplete(() {});
urlDownload = await snapshot.ref.getDownloadURL();
print('Download-Link: $urlDownload');
}
postSource() async {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
await firebaseFirestore
.collection("users")
.doc(user?.uid)
.update({'imgsrc': urlDownload});
}
The link gets uploaded properly and I'm able to get the link in my NetworkImage, but it doesn't get rendered immediately. I have to close the parent drawer and open it again to get it. I call setState(){} as well after posting the source, but it doesn't make any difference. How can I get the image without having to close and open the drawer?
Any help would be appreciated!
Thanks
You also have to update image in model class or in this imgsrc also just add this line above setState in onPressed of TextButton.
loggedInUser.imgsrc = urlDownload;

How to wait for value before calling method Flutter/Dart

I am having a pretty hard time with certain actions in flutter. I currently have a method in an outside class that updates a db that my widget relies on for displaying info. I am correctly updating the values in the db and updating the UI correctly. BUT I am having a hard time getting an input first, THEN having that method function. I have tried having it all in the same body and no dice, I have tried to have the addStock method show the input and does not work. The only thing that has been a ban-aid has been to use Navigator.push to the screen again or using a time delayed. Both have produced undesired consequences. I have also tried having the addStock method inside the displayAmountToADD on pressing okay and does not update UI.
//a button inside the UI
onPressed: () async {
displayAmountToAdd(context, index);
setState(() {});
},
....
Future<void> displayAmountToAdd(
BuildContext context,
int index,
) async {
final _textFieldController = TextEditingController();
double materialKG = 0;
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text('Enter amount to add'),
content: Row(
children: <Widget>[
Expanded(
child: TextField(
onChanged: (materialQuanity) {
materialKG = double.parse(materialQuanity);
},
controller: _textFieldController,
decoration: InputDecoration(hintText: "KG"),
),
),
],
),
actions: <Widget>[
TextButton(
child: Text('OK'),
onPressed: () {
materialKG = double.parse(_textFieldController.text);
addStock(context, mapM[index]['quanity'], mapM[index]['name'],
materialKG);
Navigator.pop(context);
},
),
TextButton(
child: Text("Cancel"),
onPressed: () {
Navigator.pop(context);
})
],
);
},
);
//return Future.delayed(Duration(seconds: 4),()=>materialKG); //TRYING TO AVOID THIS
}
//outside the ui file
addStock(
BuildContext context,
double currentQuanity,
String name,
double amountToAdd
) async {
//final db = await database;
double newStock;
late double materialKG;
newStock=currentQuanity+amountToAdd;
await db.rawUpdate(
'UPDATE materials SET quanity = $newStock WHERE name = "$name" ');
mapM = await db.query('materials'); //update values
//the following is only because setState is not working properly on other screen
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text("Added $amountToAdd KG to $name"),
));
}
displayAmountToAdd and showDialog method are future. Use await before theses to hold method to finish.
A sample example:
const oneSecond = Duration(seconds: 1);
// ···
Future<void> printWithDelay(String message) async {
await Future.delayed(oneSecond);
print(message);
}
Learn more about async-await.

Only update UI when there are changes in the server, using streambuilder

I am using streambuilder to check whether a new order is placed or not.
I am checking the order status, if the order status is unknown I want to show a pop up, which works fine. but if i don't select an option to update the order status, streambuilder refreshes after a few seconds, and show another pop up on top of it.
Get Orders Function:
Future<Orders> getOrders() async {
String bsid = widget.bsid;
try {
Map<String, dynamic> body = {
"bsid": bsid,
};
http.Response response = await http.post(
Uri.parse(
"**API HERE**"),
body: body);
Map<String, dynamic> mapData = json.decode(response.body);
Orders myOrders;
print(response.body);
if (response.statusCode == 200) {
print("Success");
myOrders = Orders.fromJson(mapData);
}
return myOrders;
} catch (e) {}
}
Here's the stream function:
Stream<Orders> getOrdersStrem(Duration refreshTime) async* {
while (true) {
await Future.delayed(refreshTime);
yield await getOrders();
}}
StreamBuilder:
StreamBuilder<Orders>(
stream: getOrdersStrem(
Duration(
seconds: 2,
),
),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
CircularProgressIndicator.adaptive(),
);
}
var orders = snapshot.data.statedatas;
return ListView.builder(
itemCount: orders.length,
itemBuilder: (BuildContext context, int index) {
var orderResponse =
snapshot.data.statedatas[index].strAccept;
print(orderResponse);
if (orderResponse == "0") {
print("order status unknown");
Future.delayed(Duration.zero, () {
_playFile();
showCupertinoDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Center(
child: Text(
"#${orders[index].ordrAutoid}",
),
),
content: Row(
children: [
SizedBox(
width: 120,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty
.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(
MaterialState.pressed))
return Colors.black;
return Colors
.green; // Use the component's default.
},
),
),
onPressed: () async {
_stopFile();
Navigator.pop(context);
await changeOrderStatus(
orders[index].orid, "accept");
// setState(() {});
},
child: Text('Accept'),
),
),
SizedBox(
width: 15,
),
SizedBox(
width: 120,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty
.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(
MaterialState.pressed))
return Colors.black;
return Colors
.red; // Use the component's default.
},
),
),
onPressed: () async {
_stopFile();
Navigator.pop(context);
await changeOrderStatus(
orders[index].orid, "Reject");
// setState(() {});
},
child: Text('Reject'),
),
),
// TextButton(
// onPressed: () async {
// _stopFile();
// Navigator.pop(context);
// await changeOrderStatus(
// orders[index].orid, "reject");
// },
// child: Text('reject'),
// ),
],
),
),
);
}).then((value) {
_stopFile();
print("ENDING");
});
}
return Container();
Create a variable to check for the last known order status, outside your if statement, and when a new value comes, compare it to the old value first, then do the if statement logic.
//This is outside the stream builder:
String orderResponseCheck = "";
.
.
.
//This is inside your streambuidler, if the orderResponseCheck is still equal to "", the if statement will be executed,
//and the value of orderResponse wil be assigned to it. This will only show the alert dialog if the orderResponse status changes from the one that previously triggered it.
var orderResponse =snapshot.data.statedatas[index].strAccept;
print(orderResponse);
if (orderResponseCheck != orderResponse && orderResponse == "0") {
orderResponseCheck = orderResponse;
.
.
.
//logic same as before
You shouldn't call showCupertinoDialog (and probably _playFile()) from your build method. Wrapping it with Future.delayed(Duration.zero, () { ... }) was probably a workaround for an error that was given by the framework.
The build method can get executed multiple times. You probably want a way to run _playFile and show the dialog that isn't depending on the UI. I don't think StreamBuilder is the right solution for this.
You could use a StatefulWidget and execute listen on a stream from the initState method. initState will only be called once.
From what I'm reading, you're querying your API every two seconds.
Every time your API answers, you're pushing the new datas to your StreamBuilder, which explains why you're having multiple pop-ups are stacking.
One simple solution to your problem would be to have a boolean set to true when the dialog is displayed to avoid showing it multiple times.
bool isDialogShowing = false;
...
if (orderResponse == "0" && !isDialogShowing) {
isDialogShowing = true;
...
But there are a few mistakes in your code that you should avoid like :
Infinite loops
Querying your API multiple times automatically (it could DDOS your service if plenty of users are using your app at the same time)
Showing your Dialog in a ListView builder

Run a function AFTER that the alertbox has been dismissed

I already read countless links like this one but it does not help.
The use case is very simple, I want to run a function AFTER the alert box has been dismissed.
void dummyFunc() {
sleep(Duration(seconds:3));
print("done");
}
Future<bool> displayDialog() async {
return showDialog<bool>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('This is a demo alert dialog.'),
Text('Would you like to approve of this message?'),
],
),
),
actions: <Widget>[
FlatButton(
child: Text('Decline'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
FlatButton(
child: Text('Approve'),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
elevation: 24.0,
shape:RoundedRectangleBorder(),
);
},
);
}
var button = AnimatedOpacity(
opacity: 1.0,
duration: Duration(milliseconds: 500),
child: FloatingActionButton(
tooltip: 'Start',
child: Icon(iconShape),
onPressed: () async {
bool shouldUpdate = await displayDialog();
print(shouldUpdate);
if (shouldUpdate)
dummyFunc();
})
);
But the alert dialog is dismissed 3sd after.
Kindly let me know what I am doing wrong, Thank you~
I think this is happening because you are using sleep. Instead of that use Future delay.
void dummyFunc() {
print("done");
}
If you don't want delay, then you can also remove this future, this function will executed after dialog box dismissed.
Sleep will hold the process, that’s why you are facing this error.
Solved it thanks to Viren who gave me a good intuition, Timer works also nicely if you are not using a loop:
void dummyFunc() {
Timer(Duration(seconds: 3), () {
print("done");
});
}
Edit: Actually Viren's answer work better with loops! This can work also. Just avoid sleep. Spent 3h on this, now I hate sleep().