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.
Related
I am working on a statefulWidget and my purpose is to make sure that the next button is not clickable until an option (in this language is selected). However it doesn't seem to work, I also added Yaesin's(Someone who answered) answer to the code
ListView.builder(
itemCount: histoires.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
histoires[index].title,
style: TextStyle(color: Colors.pink),
),
trailing: IconButton(
icon: Icon(Icons.play_arrow),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) =>
AlertDialog(
content: Column(children: [
InkWell(
onTap: () {
_handleTap;
},
child: ListTile(
trailing: Icon(Icons
.flag_circle_rounded),
title: Text(
"French",
style: TextStyle(
color: Colors
.blueGrey),
))),
_active
? InkWell(
onTap: () {},
child: Image.asset(
"assets/nextactive.png",
height: height * 0.2,
width: width * 0.4),
)
: Image.asset(
"assets/nextinactive.png",
height: height * 0,
width: width * 0)
]),
));
});
}));
}),
To update dialog UI, you can use StatefulBuilder's setState
return StatefulBuilder(
builder: (context, setState) =>
AlertDialog(
content: Column(children: [
While using separate method, pass the StatefulBuilder's setState to the function. For your case, it will be
onPressed: () async {
await showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setStateSB) => AlertDialog(
content: Column(children: [
InkWell(
onTap: () {
_handleTap(setStateSB);
},
child: ListTile(
Also make sure to receive this setStateSB(renamed to avoid confusion with state's setState).
_handleTap(setStateSB){ ....
More about using StatefulBuilder
Since your in a Dialog, for setState to work, you need to wrap it with a StatefulBuilder.
You haven't included your full code, so I'm using this example taken from the docs:
await showDialog<void>(
context: context,
builder: (BuildContext context) {
int? selectedRadio = 0;
return AlertDialog(
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
);
See also
A YouTube video by the Flutter team explaining StatefulBuilder
I have a helper function to create dialogs in my flutter app:
Future<void> showContentDialog(BuildContext context,
{required Widget content, String? title, List<Tuple2<String, void Function()>>? actions}) async {
Widget? titleWidget;
if (title != null) {
titleWidget = Text(
title,
style: Theme.of(context).textTheme.titleSmall!.copyWith(fontWeight: FontWeight.bold),
);
}
var dialogActions = <Widget>[];
if (actions != null) {
dialogActions.addAll(
actions.map(
(a) => TextButton(
child: Text(a.item1),
onPressed: () {
a.item2();
}),
),
);
}
await showDialog(
context: context,
barrierDismissible: true,
builder: (context) => AlertDialog(
title: titleWidget,
content: SizedBox(width: ThemeHelpers.maxPopupWidth, child: content),
actions: dialogActions,
),
);
}
There is another similar one that is used on Apple devices that uses equivalent widgets.
I can easyly manage state on the content portion of the dialog by wrapping it in a StatefulBuilder, but how can I enable and disable the dialog buttons (the actions passed to the AlertDialog) builder depending on content state?
My first idea was to add another a ValueNotifier parameter to the action builders and wrap them in ValueListenerBuilders but that didn't work.
Do I have any way of doing that other than including the actions as buttons inside the content (were I can easyly manage their state)?
You can pass null on onPressed to disable the button state. While it is not clear from where you like to controll the state, you can use ValueNotifier, and it work for all widget
final ValueNotifier<bool> enableButton = ValueNotifier(false);
Future<void> showContentDialog(
BuildContext context,
) async {
await showDialog(
context: context,
barrierDismissible: true,
builder: (context) => AlertDialog(
content: SizedBox(
width: 222,
child: Column(
children: [
Text("A"),
ElevatedButton(
onPressed: () {
enableButton.value = !enableButton.value;
},
child: Text("toggleButtonState"),
)
],
),
),
actions: [
ValueListenableBuilder<bool>(
valueListenable: enableButton,
builder: (context, value, child) => ElevatedButton(
onPressed: value ? () {} : null,
child: Text("BTN"),
),
),
],
),
);
}
In content add column and add dialog widgets in it
await showDialog(
context: context,
barrierDismissible: true,
builder: (context) => AlertDialog(
title: titleWidget,
content: Column(
mainAxisSize: MainAxisSize.min,
children : [
SizedBox(width: ThemeHelpers.maxPopupWidth, child: content),
dialogActions,
]
)
),
);
I am using an alert box where I am getting the image from gallery of the user, but the updated image is not getting displayed.
When I close the alert box and again open the alert box, then it's getting displayed. I am using provider package to handle the data.
Here is a video of what I am getting now
Here is my code:
child: ChangeNotifierProvider<MyProvider>(
create: (context) => MyProvider(),
child: Consumer<MyProvider>(
builder: (context, provider, child) {
return Column(
children: [
ElevatedButton(
onPressed: () {
showDialog(
barrierDismissible: true,
context: context,
barrierColor: Colors.black.withOpacity(0.5),
builder: (ctx) => AlertDialog(actions: <Widget>[
----> // alert box styling
Expanded(
child: Column(
children: [
CircleAvatar(
backgroundColor:
Colors.transparent,
radius: 175,
child: ClipOval(
child: provider
.image !=
null
? Image.network(
provider.image
.path,
height: 200,
)
: Image.asset(
'assets/profile.webp',
width: 250.0,
height: 250.0,
fit: BoxFit
.cover,
),
)),
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: <Widget>[
ElevatedButton(
onPressed: () async {
var image = await ImagePicker()
.pickImage(
source: ImageSource
.camera);
provider.setImage(
image);
},
child: Text(
'Use camera',
style: t3b,
),
),
},
child: const Text('Click me ')),
],
);
},
),
),
),
);
}
}
class MyProvider extends ChangeNotifier {
var image;
Future setImage(img) async {
image = img;
notifyListeners();
}
I am also facing the same issue in mobile development then I know we have to rebuild the whole dialog and then it will work well
showDialog(
context: context,
builder: (BuildContext context) {
int selectedRadio = 0; // Declare your variable outside the builder
return AlertDialog(
content: StatefulBuilder( // You need this, notice the parameters below:
builder: (BuildContext context, StateSetter setState) {
return Column( // Then, the content of your dialog.
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int value) {
// Whenever you need, call setState on your variable
setState(() => selectedRadio = value);
},
);
}),
);
},
),
);
},
);
Use a StatefulBuilder in the content section of the AlertDialog. Even the StatefulBuilder docs actually have an example with a dialog.
What it does is provide you with a new context, and setState function to rebuild when needed.
also sharing the reference I used for this: Reference for solving this same
I cannot understand why my rangeslider is not updating values when dragging. I am supposed to update the state with the onChanged function, but nothing seems to work. It only works when I press the "Apply" button and I reopen my alertDialog again, where I see the values of the slider updated. All this is wrapped inside and Appbar in a statefulWidget. When I press the filter button a pop up appears with the filter.
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Filter'),
content: SizedBox(
child: Card(
child: Column(
children: [
Text('Age'),
RangeSlider(
values: _rangeValues,
divisions: 20,
labels: RangeLabels(
_rangeValues.start.round().toString(),
_rangeValues.end.round().toString()),
onChanged: ( value ) {
_rangeValues = value ;
setState(() {
isFiltering = false;
varSelectedFilterAgeStart = value.start;
varSelectedFilterAgeEnd = value.end;
});
},
min: 0.0,
max: 20.0,
),
],
),
),
),
actions: [
ElevatedButton(
child: const Text('Apply'),
onPressed: () {
setState(() {
isFiltering = true;
varSelectedFilterAge = varSelectedFilterAgeStart;
});
Navigator.of(context).pop(varSelectedFilterAge);
},
),
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
What am I doing wrong??
Wrap your AlertDialog with StatefulBuilder and use its setState.
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) => AlertDialog(
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