Dropdown List Title Not Changing - flutter

The value of drop down list is changing but the title of it isn't changing, the title only changes when I hot reloads the app.
List<String> _states = ['A', 'B', 'C', 'D'];
String _chosenValue;
void _showSettingsPanel() {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(children: [
DropdownButton(
hint: Text("Select State"),
value: _chosenValue,
onChanged: (newValue) {
setState(() {
_chosenValue = newValue;
});
},
items: _states.map((valueItem) {
return DropdownMenuItem(
value: valueItem,
child: Text(valueItem),
);
}).toList(),
),
]);
},
);
}

Use StatefulBuilder for setting state in ModelSheet. Do as follows:
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return BottomSheet(
onClosing: () {},
builder: (BuildContext context) {
return StatefulBuilder(
builder: (BuildContext context, setState) {
return Column(
children: [
DropdownButton(
hint: Text("Select State"),
value: _chosenValue,
onChanged: (newValue) {
print(newValue);
setState(() {
_chosenValue = newValue.toString();
});
},
items: _states.map((valueItem) {
return DropdownMenuItem(
value: valueItem.toString(),
child: Text(valueItem),
);
}).toList(),
),
],
);
});
},
);
},
)

SetState won't work for bottomsheet so you can use ValueNotifier to reflect changes in bottomsheet. Have a look on below code.
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
List<String> _states = ['A', 'B', 'C', 'D'];
// String _chosenValue;
ValueNotifier<String> _chosenValue = ValueNotifier<String>("A");
#override
void dispose() {
_chosenValue.dispose();
super.dispose();
}
void _incrementCounter() {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(
children: [
ValueListenableBuilder<String>(
valueListenable: _chosenValue,
builder: (BuildContext context, String value, Widget child) {
return DropdownButton(
hint: Text("Select State"),
value: _chosenValue.value,
onChanged: (newValue) {
setState(() {
_chosenValue.value = newValue;
});
},
items: _states.map((valueItem) {
return DropdownMenuItem(
value: valueItem,
child: Text(valueItem),
);
}).toList(),
);
},
),
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Related

DropdowMenu does not show the selected Item

In the following code i can add and remove Tabs to the screen. For removing, i have defide a Button on the AppBar that after pressing it a DropdownMenu appears who let me select which Tab i want to remove and it removes the selected Item.
The problem that i have is that when i select a item DropdownMenu it does not show the selected item.
Thanks in advance for some help.
Follows the complete code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
String? selectedTab = tabs[0].text;
var tabName = "";
static List<Tab> tabs = [
const Tab(text: ""),
];
List<Widget> tabViewChildren = [
Container(
height: 400,
),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: tabs.length,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Enter tab name"),
content: TextField(
onChanged: (String value) {
tabName = value;
},
),
actions: <Widget>[
ElevatedButton(
child: const Text("Add"),
onPressed: () {
setState(() {
tabs.add(Tab(text: tabName));
tabViewChildren.add(Container(height: 400));
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(
Icons.add_box,
),
label: const Text('Add Tab'),
),
Opacity(
opacity: tabs.isNotEmpty ? 1 : 0.4,
child: ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Select tab to remove"),
content: tabs.isNotEmpty
? DropdownButton<String>(
items: tabs
.map((tab) => DropdownMenuItem<String>(
value: tab.text,
child: Text(tab.text ?? ""),
))
.toList(),
onChanged: (String? value) {
setState(() {
selectedTab = value;
});
},
value: selectedTab,
)
: Container(),
actions: <Widget>[
ElevatedButton(
child: const Text("Remove"),
onPressed: () {
setState(() {
int index = tabs.indexWhere((tab) => tab.text == selectedTab);
tabs.removeAt(index);
tabViewChildren.removeAt(index);
selectedTab = tabs.isNotEmpty ? tabs[0].text : null;
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(Icons.remove),
label: const Text('Remove Tab'),
),
),
],
title: const Text("Tab in Flutter"),
bottom: TabBar(tabs: tabs),
),
body: TabBarView(children: tabViewChildren)));
}
}
The Problem:
Flutter works as a tree, each node has its own build context so showDialog is returning a build with a new build context, therefore in your code whenever you call setState in the dialog => you are calling the setState for the parent context (page), basically, you are updating the Screen widget not the dialog widget.
The Solution:
you have to use StatefulBuilder inside the Dialog widget so that it will have its own setState functionality. see the code below
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
String? selectedTab = tabs[0].text;
var tabName = "";
static List<Tab> tabs = [
const Tab(text: ""),
];
List<Widget> tabViewChildren = [
Container(
height: 400,
),
];
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: 0,
length: tabs.length,
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Enter tab name"),
content: TextField(
onChanged: (String value) {
tabName = value;
},
),
actions: <Widget>[
ElevatedButton(
child: const Text("Add"),
onPressed: () {
setState(() {
tabs.add(Tab(text: tabName));
tabViewChildren.add(Container(height: 400));
});
Navigator.of(context).pop();
},
),
],
);
},
);
},
icon: const Icon(
Icons.add_box,
),
label: const Text('Add Tab'),
),
Opacity(
opacity: tabs.isNotEmpty ? 1 : 0.4,
child: ElevatedButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return StatefulBuilder(
builder: (context, setState) => AlertDialog(
title: const Text("Select tab to remove"),
content: tabs.isNotEmpty
? DropdownButton<String>(
items: tabs
.map(
(tab) => DropdownMenuItem<String>(
value: tab.text,
child: Text(tab.text ?? ""),
))
.toList(),
onChanged: (String? value) {
selectedTab = value;
setState(() {});
},
value: selectedTab,
)
: Container(),
actions: <Widget>[
ElevatedButton(
child: const Text("Remove"),
onPressed: () {
setState(() {
int index = tabs.indexWhere(
(tab) => tab.text == selectedTab);
tabs.removeAt(index);
tabViewChildren.removeAt(index);
selectedTab =
tabs.isNotEmpty ? tabs[0].text : null;
});
Navigator.of(context).pop();
},
),
],
),
);
},
);
},
icon: const Icon(Icons.remove),
label: const Text('Remove Tab'),
),
),
],
title: const Text("Tab in Flutter"),
bottom: TabBar(tabs: tabs),
),
body: TabBarView(children: tabViewChildren)));
}
}

