setState not working from inside AlertDialog - flutter

I am trying to call setState inside an AlertDialog, but surprisingly setState is not changing the state variable. The variable (tasbeehChantCanAdd) i want to set from the dialog box is inside the body of the main page(outside the dialog box) This is the code for the alertDialog:
Future<void> _alertRemoveEntry(BuildContext context, String textToRemove) {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return WillPopScope(
onWillPop: (){},
child: StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
),
title: const Text('Enter your info',style: TextStyle(fontWeight: FontWeight.normal,fontSize: 14),),
content: Container(
height: 150,
child: const Text("Sure to remove this?")
),
actions: <Widget>[
Container(
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
//side: BorderSide(color: Colors.red)
),
primary: Colors.purple,
),
child: Text("CANCEL"),
onPressed: () {
Navigator.of(context).pop();
},
),
Container(
width: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
//side: BorderSide(color: Colors.red)
),
primary: Colors.purple,
),
child: Text("REMOVE"),
onPressed: (){
setState((){
tasbeehChantCanAdd = "state changed";
});
ClassHub().myListSharePreference("sptasbeehAddedList", "set", tasbeehChantCanAdd, "");
Navigator.of(context).pop();
},
),
],),),
],
);
},
),
);
});
}
Please what am i doing wrong? Any help would be appreciated. Thanks

The main state class also have setState and while you are using StatefulBuilder it also has setState, being scope priority setState is coming from StatefulBuilder. You can rename it and use
child: StatefulBuilder(
builder: (context, setStateSB) {
....
setState((){ /// for state class IU update
tasbeehChantCanAdd = "state changed";
});
setStateSB((){ // update Ui inside dialog
tasbeehChantCanAdd = "state changed";
});

Try to add your AlertDialog widget inside StatefulBuilder hope its helpful to you.
Refer StatefulBuilder here
yourDropdown(BuildContext context) {
return showDialog(
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, StateSetter setState) {
return AlertDialog(
);
},
);
},
);
}
Refer my answer here

Are you me from another dimension? Seriously, I just solved the same problem. I don't know if it is the recommended way of setting states on dialog, since the documentation on showDialog mentions something about states, but I solved it this way:
var choice = await showDialog(
context: context,
builder: (context) {
var list = [];
return StatefulBuilder(builder: (BuildContext context, setState) {
return AlertDialog(
[...]
);
});
});
Just put the variables you need to update through setState just before the StatefulBuilder

Related

Text widget doesn't appear in dialog after user Future.delayed in Flutter

I have a dialog that appears after user click on send button . When dialog appears , I want to show a text after 5 seconds . I use Future.delayed but the text doesn't appear at all . It appears only when I close the dialog and open it again . I want to show the text after 5 seconds from opening the dialog .
Here is my function
void _initialize() {
Future<void>.delayed(const Duration(seconds: 3), () {
if (mounted) {
setState(() {
visibility = true;
});
}
});
}
}
And here is my dialog code in the onTab of the button
_initialize()
showDialog(context: context,
barrierDismissible: false,
builder: (BuildContext contextd){
return WillPopScope(
onWillPop: () {return Future.value(false);},
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Padding(
padding: EdgeInsets.fromLTRB(20.w, 20.h, 20.w, 20.h),
child: Column(
children: [
//here are some widgets
Visibility(
visible:visibility?true:false,
child: Text("Resend?",style: TextStyle(decoration:
TextDecoration.underline,),)),
],),
),
),
);
});
Try using StatefulBuilder like this:
showDialog(
context: context,
builder: (BuildContext context) => StatefulBuilder(
builder: (context, setState) {
return //somthing to return;
},
));
This happens because your setState that update visibility has a different context than builder dialog. Visibility is actually update but only appear in dialog when close and open again because this is how build dialog is updated. If you want to work with the context dialog need to use that widget:
StatefulBuilder(
builder: (context, setState) {
Into your dialog and then call that setState.
Try using a StatefulBuilder. Using the bool visible and the setDialogState argument, you can update the state of the contents of the dialog.
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return StatefulBuilder(
builder(context, setDialogState) {
// this variable is used for the conditional display
bool visible = false;
// this future will update the visible variable in 3 seconds, after the dialog is displayed already
Future.delayed(Duration(seconds: 3)).then(() => setDialogState(() { visible = true;}));
return WillPopScope(
onWillPop: () { return Future.value(false); },
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: Padding(
padding: EdgeInsets.fromLTRB(20.w, 20.h, 20.w, 20.h),
child: Column(
children: [
//here are some widgets
Visibility(
visible:visible,
child: Text(
"Resend?",
style: TextStyle(
decoration: TextDecoration.underline,
),
),
),
],
),
),
),
);
},
);
};

