How to change a variable when changing DropDownButton - flutter

I have 3 variables containing text that I can pass to message.
String easyDrop = 'All ok';
String mediumDrop = '1 problem';
String hardDrop = 'All not ok';
message: easyDrop ,
I would like to change them depending on the state of my DropDownButton. How can I do this?
import 'package:flutter/material.dart';
class DropDownButtonDifficultySettingsWidget extends StatefulWidget {
DropDownButtonDifficultySettingsWidget({Key? key}) : super(key: key);
#override
State<DropDownButtonDifficultySettingsWidget> createState() => _DropDownButtonDifficultySettingsState();
}
class _DropDownButtonDifficultySettingsState extends State<DropDownButtonDifficultySettingsWidget> {
String dropdownValue = 'Medium';
#override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
splashColor: Colors.blue.withOpacity(0.4),),
child: DropdownButton<String>(
value: dropdownValue,
elevation: 8,
alignment: Alignment.centerRight,
iconDisabledColor: Colors.blue,
iconEnabledColor: Colors.blue,
underline: Container(
height: 0,
),
style: const TextStyle(color: Colors.blue, fontWeight: FontWeight.w500, ),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Easy', 'Medium', 'Hard']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
);
}
}

You can include a callback method to get selected item from DropDownButtonDifficultySettingsWidget
class DropDownButtonDifficultySettingsWidget extends StatefulWidget {
final Function(String? selectedValue) callback;
const DropDownButtonDifficultySettingsWidget(
{Key? key, required this.callback})
: super(key: key);
#override
State<DropDownButtonDifficultySettingsWidget> createState() =>
_DropDownButtonDifficultySettingsState();
}
And on changed
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
widget.callback(dropdownValue); //this
},
Now when ever you use DropDownButtonDifficultySettingsWidget you will get selected value on callback
DropDownButtonDifficultySettingsWidget(
callback: (selectedValue) {
print(selectedValue);
/// do the thing you like to have
},
),

Related

how to use 'dropdownbutton'?

There was an error when I inserted the DropdownButton code into my code.
Outside the code containing the body, they declared it as a class, and when I put the class declared in the code, an error message appeared as below.
'_AssertionError ('package:flutter/src/material/dropdown.dart': Failed assertion: line 890 pos 15: 'items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1': There should be exactly one item with [DropdownButton]'s value: sex.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value)'
Below is my code.
.....
....
onChanged: (_) {
setState(() {});
}
),
SelectButton(),
],
),
),
class SelectButtonState extends State<SelectButton> {
final List<String> _valueList = ['M', 'F'];
String _selectedValue = 'sex';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: _selectedValue,
items: _valueList.map((value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
_selectedValue = value!;
});
},
);
}
}
class SelectButton extends StatefulWidget {
const SelectButton ({Key? key}) : super(key: key);
#override
State<SelectButton> createState() => SelectButtonState();
}
I want to make sex select button...
Your _valueList contains ['M', 'F'] only and you are creating a DropDownButton out of it. When compiler finds initial value as "Select Sex" which is not available in the _valueList array, you get NULL error.
Solution -> Use 'Select Sex' as dropdown hint. Keep _selectedValue as null in intial declaration so that hint will be displayed.
Setting _selectedValue as null with null check:
String? _selectedValue = null;
Try this one
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: aa(),
),
),
);
}
}
class aa extends StatefulWidget {
const aa({Key? key}) : super(key: key);
#override
State<aa> createState() => _aaState();
}
class _aaState extends State<aa> {
String selectedNumber = '1';
List numberList = ['1', '2', '3', '4'];
#override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(8)),
child: DropdownButton(
style: const TextStyle(color: Colors.black),
dropdownColor: Colors.white,
underline: Container(),
value: selectedNumber,
onChanged: (value) {
selectedNumber = value! as String;
setState(() {});
},
items: numberList.map((itemone) {
return DropdownMenuItem(
value: itemone,
child: Center(
child: Text(
itemone,
style: const TextStyle(
color: Colors.black, fontWeight: FontWeight.w700),
),
),
);
}).toList(),
),
);
}
}

The Passed Value isn't updating in Flutter

