Clicking the button does not show the loader in Flutter - flutter

I need to add a loader to the button. That is, when you click on the Save button, you need to show the CircularProgressIndicator. I wanted to implement this through Bloc, because if I do this through setState (() {}), then I will have to rebuild the widget and the new added data on the page will disappear. Therefore, I want to do it through Bloc. I created a variable there and assign values ​​to it, the values ​​change but the loader does not update, tell me how can I make the loader show?
return BlocBuilder<EditPhotoCubit, EditPhotoState>(
builder: (context, state) {
return SizedBox(
height: size.height,
width: size.width,
child: _child(size, topPadding, context, cubitEditPhoto,
mainState.charging),
);
});
}
return const SizedBox();
});
...
SizedBox(
width: 148,
child: cubitEditPhoto.isLoading
? const CircularProgressIndicator(
color: constants.Colors.purpleMain)
: DefaultButtonGlow(
text: 'Save',
color:Colors.purpleMain,
shadowColor: Colors.purpleMain,
textStyle: Styles.buttonTextStyle,
onPressed: () async {
cubitEditPhoto.isLoading = true;
await cubitEditPhoto
.uploadImage(widget.chargingId)
.then((value) {
cubitEditPhoto.isLoading = false;
});
},
),
),
cubit
class EditPhotoCubit extends Cubit<EditPhotoState> {
EditPhotoCubit() : super(EditPhotoInitial());
bool isLoading = false;
}

Related

Flutter update refresh previous page when page has been pushed via a stateless widget

So here is the problem.
TabScreen() with 3 pages and one fabcontainer button (Stateless widget).
When pressed the fabcontainer will give you the chances of make one upload, after the upload i would like to refresh one of the page of the tabscreen.
return Container(
height: 45.0,
width: 45.0,
// ignore: missing_required_param
child: FabContainer(
icon: Ionicons.add_outline,
mini: true,
),
);
}
OnTap of the fabcontainer:
Navigator.pop(context);
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => CreatePost(),
),
);
},
Cannot add a .then(){setState... } because it is a stateless widget and i need to set the state of a precise page, not of the fabcontainer.
Any idea?
Thanks!
Define a updateUi method inside your TabScreen (which defines the pages)
TabScreen:
void updateUi(){
// here your logic to change the ui
// call setState after you made your changes
setState(() => {});
}
Pass this function as a constructor param to your FabContainer button
FabContainer(
icon: Ionicons.add_outline,
mini: true,
callback: updateUi,
),
Define it in your FabContainer class
final Function() callback;
Call it to update the ui
callback.call();
So what Ozan suggested was a very good beginning but i could not access the stateful widget in order to set the state.
What i did on top of Ozan's suggestion was giving the state a globalkey:
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
Assigning it to the scaffold:
return Scaffold(
key: scaffoldKey,
Making the state public removing the _MyPizzasState -> MyPizzasState
Creating a method to refresh the data:
refreshData() {
pizzas = postService.getMyPizzas();
setState(() {
});
}
Assigning a key during the creation of the MyPizzaPage:
final myPizzasKey = GlobalKey<MyPizzasState>();
{
'title': 'My Pizza',
'icon': Ionicons.pizza_sharp,
'page': MyPizzas(key: myPizzasKey),
'index': 0,
},
And, how Ozan said once i received the callback :
buildFab() {
return Container(
height: 45.0,
width: 45.0,
// ignore: missing_required_param
child: FabContainer(
icon: Ionicons.add_outline,
mini: true,
callback: refreshMyPizzas,
),
);
}
void refreshMyPizzas() {
print("Refreshing");
myPizzasKey.currentState?.refreshData();
}

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

GestureDetector and exclusive activation while calling onTap in a widget list

I'm trying to create a simple vertical scrolling calendar.
Problem is that I can't manage to find a way to reset back to previous state in case I tap on a new container.
Here's the code:
class CalendarBox extends StatelessWidget {
BoxProprieties boxProprieties = BoxProprieties();
Map item;
CalendarBox({this.item});
bool selected = false;
#override
Widget build(BuildContext context) {
return Consumer<Producer>(
builder: (context, producer, child) => GestureDetector(
onTap: () {
print(item['dateTime']);
selected = producer.selectedState(selected);
},
child: AnimatedContainer(
duration: Duration(milliseconds: 100),
color: selected == true ? Colors.blue : Colors.grey[200],
height: 80,
width: 50,
margin: EdgeInsets.only(top: 5),
child: Column(
children: [
Text(
'${item['dayNum']}',
style: TextStyle(
fontWeight: FontWeight.bold,
color: boxProprieties.dayColor(item['dateTime'])),
),
],
),
),
),
);
}
}
Here's the situation:
One way to achieve it is, create a model for boxes and keep a value current selected block, in your model you will have the index assigned to that block,
int currentSelected =1; //initial value
class Block{
int id;
..
.. // any other stuff
}
now in your code, the check modifies to
block.id == currentSelected ? Colors.blue : Colors.grey[200],
your on tap modifies to
onTap: () {
setState(){
currentSelected = block.id
};
},
If you want to prevent the rebuild of the whole thing every time you can use valueNotifire for current selected block. Hope this gives you an idea.

Show circular progress indicator, async task

I am trying to show a circular progress indicator, while I run an async task. The async task is triggered by a button click and is also directing the user to a new page. The code looks like this:
child: GestureDetector(
onTap: () async{
//some async task that takes a few seconds
Navigator.push( ...
}
I want the user, when he clicks the button to first see a circular progress indicator and when the task is complete he should be directed to another screen. But I have no idea how to show him this progress indicator.
Thanks for your answers in advance!
I made a little simulation of what you want:
Check the code below:
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// boolean variable to decide when to show progress indicator
bool showProgress = false;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: InkWell(
onTap: () async {
// set the progress indicator to true so it will be visible
setState(() {
showProgress = true;
});
// perform asynchronous task here
await Future.delayed(Duration(seconds: 4), null);
setState(() {
// set the progress indicator to true so it would not be visible
showProgress = false;
// navigate to your desired page
Navigator.push(context,
MaterialPageRoute(builder: (context) => AnotherScreen()));
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Your widgets can stay here',
style: TextStyle(fontSize: 20),
),
SizedBox(
height: 20,
),
Container(
height: 50,
width: 50,
color: Colors.blue,
child: Center(
// use ternary operator to decide when to show progress indicator
child: showProgress
? CircularProgressIndicator()
: Text('Tap me'),
),
),
],
),
),
),
);
}
}
Check output below:
Output here
NOTE: For the sake of simplicity(the example above was used). To avoid calling setState multiple times. You can use a state management technique like Bloc or Provider and maybe decide to make use of a service locator for injection.
You can use this library https://pub.dev/packages/simpleprogressdialog
Create an object of ProgressDialog
ProgressDialog progressDialog = ProgressDialog(context: context, barrierDismissible: false);
Then invoke showMaterial before your async task
progressDialog.showMaterial(layout: MaterialProgressDialogLayout.overlayWithCircularProgressIndicator);
Then after the async task, dismiss the dialog.
progressDialog.dismiss();