why showDialog function is not opening the Dialog in parent?

I want to open a dialog in a parent statefull widget and use a callback to trigger that function from child class statefull widget, but the function called in parent does not get executed from child, the function does get executed but the showDialog seems not to be used, I even tried to move the function responsible to open to dialog to the child class but showDialog does not work either.
Here the code:
shareDilog function in parent class:
shareDialog(screenWidth, BuildContext cont) {
return showDialog(
context: cont,
builder: (BuildContext cont) {
print('inside inside');
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
elevation: 0,
child: StatefulBuilder(
builder: (BuildContext cont, StateSetter setState) {
return Container(
height: 680,
width: 650,
decoration: BoxDecoration(
color: Color(0xFF282828),
),
child: ShareDialog(),
);
}),
);
},
barrierDismissible: false
);
}
Where I pass this shareDialog function in parent to child:
Record(
visibleColumns: visibleColumns,
recordFullNameFieldName: recordFullNameFieldName,
oneRecord: listRecordsFilter[index],
screenWidth: screenWidth,
openShareDialog: shareDialog, // <-------
dashboardContext: context, // <--------
)
Where the call gets executed in child:
final Function(double, BuildContext)? openShareDialog; // the constructor parameter
PopupMenuItem(
onTap: () {
try {
widget.openShareDialog!(widget.screenWidth!, widget.dashboardContext!); // <---here the call back
// _showMyDialog(); // This is the test when I moved the function to the child
} catch(e) {
print(e);
}
},
child: ListTile(
leading: Icon(Icons.groups_sharp),
title: Text('Share'),
),
),
This can be achieved in many ways, here is my way:
Create the shareDialog() function in the parent widget. please try to assign different names for all the contexts. this may confuse flutter.
here is a mini version of your code for the same:
shareDialog(screenWidth, BuildContext cont) {
return showDialog(
context: cont,
builder: (ctx) {
print('inside inside');
return Dialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
elevation: 0,
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Color(0xFF282828),
),
child: Text(
"Hello World!",
style: TextStyles.title2.colour(Colors.white),
),
),
);
},
);}
Next pass the function and other parameters to the child widget.
Record(showDialog: shareDialog)
and finally, use the passed function in the child widget. here I have created a dummy Record widget that contains a listTile which on tapping executes the shareDialog function:
class Record extends StatelessWidget {
final Function showDialog;
Record({this.showDialog});
#override
Widget build(BuildContext context) {
return ListTile(
onTap: () => showDialog(SizeConfig.screenWidth, context),
title: Text("Tile"),
leading: CircleAvatar(
backgroundColor: Colors.amber,
child: Icon(Icons.umbrella, color: Colors.white),
),
subtitle: Text("Show Share dialog"),
);
}
}

setstate for elevated button in dialog(Flutter)?

For the elevated button, I would like to show the visible/hide part content through setState. However, I could do it in normal pages but not a Dialog.
Here's the code:
bool addmaterial = true;
ClipRRect(
borderRadius: BorderRadius.circular(50),
child: SizedBox(
height: 40,
width: 400,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all<Color>(
Color(0xfff4f4f4)),
),
onPressed: () {
addmaterial = !addmaterial;
},
Try adding a StateFulBuilder inside your showDialog widget. If you want to change the state inside the showDialog
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
actions: <Widget>[],
content: Container(),
);
},
);
},
);

AlertDialog should show another AlertDialog but it's not happening till i close it and open it again