Flutter: How to select multiple options in checkboxlisttile

class PickLabelScreen extends StatefulWidget {
const PickLabelScreen({
Key? key,
required this.labelTitle,
}) : super(key: key);
final String labelTitle;
#override
State<PickLabelScreen> createState() => _PickLabelScreenState();
}
class _PickLabelScreenState extends State<PickLabelScreen> {
late String _labelChoosed;
#override
void initState() {
super.initState();
_labelChoosed = widget.labelTitle;
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
Navigator.of(context).pop(_labelChoosed);
return false;
},
child: Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(
Icons.arrow_back,
),
onPressed: () {
Navigator.of(context).pop(_labelChoosed);
},
),
actions: [
IconButton(
onPressed: () async {
final String newLabel = await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const DialogLabelWidget(),
);
setState(() {
if (newLabel.isNotEmpty) _labelChoosed = newLabel;
});
},
icon: const Icon(Icons.add),
),
],
),
body: Consumer<LabelProvider>(
builder: (context, labelProvider, child) =>
labelProvider.items.isEmpty
? child!
: ListView.builder(
itemBuilder: (context, index) {
final currentLabel = labelProvider.items[index];
return CheckboxListTile(
value: _labelChoosed == currentLabel.title,
title: Text(
currentLabel.title,
style: TextStyleConstants.titleStyle3,
),
secondary: const Icon(Icons.label_outline),
onChanged: (value) {
setState(() {
if (value == true) {
_labelChoosed = currentLabel.title;
} else {
_labelChoosed = '';
}
});
},
activeColor: ColorsConstant.blueColor,
);
},
itemCount: labelProvider.items.length,
),
child: const SizedBox.shrink(),
),
),
);
}
}
This is a flutter note app, and I am trying to label the notes.
Can anyone tell me how to select several options to label notes?
This code can create multiple options with dialoge, but cannot click them at once.
What I want is to create, select more than one checkbox, and save them to database.
Here is pick_label_screen.dart code.