I pass a String named "sourceCity" and "destinationCity" to my DropDown widget but when the DropDownValue changed, the String value doesn't automatically changed The new value is only updated inside the DropDownWidget.
add_journey.dart:
class AddJourneyPage extends StatefulWidget {
#override
_AddJourneyPage createState() => _AddJourneyPage();
}
class _AddJourneyPage extends State {
String sourceCity = 'Source City';
String destenationCity = 'Destination City';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Dropdown(sourceCity, Colors.grey.shade200, Colors.black, Colors.grey.shade700, 18.0, items: ['Aleppo','Damascus','Homs','Hama']),
Dropdown(destenationCity, Colors.grey.shade200, Colors.black, Colors.grey.shade700, 18.0, items: ['Aleppo','Damascus','Homs','Hama']),
ElevatedButton(
onPressed: () {
print(sourceCity);
//it's printing: Source City
print(destenationCity);
//it's printing: Destination City
},
child: const Text('Add Journey'))
],
),
);
}
}
DropDown.dart:
class Dropdown extends StatefulWidget{
final List<String> items;
late String hint;
Color backgroundColor;
Color iconColor;
Color textColor;
double fontSize;
Dropdown(this.hint, this.backgroundColor, this.iconColor, this.textColor, this.fontSize, {super.key, required this.items});
#override
State<StatefulWidget> createState() => DropdownState();
}
class DropdownState extends State<Dropdown>{
String hint="";
#override
void initState() {
hint = widget.hint;
}
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
canvasColor: widget.backgroundColor,
),
child: DropdownButton<String>(
hint: Text(hint, style: TextStyle(color: widget.textColor),),
icon: Icon(Icons.arrow_drop_down, color: widget.iconColor),
elevation: 16,
style: TextStyle(color: widget.textColor),
underline: Container(
height: 2,
width: 50,
color: Colors.white,
),
onChanged: (String? newValue) {
setState(() {
hint = newValue!;
});
},
items: widget.items
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(fontSize: widget.fontSize),),
);
}).toList(),
)
);
}
}
How Could I update the passed String when it changed?
Because you use the widget in two different domains, to update the parent widget, you need to use the event when the values of the dropdown list change.
First, define a global function, then
Change your code to the following
typedef CallbackDropDown = void Function(
String newValue);
class AddJourneyPage extends StatefulWidget {
#override
_AddJourneyPage createState() => _AddJourneyPage();
}
class _AddJourneyPage extends State {
String sourceCity = 'Source City';
String destenationCity = 'Destination City';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Dropdown(sourceCity, Colors.grey.shade200, Colors.black, Colors.grey.shade700, 18.0, items: ['Aleppo','Damascus','Homs','Hama'],
callbackDropDown:(newValue){
sourceCity = newValue;
}),
Dropdown(destenationCity, Colors.grey.shade200, Colors.black, Colors.grey.shade700, 18.0, items: ['Aleppo','Damascus','Homs','Hama'],
callbackDropDown:(newValue){
destenationCity = newValue;
}),
ElevatedButton(
onPressed: () {
print(sourceCity);
//it's printing: Source City
print(destenationCity);
//it's printing: Destination City
},
child: const Text('Add Journey'))
],
),
);
}
}
class Dropdown extends StatefulWidget{
final List<String> items;
final CallbackDropDown callbackDropDown;
late String hint;
Color backgroundColor;
Color iconColor;
Color textColor;
double fontSize;
Dropdown(this.hint, this.backgroundColor, this.iconColor, this.textColor, this.fontSize, {super.key, required this.items, required this.callbackDropDown});
#override
State<StatefulWidget> createState() => DropdownState();
}
class DropdownState extends State<Dropdown>{
String hint="";
#override
void initState() {
hint = widget.hint;
}
#override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
canvasColor: widget.backgroundColor,
),
child: DropdownButton<String>(
hint: Text(hint, style: TextStyle(color: widget.textColor),),
icon: Icon(Icons.arrow_drop_down, color: widget.iconColor),
elevation: 16,
style: TextStyle(color: widget.textColor),
underline: Container(
height: 2,
width: 50,
color: Colors.white,
),
onChanged: (String? newValue) {
setState(() {
hint = newValue!;
widget.callbackDropDown(newValue!);
});
},
items: widget.items
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style: TextStyle(fontSize: widget.fontSize),),
);
}).toList(),
)
);
}
}
Change Dropdown to accept function in constructor:
class Dropdown extends StatefulWidget{
final List<String> items;
late String hint;
Color backgroundColor;
Color iconColor;
Color textColor;
double fontSize;
fined Function(String?) onChange;
Dropdown(this.hint, this.backgroundColor, this.iconColor, this.textColor, this.fontSize, {super.key, required this.items, required this.onChange});
#override
State<StatefulWidget> createState() => DropdownState();
}
and in your DropdownButton's onChange do this:
onChanged: (String? newValue) {
widge.onChange(newValue)
},
and finally last change in your AddJourneyPage class:
Dropdown(sourceCity, Colors.grey.shade200, Colors.black, Colors.grey.shade700, 18.0, items: ['Aleppo','Damascus','Homs','Hama'], onChange:(value){
setState(() {
sourceCity = value ?? '';
});
}),