I want to show another AlertDialog when I click on one of its children
But when I click on it doesn't
Show it until I close the Alert and open it again
I want to navigate to Second AlertDialog without closing it
Any help will be appreciated
any way to make it open another dialog or a way to close it and open it again
Here is the code
padding: const EdgeInsets.only(top: 12.0),
child: ListView(children: [
FutureBuilder<DropDown>(
future: getDropData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
DropDown dropdown = snapshot.data;
return RaisedButton(
color: maincolor,
splashColor: accentcolor,
onPressed: () {
showDialog(
context: context,
useSafeArea: true,
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 20),
child: ListView.builder(
itemCount: dropdown.categories.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(
top: 8.0, right: 8, left: 8),
child: Container(
),
child: FlatButton(
onPressed: () {
setState(() {
categoryID = dropdown
.categories[index]
.categoryId;
});
getDropData();
},
child: Text(
dropdown.categories[index].name,
)),
),
);
}),
),
));```
I think you just need to use setState() to wrap the showDialog() but I can't test it because I don't have a DropDown class.
EDIT
I tried with a simple structure and it works fine. As Dung Ngo mentioned, just use a StatefulBuilder to build the content of the first AlertDialog widget. Triggering another showDialog() inside to bring up the second AlertDialog widget.
class _YourWidgetState extends State<YourWidget> {
AlertDialog alert = AlertDialog(content: Center(child:Text("Second Alert Dialog")));
#override
Widget build(BuildContext context) {
return RaisedButton(onPressed: (){
showDialog(
context: context,
builder: (_) => AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState){
return Column(
children: <Widget>[
RaisedButton( onPressed: (){
showDialog(context: context, builder: (_) => alert);
}),
RaisedButton(onPressed: (){
showDialog(context: context, builder: (_) => alert);
}),
],
);
}
),
)
);
});
}
}
Results
The setState doesn't work until you close the first one because it belongs to the context of mainpage, not the context of your 1st dialog.
You can use a StatefulBuilder to create a a StateSetter that invoke a rebuild base on the context of the 1st dialog: https://api.flutter.dev/flutter/widgets/StatefulBuilder-class.html
Thanks to Dung Ngo and Kennith for your answers it really helped me and I learnt from your answers a lot your answers were right by the way
here what I have done
a method that opens the second dialog
showGeneralDialog(
barrierLabel: "Barrier",
barrierDismissible: true,
barrierColor: maincolor,
transitionDuration: Duration(milliseconds: 200),
context: context,
pageBuilder: (_, __, ___) {
return FutureBuilder<Manufacturer>(
future: getManufucturer(categoryID, parentID),
builder: (context, snapshot) {
if (snapshot.hasData) {
Manufacturer dropdown = snapshot.data;
return Container(
height: 400,
width: 200,
child: ListView.builder(
itemCount: dropdown.manufacturers.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 60),
child: RaisedButton(
child: Text(
dropdown.manufacturers[index].name,
style: GoogleFonts.cairo(
color: maincolor,
fontSize: 14,
fontWeight: FontWeight.bold),
),
onPressed: () async {
parentID =
dropdown.manufacturers[index].manufacturerId;
manufacturerID = parentID;
print(parentID);
Manufacturer newDropDown =
await getManufucturer(categoryID, parentID);
Navigator.pop(context);
Navigator.pop(context);
}),
);
},
),
);
} else {
return Center(
child: CircularProgressIndicator(
strokeWidth: 12,
backgroundColor: maincolor,
),
);
}
});
},
transitionBuilder: (_, anim, __, child) {
return ScaleTransition(
scale: Tween(begin: 0.0, end: 1.0).animate(anim),
child: child,
);
},
);
}
then I created the button that opens the first dialog and it that dialog I
called that method that I defined earlier
again thanks for your efforts Dung and kennith you really helped me

How to add tabs to an alert dialog in flutter

I have a stand-alone class called dialogs.dart which contains a reusable alert dialog. I want this alert dialog to have tabs, but I have no idea how to implement it if it's possible. Any ideas are highly appreciated.
Here's my code for dialogs.dart
import 'package:flutter/material.dart';
enum DialogAction{yes,abort}
class Dialogs{
static int selectedRadio;
static void setSelectedRadio(int val){
selectedRadio=val;
}
static Future<DialogAction> yesAbortDialog(
BuildContext context,
String title,
String body,
)async{
final action= await showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
title: Text(title),
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(3, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int value) {
setState(() => selectedRadio = value);
},
);
}),
);
},
),
actions: <Widget>[
FlatButton(
onPressed: ()=>Navigator.of(context).pop(DialogAction.abort),
child: const Text('cancel'),
),
RaisedButton(
onPressed: ()=>Navigator.of(context).pop(DialogAction.yes),
child: const Text('Proceed', style: TextStyle(color: Colors.white),),
color: Colors.green,
),
],
);
}
);
return(action!=null)?action: DialogAction.abort;
}
}
Don't use 'AlertDialog' for this purpose. Use 'Dialog' class. Inside it you can configure your own child the way you like. Tie tabs and events to it. Refer this - https://api.flutter.dev/flutter/material/Dialog-class.html
You can use 'container' with margin to get the barrier effect.