Flutter setState not updating variable inside ExpansionPanelList

I'm trying to create a filter that update the selected option like the image using a ExpansionPanelList, something like this...
Goal
In my code I'm trying to update a subtitle Text from a property returned from the body of the same ListTile Widget which contain the RadioListTile Widget inside of ExpansionPanel Widget inside of ExpansionPanelList Widget.
The value I want is from another StatefulWidget class where the RadioListTile works perfectly, and the value is returned by a Callback to the class I need to use this variable named _orderByOptionSelected, but the variable I'm using is not updated even inside of the setState method.
Here is the class that contains the RadioListTile selection:
class ElementFilterOrderBy extends StatefulWidget {
const ElementFilterOrderBy({Key? key, required this.onChanged})
: super(key: key);
static const String best = 'best';
static const String reviews = 'reviews';
static const String price = 'price';
static const String location = 'location';
final Function(String) onChanged;
#override
State<ElementFilterOrderBy> createState() => _ElementFilterOrderByState();
}
class _ElementFilterOrderByState extends State<ElementFilterOrderBy> {
String _orderBySelection = ElementFilterOrderBy.best;
#override
Widget build(BuildContext context) {
return Column(
children: [
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.best),
value: ElementFilterOrderBy.best,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.reviews),
value: ElementFilterOrderBy.reviews,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.price),
value: ElementFilterOrderBy.price,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
RadioListTile<String>(
title: const Text(ElementFilterOrderBy.location),
value: ElementFilterOrderBy.location,
groupValue: _orderBySelection,
onChanged: (value) {
setState(() {
_orderBySelection = value!;
widget.onChanged(_orderBySelection);
});
},
activeColor: kPrimaryColor,
),
],
);
}
}
And this is my class where I'm trying to update the value returned:
class CustomBottomSheet extends StatefulWidget {
const CustomBottomSheet({Key? key}) : super(key: key);
#override
State<CustomBottomSheet> createState() => _CustomBottomSheetState();
}
class _CustomBottomSheetState extends State<CustomBottomSheet> {
late String _orderByOptionSelected;
late String _searchLocation;
late List<ItemExpansionPanel> _optionsFilter;
#override
void initState() {
super.initState();
_orderByOptionSelected = 'best';
_searchLocation = 'Actual Location';
_optionsFilter = [
ItemExpansionPanel(
headerValue: kFilterOptionOrderBy,
widgetBody: ElementFilterOrderBy(
onChanged: (selectedOption) {
setState(() {
_orderByOptionSelected = selectedOption;
});
},
),
optionSelected: _orderByOptionSelected,
),
ItemExpansionPanel(
headerValue: kFilterOptionLocation,
widgetBody: Container(),
optionSelected: _searchLocation,
),
];
}
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(kPaddingApp),
child: Column(
children: [
const Text(
kFilterTitle,
style: kTextStyleBoldBlackBig,
),
const SizedBox(
height: kMarginApp,
),
Expanded(
child: SingleChildScrollView(
child: _buildPanel(),
),
),
],
),
);
}
Widget _buildPanel() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_optionsFilter[index].isExpanded = !isExpanded;
});
},
children: _optionsFilter.map<ExpansionPanel>((ItemExpansionPanel item) {
return ExpansionPanel(
canTapOnHeader: true,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
subtitle: Text(
item.optionSelected,
style: const TextStyle(
color: kAccentColor,
),
),
);
},
body: item.widgetBody,
isExpanded: item.isExpanded,
);
}).toList(),
);
}
}
// stores ExpansionPanel state information
class ItemExpansionPanel {
ItemExpansionPanel({
required this.headerValue,
required this.widgetBody,
required this.optionSelected,
this.isExpanded = false,
});
final Widget widgetBody;
final String headerValue;
bool isExpanded;
String optionSelected;
}
Edit 1: Added more elements on the list to only change the ItemExpansionPanel selected
You should use _orderByOptionSelected as text value not item.optionSelected.
go to CustomBottomSheet then _buildPanel() widget then change it to this.
Widget _buildPanel() {
return ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
setState(() {
_optionsFilter[index].isExpanded = !isExpanded;
});
},
children: _optionsFilter.map<ExpansionPanel>((ItemExpansionPanel item) {
return ExpansionPanel(
canTapOnHeader: true,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: Text(item.headerValue),
subtitle: Text(
_orderByOptionSelected,
// item.optionSelected, <== DELETE This
style: const TextStyle(
color: Colors.purple,
),
),
);
},
body: item.widgetBody,
isExpanded: item.isExpanded,
);
}).toList(),
);
}
}