Flutter DropDownButton hide button (only the button) when menu is open

It is possible to hide the DropDownButton (only the button) when menu is open?
I need the background of the menu to be transparent, when I do it I find that the button and the menu are misaligned and also that you can see the text of the button in the background (see image)
this is my problem
I need that once the menu is opened it looks more or less like the following image
this is a fake recreation of the menu i need
If somebody know any answer that is an alternative to using the DropDownButton, made with containers, some packages or whatever, and that can do the same functions, that would be welcome too.
Actual Code
class DropDownMenuAppBar extends StatefulWidget {
const DropDownMenuAppBar({
Key? key,
}) : super(key: key);
#override
State<DropDownMenuAppBar> createState() => _DropDownMenuAppBarState();
}
class _DropDownMenuAppBarState extends State<DropDownMenuAppBar> {
String dropdownValue = 'Today';
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<String>(
alignment: Alignment.bottomLeft,
dropdownColor: Colors.white10,
value: dropdownValue,
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
elevation: 0,
style: AppTextStyle.boldLarge.copyWith(color: Colors.white),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Today', 'All Habits']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
);
}
}
Thanks
You can add a focus to the dropdown to know if its open or closed and set the visibility of the selected item like
class DropDownMenuAppBar extends StatefulWidget {
const DropDownMenuAppBar({Key? key}) : super(key: key);
#override
State<DropDownMenuAppBar> createState() => _DropDownMenuAppBarState();
}
class _DropDownMenuAppBarState extends State<DropDownMenuAppBar> {
String dropdownValue = 'Today';
late FocusNode _focus;
#override
void initState() {
super.initState();
_focus = FocusNode();
_focus.addListener(_handleFocusChange);
}
void _handleFocusChange() {
if (_focus.hasFocus != _focused) {
setState(() {
_focused = _focus.hasFocus;
});
}
print(!_focused);
}
bool _focused = true;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
alignment: Alignment.bottomLeft,
dropdownColor: Colors.white10,
value: dropdownValue,
focusNode: _focus,
icon: Visibility(
visible: _focused,
child: Icon(
Icons.arrow_drop_down,
color: Colors.white,
),
),
elevation: 0,
style: const TextStyle(color: Colors.deepPurple),
selectedItemBuilder: (BuildContext context) {
return <String>['Today', 'All Habits']
.map((String value) {
return Visibility(
visible: _focused,
child: DropdownMenuItem<String>(
value: value,
child: Text(value),
),
);
}).toList();
},
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
if (_focus.hasFocus) {
_focus.unfocus();
} else {
_focus.requestFocus();
}
},
items: <String>['Today', 'All Habits']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}

is there a way to get a value from a class to outside the class?

i have a drop down class which has in its build widget a dropdown widget which on changed it set state the value of selected option to a value in the class.
i used the class in my screen and I don't find a way to use the selected value :(
here is my code
class ServiceCategoryDropDownList extends StatefulWidget {
const ServiceCategoryDropDownList({ Key key }) : super(key: key);
#override
State<ServiceCategoryDropDownList> createState() => _ServiceCategoryDropDownListState();
}
class _ServiceCategoryDropDownListState extends State<ServiceCategoryDropDownList> {
String value;
#override
Widget build(BuildContext context) {
final servicecategories = Provider.of<List<ServiceCategory>> (context);
return Container(
child:
DropdownButtonHideUnderline(
child:DropdownButton<String>(
value: value,
isExpanded: true,
icon: Icon(Icons.arrow_drop_down),
iconSize: 36,
items: servicecategories.map((item){
print(item.serviceCategoryName);
return DropdownMenuItem<String>(
value: item.serviceCategoryID,
child: Text(item.serviceCategoryName,style: TextStyle(color: Colors.black,fontSize: 14),),
);
}).toList(),
onChanged: (value) {
setState(() =>this.value = value);
print(value);
}
),
)
);
}
}
and here where i call for the class dropdown
class ServicesContent extends StatefulWidget {
const ServicesContent({ Key key }) : super(key: key);
#override
State<ServicesContent> createState() => _ServicesContentState();
}
class _ServicesContentState extends State<ServicesContent> {
#override
Widget build(BuildContext context) {
return
Scaffold(
body: StreamProvider<List<ServiceCategory>>.value(
value: Database().serviceCategory,
initialData: [],
child: Column(
children: [
ServiceCategoryDropDownList(),
ElevatedButton(
child: Text("Add"),
onPressed: () => print(ServiceCategoryDropDownList().value)
)
])));}
}
please any one help me :(
If I understood correctly your problem, do you need to get the selected value out of the dropdown widget? if it is yes, check this explanation and the code
Accept a callback property in your widget constructor
After that, call your callback passing the selected value
Now, you can read the selected value in your ServicesContent as the widget callback parameter
Dropdown widget
class ServiceCategoryDropDownList extends StatefulWidget {
// 1. Accept a callback property in you widget constructor
final Function onChanged;
const ServiceCategoryDropDownList({
Key? key,
required this.onChanged,
}) : super(key: key);
#override
State<ServiceCategoryDropDownList> createState() =>
_ServiceCategoryDropDownListState();
}
class _ServiceCategoryDropDownListState
extends State<ServiceCategoryDropDownList> {
String? value = '';
#override
Widget build(BuildContext context) {
final servicecategories = Provider.of<List<ServiceCategory>>(context);
return Container(
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: value,
isExpanded: true,
icon: const Icon(Icons.arrow_drop_down),
iconSize: 36,
items: servicecategories.map((item) {
print(item.serviceCategoryName);
return DropdownMenuItem<String>(
value: item.serviceCategoryID,
child: Text(
item.serviceCategoryName,
style: const TextStyle(
color: Colors.black,
fontSize: 14,
),
),
);
}).toList(),
onChanged: (value) {
setState(() => this.value = value);
// 2. Call your callback passing the selected value
widget.onChanged(value);
},
),
),
);
}
}
ServiceContent
class _ServicesContentState extends State<ServicesContent> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamProvider<List<ServiceCategory>>.value(
value: Database().serviceCategory,
initialData: [],
child: Column(
children: [
ServiceCategoryDropDownList(
onChanged: (String value) {
// 3. Now you can read the selected value here and make cool things
print(value);
},
),
ElevatedButton(
child: Text("Add"),
// 4. You can't do this ServiceCategoryDropDownList().value, so delete it and get the value at point 3
onPressed: () => print(ServiceCategoryDropDownList().value),
)
],
),
),
);
}
}
Let me know if you can solve your issue :)