The dialog box opens multiples time at a same time in flutter

whenever I click many times on wheel, open multiple dialog boxes at the same time.
I just want, it should be open after previous got dismissed.
I took an image and add animation on it and wrapped it with GestureDetector widget.
onTap: event i called alertDialogBox() method which is defined for Dialog box. watch above the gif image, and called the animation method with specific Condition
CODE:
Dialog box
alertDialogBox(BuildContext context) {
return showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16.0))),
contentPadding: EdgeInsets.only(top: 10.0),
content: Stack(
children: <Widget>[
....
],
),
);
});
}
The Wheel:
GestureDetector(
child: Container(
alignment: Alignment.center,
color: Colors.blue,
child: new AnimatedBuilder(
animation: _animationCtrl,
child: Container(
height:MediaQuery.of(context).size.height/2.3,
width: MediaQuery.of(context).size.width/1.3,
decoration: BoxDecoration(
color: Colors.blue,
image: DecorationImage(
image: AssetImage('assets/wheel.png', ),
fit: BoxFit.contain,
),
borderRadius: BorderRadius.all(Radius.circular(130.0)),
)
),
builder: (BuildContext context, Widget _widget) {
.......
},
),
),
onTap: ()async{
await Firestore.instance.collection('Users').document(uid).get().then((DocumentSnapshot documnet){
getIsSpin=documnet['isSpin'];
});
if(getIsSpin=="0"){
if (!_animationCtrl.isAnimating) {
//applying animation
}
DateTime now = DateTime.now();
// String lastSpinTime =DateFormat("yyyy-MM-dd hh:mm:ss").format(now);
.....//here updating isSpin value=1 and also updating spining Date time to firestore
}else {
oneDayDuration();
}
}
)
After 24 hours trying to spin the wheel
oneDayDuration():
void oneDayDuration()async{
int differenceTime;
await({
....here fetching last spin date time from firestore});
....//here getting difference hours between last spining time and current time
if(differenceTime>=24){
await({......//updating ispin=0 to firbase
})
.then((result) => {
print("Now you're able to spin"),
}).catchError((err) => print(err));
}else{
print("Please wait for 24 hours");
alertDialogBox(context);
}
}
}
Maybe this is because, you are trying to show dialog Asynchronously, where you don't have to. Just remove async, it is unnecessary while showing a simple dialog.
You better create a method that runs async in the if condition, and remove async in the onTap. This will separate your dialog code with async.
It is too late to answer this question, I came across the same scenario and solved it.
This is because the alertDialogBox function is invoked by build method every time the state is changed. you need to limit it by adding a variable to class like 'isAlertboxOpened' and make opening of alertDialogBox conditional and avoid opening multiple dialog boxes.
The following code should work
class _MyHomePageState extends State<MyHomePage> {
bool isAlertboxOpened; // add this
#override
void initState(){
isAlertboxOpened = false; // add this
}
alertDialogBox(BuildContext context) async {
setState(() => isAlertboxOpened = true); // add this
return showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16.0))),
contentPadding: EdgeInsets.only(top: 10.0),
content: Stack(
children: <Widget>[
....
// when press ok button on pressed add this
onPressed:(){
// your code
setState(() => isAlertboxOpened = false);
Navigator.of(context).pop();
}
],
),
);
});
}
void oneDayDuration()async{
int differenceTime;
await({
....here fetching last spin date time from firestore});
....//here getting difference hours between last spining time and current time
if(differenceTime>=24){
await({......//updating ispin=0 to firbase
})
.then((result) => {
print("Now you're able to spin"),
}).catchError((err) => print(err));
}else{
print("Please wait for 24 hours");
isAlertboxOpened ? (){} : // add this to make this conditional
alertDialogBox(context);
}
}
}