Flutter Checkbox did not work in AlertDialog

I am doing a checkbox in AlertDialog. I want the user to tick the checkbox before upload. My problem is if I set the variable as false the checkbox will not change to true or change back to false. Here is my code:
bool _isChecked = false;
List<String> _texts = [
"I have confirm the data is correct",
"I have agreed to terms and conditions.",
];
showConfirmationDialog(BuildContext context){
AlertDialog alert=AlertDialog(
title: Text('Confirmation'),
content:
ListView(
padding: EdgeInsets.all(8.0),
children: _texts.map((text) => CheckboxListTile(
title: Text(text),
value: _isChecked,
onChanged: (val) {
setState(() {
_isChecked = val;
});
},
)).toList(),
),
actions: <Widget>[
SizedBox(
width: 300,
child:RaisedButton(
color: Colors.blue,
onPressed: () => upload(context),
child:Text('Upload'),
)
)
],
);
showDialog(barrierDismissible: false,
context:context,
builder:(BuildContext context){
return alert;
},
);
}
Can anyone help me?
You need stateful widget because showDialog can't change state.
Following code help you to understand more.
class DeleteWidget extends StatefulWidget {
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
var currentSection;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: () {
showConfirmationDialog(context);
},
child: Text("data"),
),
),
),
);
}
showConfirmationDialog(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (BuildContext context) {
return CustomDialog();
},
);
}
}
class CustomDialog extends StatefulWidget {
#override
_CustomDialogState createState() => _CustomDialogState();
}
class _CustomDialogState extends State<CustomDialog> {
List<bool> _isChecked = [false, false];
bool canUpload = false;
List<String> _texts = [
"I have confirm the data is correct",
"I have agreed to terms and conditions.",
];
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Confirmation'),
content: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ListView(
shrinkWrap: true,
padding: EdgeInsets.all(8.0),
children: [
ListView.builder(
shrinkWrap: true,
itemCount: _texts.length,
itemBuilder: (_, index) {
return CheckboxListTile(
title: Text(_texts[index]),
value: _isChecked[index],
onChanged: (val) {
setState(() {
_isChecked[index] = val;
canUpload = true;
for (var item in _isChecked) {
if (item == false) {
canUpload = false;
}
}
});
},
);
},
),
]),
),
],
),
),
actions: <Widget>[
SizedBox(
width: 300,
child: RaisedButton(
color: Colors.blue,
onPressed: canUpload
? () {
print("upload");
}
: null,
child: Text('Upload'),
))
],
);
}
}
bool _isChecked = false;
List<String> _texts = [
"I have confirm the data is correct",
"I have agreed to terms and conditions.",
];
showConfirmationDialog(BuildContext context) {
showDialog(
barrierDismissible: false,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text('Confirmation'),
content: ListView(
padding: EdgeInsets.all(8.0),
children: _texts
.map((text) => CheckboxListTile(
activeColor: Colors.pink,
title: Text(text),
value: _isChecked,
onChanged: (val) {
setState(() {
_isChecked = val;
});
},
))
.toList(),
),
actions: <Widget>[
SizedBox(
width: 300,
child: RaisedButton(
color: Colors.blue,
onPressed: () => () {
debugPrint("upload image");
},
child: Text('Upload'),
))
],
);
},
);
},
);
}
inside the class add
void isChecked(bool newValue) => setState(() {
_isChecked = newValue;
if (_isChecked) {
//something here
} else {
// change value here
}
});
//in checkbox
onChanged: (val) {
isChecked(val);
}
I hope it's working
That is because of the alert dialog state.
If we are using a StatefullBuilder in a showModelBottomSheet, The context of the page will work fine from the showModelBottomSheet. But the Aler dialog will not change while setState is calling.
This is because the AlerDialog needs a stateful class.
Note: the State of showModelBottomSheet and an alert Dialog are not equal.
Hope the following code will help.
ElevatedButton(
onPressed: () => showDialog(
context: context,
builder: (BuildContext contex) {
return EnterUPIBOX();
}),
child: Text('UPI ID'),
),
//The alert dialog will inherit from statefulWidget.
class EnterUPIBOX extends StatefulWidget {
const EnterUPIBOX({
Key? key,
}) : super(key: key);
#override
State<EnterUPIBOX> createState() => _EnterUPIBOXState();
}
class _EnterUPIBOXState extends State<EnterUPIBOX> {
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('CheckBox'),
actions:[ Checkbox(
value: check,
onChanged: (value) {
setState(() {
check = !check;
});
}
),
]
),
}
}

