How to set global variable using OK button on AlertDialog in StatefulBuilder?
String stringMain = "My String";
Future<String?> dialogChangeMainContent(BuildContext context) {
return showDialog<String>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
return AlertDialog(
title: const Text('AlertDialog Title'),
content: const Text('AlertDialog description'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context, 'OK');
setState(() {
// setState2();
stringMain = "New String";
});
},
child: const Text('OK'),
),
],
);
});
});
}
I changed StatefulWidget from StatelessWidget, but nothing changes. What I know is the problem is because the button is in the StatefulBuilder. If using BLoC etc, it's too wasteful for only small applications.
I found a way to solve my problem, redrawing (refreshing) the Text (or other widget) in a Stateless Widget from an AlertDialog in a StatefulBuilder. By creating a setState with a different name, for example setStateTarget.
Without having to rebuild the entire page, or without state management like BLoC. My code:
String stringMain = "My String";
#override
Widget build(BuildContext context) {
return Column(
// ....
children: [
StatefulBuilder(builder: (BuildContext context, setStateTarget) {
return Column(children: [
Text(stringMain), // <--- target
TextButton(
onPressed: () => dialogChangeMainContent(context, setStateTarget),
child: const Text('Show Dialog'))
]);
})
],
);
}
Future<String?> dialogChangeMainContent(BuildContext context, StateSetter setStateTarget) {
return showDialog<String>(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
return AlertDialog(
title: const Text('AlertDialog Title'),
content: const Text('AlertDialog description'),
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.pop(context, 'OK');
setStateTarget(() {
// setState2();
stringMain = "New String";
});
},
child: const Text('OK'),
),
],
);
});
});
}
Related
I´m new in the bloc pattern. I want to show up an alert dialog when I press a button, but when I use showDialog func and showDialog I want to access Bloc from context or BlocListner doesn't contain bloc and throw error is there a way to access bloc in this situatuion
Error: Could not find the correct Provider above this BlocListener<TeacherFinancialCubit, TeacherFinancialState> Widget
#override
Widget build(BuildContext context) {
return BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
if(state is! Authorize) {
return const BaBaFullScreenLoading();
}
return BlocProvider<TeacherFinancialCubit>(
lazy: false,
create: (context) => TeacherFinancialCubit(locator())..getTeacherFinancial(state.user, state.account, teacherId ?? 0),
child: _masterLayout(context),
);
},
);
}
Widget _masterLayout(BuildContext context) {
Authorize authorize = context.read<UserBloc>().state as Authorize;
return MasterLayout(
showBack: true,
floatingActionButton: authorize.account.type == master ? FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Future.microtask(() => _showAddPaymentDialog(context));
},
) : null,
title: const Text(""),
child: WillPopScope(
onWillPop: () => Future.value(false),
child: _showTeacherFinancials(context, authorize),
),
);
}
void _showAddPaymentDialog(BuildContext context) async {
return showDialog(
barrierDismissible: false,
context: context,
builder: (_) {
return BlocListener<TeacherFinancialCubit, TeacherFinancialState>(
listener: (context, state) {
if(state is TeacherFinancialLoading) {
EasyLoadingHelper.showLoading();
}
else {
EasyLoadingHelper.dismiss();
if(state is TeacherFinancialFailed) {
EasyLoadingHelper.showToastError(context, message: state.error);
}
else if(state is TeacherFinancialSuccess) {
EasyLoadingHelper.showToastSuccess(context, message: successMessage);
Navigator.pop(context);
}
}
},
child: AlertDialog(
title: const Text(''),
content: Form(
key: addPaymentFormKey,
child: Text(""),
),
actions: [],
),
);
}
);
}
Make sure to provide the bloc to the AlertDialog. You can do something like this:
return showDialog(
...
builder: (dialogContext) => BlocProvider.value(
value: BlocProvider.of<TeacherFinancialCubit>(context),
child: BlocListener<TeacherFinancialCubit, TeacherFinancialState>(
...
),
),
I am fetching data from api in getSmartTags()
And if error occurs, I want to show the dialog.
I made the code below but this error comes.
lib/main.dart:1218:14: Error: Undefined name 'context'. context: context,
^^^^^^^
#override
Widget build(BuildContext context) {
context is in build function, but I want to show the alert dialog not in build function, is it possible?
Future<void> _showMyDialog() async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text('This is a demo alert dialog.'),
Text('Would you like to approve of this message?'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('Approve'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Future getSmartTags() async {
var url = Uri.https(apiServer,'/api/smarttags');
try{
var resp = await http.get(url);
throw Exception();
}catch(e){
_showMyDialog();
}
}
Try this
//add context as parameter
Future<void> _showMyDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('AlertDialog Title'),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text('This is a demo alert dialog.'),
Text('Would you like to approve of this message?'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('Approve'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Future getSmartTags() async {
var url = Uri.https(apiServer,'/api/smarttags');
try{
var resp = await http.get(url);
throw Exception();
}catch(e){
_showMyDialog(context); //add this too
}
}
What is the right way to place all these widgets together? When I tried to place together widget which is building the listview. separated widget and the widgets where I am creating the UI for filters, it draws only widget with filtering items but doesn't create the listview. I was searching that I should place it in the column widget and in the expanded but it also doesn't work as I want.
Here is my code:
class _ProductListState extends State<ProductList> {
#override
Widget build(BuildContext context) {
var providerGridPagination = Provider.of<ProviderGridProduct>(context);
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
title: Text("Категории товаров", style: TextStyle(color: Colors.black45),),
leading: IconButton(onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Home()),
);
}, icon: Icon(Icons.arrow_back, color: Colors.black45,),),
),
body: Column(
children: [
FiltersWidget(),
SmartRefresher(
controller: providerGridPagination.refreshController,
enablePullUp: true,
onRefresh: () async {
final result = await providerGridPagination.getProductData(isRefresh: true);
if (result) {
providerGridPagination.refreshController.refreshCompleted();
} else {
providerGridPagination.refreshController.refreshFailed();
}
},
onLoading: () async {
final result = await providerGridPagination.getProductData();
if (result) {
providerGridPagination.refreshController.loadComplete();
} else {
providerGridPagination.refreshController.loadFailed();
}
},
child: ListView.separated(
itemBuilder: (context, index) {
//final gridItems = providerGridPagination.itemgrid[index];
return ListTile(
title: Text(providerGridPagination.itemgrid[index].title!),
);
},
separatorBuilder: (context, index) => Divider(),
itemCount: providerGridPagination.itemgrid.length,
),
),
],
),
);
}
}
the error is:
The following assertion was thrown during a scheduler callback:
This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
I am trying to dynamically update an alert dialog's action list when something takes place in the alert dialog. Essentially, by default the dialog has a "Cancel" action button. But once the user does something in the dialog, I want it to have a "Cancel" and an "Accept" button. I have tried using a StatefulBuilder, which is how I am getting the rest of the dialog to update state. However, it is not working with the action buttons.
I've tried conditionally rendering the button, as well as generating a list to use for the dialog actions, and using setState to add to the list when an action takes place. Neither works, although other state updates within the dialog's content work with the StatefulBuilder. The dialog opens with only the "Cancel" action, and will not update to include the "Accept" action as well.
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);
},
);
}),
actions: <Widget>[
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
selectedRadio == 1 ? TextButton(
child: Text('Accept'),
onPressed: () {
Navigator.of(context).pop();
},
) : SizedBox.Shrink(),
],
);
},
),
);
},
);
Try putting the AlertDialog inside the StatefulBuilder
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
Create a StatefulWidget to display your AlertDialog, and manage selectedRadio there. Also take advantage of Dart's collection if to handle the conditional button:
class MyDialog extends StatefulWidget {
const MyDialog({Key? key}) : super(key: key);
#override
State<MyDialog> createState() => _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
int selectedRadio = 0;
#override
Widget build(BuildContext context) {
return AlertDialog(
actions: <Widget>[
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
if (selectedRadio == 1)
TextButton(
child: Text('Accept'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
content: Column(
mainAxisSize: MainAxisSize.min,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
value: index,
groupValue: selectedRadio,
onChanged: (int? value) {
setState(() => selectedRadio = value!);
},
);
}),
));
}
}
After this, you can display your dialog for example like this:
TextButton(
child: Text('Pressme'),
onPressed: () async => await showDialog<void>(
context: context,
builder: (BuildContext context) {
return const MyDialog();
},
))
Future<bool> show(BuildContext context) async {
return Platform.isIOS
? await showCupertinoDialog<bool>
(context: context, builder: (context)=>this)
:await showDialog<bool>(
context: context,
builder: (context) => this,
);
}
Can anyone help me to understand the term 'this',what does 'this' refer to and how does showDialog works that it returns Future.I tried to read documentation but still couldn't understand it?Is it the same as AlertDialog widget?
well, it's pretty much what the documentation said, it shows a material dialog above the current content of your app, as for this it passes the current widget as child for the dialog, as for the returned value is just like normal page navigation that when you call pop(context, {value}) method you can also return a value, so that value that inside pop will be returned from the dialog.
here is an example below:
class DialogTest extends StatefulWidget {
#override
_DialogTestState createState() => _DialogTestState();
}
class _DialogTestState extends State<DialogTest> {
// the value that will be typed to the dialog
String dialogText;
// the value that will be returned from the dialog
String returnedFromDialog;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sample Code'),
),
body: Center(
child:
Text('You got this value from the dialog => $returnedFromDialog'),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
returnedFromDialog = await showDialog<String>(
context: context,
builder: (context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextField(
onChanged: (value) => dialogText = value,
),
FlatButton(
onPressed: () {
setState(() => Navigator.pop(context, dialogText));
},
child: Text(
'Close dialog',
style: TextStyle(color: Colors.red),
),
)
],
),
);
});
},
child: Icon(Icons.open_in_browser),
),
);
}
}