Flutter: Get data back from StatefulWidget child class to parent

I'm new to flutter.
I have a page (Stateful Widget) in the app with a lot of widgets in a column. To improve the code readability, I took some widgets, and made them into seperate classes. For example, I made my dropdownmenu widget, into its only class, like this:
class DropDownMenuWidget extends StatefulWidget {
DropDownMenuWidget({Key? key}) : super(key: key);
#override
_DropDownMenuWidgetState createState() => _DropDownMenuWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _DropDownMenuWidgetState extends State<DropDownMenuWidget> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: MASLULIM
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
Now, in the parent class, I display the widget like this:
DropDownMenuWidget(),
However, the problem is, when the user clicks on a item, I can only retrieve that value from the DropDownMenu class, and there the setState() method is called. However, I need to read this value in the parent class. How can I get it there?
Thanks
Instead of creating your dropdownValue variable in your Widget, you can get it from the parent Widget as following with the help of ValueNotifier
class DropDownMenuWidget extends StatefulWidget {
ValueNotifier dropdownValueNotifier;
DropDownMenuWidget(this.dropdownValueNotifier, {Key key}) : super(key: key);
#override
_DropDownMenuWidgetState createState() => _DropDownMenuWidgetState();
}
class _DropDownMenuWidgetState extends State<DropDownMenuWidget> {
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: widget.dropdownValueNotifier,
builder: (context, dropdownValue, _) {
return DropdownButton<String>(
value: dropdownValue,
// ...
onChanged: (String newValue) {
// simply change the value. You dont need setState anymore
widget.dropdownValueNotifier.value = newValue;
},
// ...
);
},
);
}
}
In the parent Widget, create the variable and pass it like this
ValueNotifier dropdownValueNotifier = ValueNotifier('One');
// ...
DropDownMenuWidget(dropdownValueNotifier),
In this case, you can use typedef
First in a separate DrobDown menu you can create the following icon outside of the class:
typedef OnItemSelectedDropDown = Function (String value);
Now you can apply this thing as follows :
class DropDownMenuWidget extends StatefulWidget {
final OnItemSelectedDropDown onItemSelected ;
DropDownMenuWidget({Key? key}) : super(key: key);
#override
_DropDownMenuWidgetState createState() => _DropDownMenuWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _DropDownMenuWidgetState extends State<DropDownMenuWidget> {
String dropdownValue = 'One';
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(
color: Colors.black,
fontSize: 20,
),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (String value) {
//This line return Value
widget.onItemSelected.call(value);
},
items: MASLULIM
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
When calling the class DropDownMenuWidget, it is called as follows on another screen:
String dropdownValue ;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('DropDown Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'New Value DropDown : $dropdownValue',
),
DropDownMenuWidget(
onItemSelected :(newValue){
setState(() {
dropdownValue = newValue ;
});
}
),
],
),
),
);
}