Flutter rebuild parent widget

I need help. I have a Dropdown widget in LanguageDropdown class, where the user can select the language. And the widget is inside a settings page widget in Settings class. The language changes on other pages, but not on current one. How can I rebuild that specific page, so the language changes on this one also?
See the code below
import 'package:jptapp/features/settings/change_language/app_localization.dart';
class LanguageDropDown extends StatefulWidget {
#override
_LanguageDropDownState createState() {
return _LanguageDropDownState();
}
}
class _LanguageDropDownState extends State<LanguageDropDown> {
String _value = allTranslations.currentLanguage;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
items: [
DropdownMenuItem<String>(
child: Text('English'),
value: 'en',
),
DropdownMenuItem<String>(
child: Text('Magyar'),
value: 'hu',
),
DropdownMenuItem<String>(
child: Text('Srpski'),
value: 'rs',
),
],
onChanged: (String value) {
setState(() async{
_value = value;
await allTranslations.setNewLanguage(_value);
});
},
hint: Text(_value),
value: _value,
);
}
}
import 'package:jptapp/core/constants/colors.dart';
import 'package:jptapp/features/settings/change_language/app_localization.dart';
import 'package:jptapp/features/settings/widgets/widgets.dart';
class Settings extends StatefulWidget {
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: MyColors.appBarColor,
title: Text(
allTranslations.text('settings'),
),
),
body: ListView(
children: ListTile.divideTiles(
context: context,
tiles: [
ListTile(
trailing: ThemeChangerAnimationButton(),
title: Text(
allTranslations.text('darkmode'),
),
),
ListTile(
trailing: LanguageDropDown(),
title: Text(
allTranslations.text('language'),
),
),
],
).toList(),
),
);
}
}
I'm not sure this will work but try this:
import 'package:flutter/material.dart';
import 'package:jptapp/features/settings/change_language/app_localization.dart';
class LanguageDropDown extends StatefulWidget {
#override
_LanguageDropDownState createState() {
return _LanguageDropDownState();
}
}
class _LanguageDropDownState extends State<LanguageDropDown> {
String _value = allTranslations.currentLanguage;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
items: [
DropdownMenuItem<String>(
child: Text('English'),
value: 'en',
),
DropdownMenuItem<String>(
child: Text('Magyar'),
value: 'hu',
),
DropdownMenuItem<String>(
child: Text('Srpski'),
value: 'rs',
),
],
onChanged: (String value) {
setState(() async {
_value = value;
await allTranslations.setNewLanguage(_value);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Settings()
));
});
},
hint: Text(_value),
value: _value,
);
}
}