Related
While coding an app i realized, that if you use a hint: with the DropdownButton and a value you only see the value. After some research and trying to work my way around it i finally found a solution. Idk if this is helpful or not but i wanted to share this with you and maybe it does help you in your own project. But without further ado here is the "not functional code":
import 'package:flutter/material.dart';
void main() => runApp(const ButtonClass());
class ButtonClass extends StatefulWidget {
const ButtonClass({Key? key}) : super(key: key);
#override
State<ButtonClass> createState() => _ButtonClassState();
}
class _ButtonClassState extends State<ButtonClass> {
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("One"), value: "Option1"),
const DropdownMenuItem(child: Text("Two"), value: "Option2"),
const DropdownMenuItem(
child: Text("Three"),
value: "Option3",
),
const DropdownMenuItem(
child: Text("Four"),
value: "Option4",
),
const DropdownMenuItem(
child: Text("Five"),
value: "Option5",
),
];
return menuItems;
}
String selectedValue = "Option1";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: 200.0,
height: 200.0,
child: DropdownButtonHideUnderline(
child: DropdownButton(
isExpanded: true,
hint: const Center(
child: FittedBox(
fit: BoxFit.contain,
child: Text(
"Displayed Text",
style: TextStyle(
color: Colors.black,
fontSize: 30.0,
fontFamily: 'Arial',
),
),
),
),
items: dropdownItems,
value: selectedValue,
onChanged: (String? newValue) {
setState(() {
selectedValue = newValue!;
});
},
),
),
),
),
),
);
}
}
And here is the solution:
Change the
String selectedValue = "Option1";
to (example)
String? _selectedColor;
and also change
value: selectedValue,
onChanged: (String? newValue) {
setState(() {
selectedValue = newValue!;
});
},
to
value: _selectedColor,
onChanged: (String? newValue) {
setState(() {
_selectedColor= newValue!;
});
},
Here is the full main.dart file:
import 'package:flutter/material.dart';
void main() => runApp(const ButtonClass());
class ButtonClass extends StatefulWidget {
const ButtonClass({Key? key}) : super(key: key);
#override
State<ButtonClass> createState() => _ButtonClassState();
}
class _ButtonClassState extends State<ButtonClass> {
List<DropdownMenuItem<String>> get dropdownItems {
List<DropdownMenuItem<String>> menuItems = [
const DropdownMenuItem(child: Text("One"), value: "Option1"),
const DropdownMenuItem(child: Text("Two"), value: "Option2"),
const DropdownMenuItem(
child: Text("Three"),
value: "Option3",
),
const DropdownMenuItem(
child: Text("Four"),
value: "Option4",
),
const DropdownMenuItem(
child: Text("Five"),
value: "Option5",
),
];
return menuItems;
}
String? _selectedColor;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Container(
width: 200.0,
height: 200.0,
child: DropdownButtonHideUnderline(
child: DropdownButton(
isExpanded: true,
hint: const Center(
child: FittedBox(
fit: BoxFit.contain,
child: Text(
"Displayed Text",
style: TextStyle(
color: Colors.black,
fontSize: 30.0,
fontFamily: 'Arial',
),
),
),
),
items: dropdownItems,
value: _selectedColor,
onChanged: (String? newValue) {
setState(() {
_selectedColor = newValue!;
});
},
),
),
),
),
),
);
}
}
I want to be able to call either one of two different widgets based on the result of an if statement.
I am able to get a Text Widget to show two different answers based on the result of the first dropdown box, but I am not able to call another widget the same way ...
import 'package:flutter/material.dart';
class FilterPage extends StatefulWidget {
const FilterPage({Key? key}) : super(key: key);
#override
State<FilterPage> createState() => _FilterPageState();
}
class _FilterPageState extends State<FilterPage> {
String firstDropdownValue = 'Option A';
String secondDropdownValue = 'Option B';
#override
void dispose() {
super.dispose();
}
static const String _title = 'Filter Code Sample';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: EdgeInsets.all(4),
),
],
title: Column(
children: [
Text(
'Filter',
style: TextStyle(fontSize: 22, color: Colors.amberAccent),
),
],
),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'What are the Options ?',
style: TextStyle(fontSize: 16, color: Colors.white),
),
/*
* This is the first dropdown that is called, This dropdown provides
* two possible options,
* */
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: firstDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
firstDropdownValue = newValue!;
});
},
items: <String>['Option A', 'Option B']
.map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
/*
* So this is the if Statement that works based upon the first
* drop down box's answer...
*
* The problem is I can not get the system to call another Drop down Butten
* Widget based on the answer of the first DropdownBox,
* However, i can get a Text Widget to print...
* */
Text((() {
if (firstDropdownValue == 'Option A') {
return "Option is Option A";
}
return "Option is Option B";
})()),
/*
* This is the second dropbox that I would like to be called if the
* previous statement is met instead of the Text Widget above...
*(DROPBOX 2)
* */
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: secondDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
secondDropdownValue = newValue!;
});
},
items: <String>[
'Option 1',
'Option 2',
'Option 3',
].map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
/*
* This is also drop box two, but this dropbox with these different
* options should be called when the if statement is not met ..
*(DROPBOX 2)
* */
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: secondDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
secondDropdownValue = newValue!;
});
},
items: <String>[
'Option 4',
'Option 5',
'Option 6',
].map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
],
),
),
),
);
}
}
The flow diagram is maybe easier to get the whole picture from.
You can use a if(condition)...[widgetA] else...[WidgetB] structure:
class _FilterPageState extends State<FilterPage> {
String firstDropdownValue = 'Option A';
String secondDropdownValue = 'Option B';
#override
void dispose() {
super.dispose();
}
static const String _title = 'Filter Code Sample';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: EdgeInsets.all(4),
),
],
title: Column(
children: [
Text(
'Filter',
style: TextStyle(fontSize: 22, color: Colors.amberAccent),
),
],
),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'What are the Options ?',
style: TextStyle(fontSize: 16, color: Colors.white),
),
/*
* This is the first dropdown that is called, This dropdown provides
* two possible options,
* */
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: firstDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
firstDropdownValue = newValue!;
});
},
items: <String>['Option A', 'Option B']
.map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
if(firstDropDownValue)...[
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: secondDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
secondDropdownValue = newValue!;
});
},
items: <String>[
'Option 1',
'Option 2',
'Option 3',
].map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
] else ...[
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: secondDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
secondDropdownValue = newValue!;
});
},
items: <String>[
'Option 4',
'Option 5',
'Option 6',
].map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
]
],
),
),
),
);
}
}
Not the best approach for this probably, but you can use a Builder widget, inside it put your if statement and if you don't want to show your widget there, return SizedBox.shrink() instead (don't forget to setState((){}) so that the widget containing them both rebuilds them. But a better answer may be using Bloc or some other state management package. Or even ValueNotifiers and ValueListenableBuilders to update your UI only where it's needed.
One solution here is to use an inline if condition like this:
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: firstDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
firstDropdownValue = newValue!;
});
},
items: <String>['Option A', 'Option B']
.map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
firstDropdownValue == 'Option A'?
DropdownButton(/*first dropdown*/) :
DropdownButton(/*second dropdown*/),
The other is creating a Widget variable at the beginning of the build method and giving it a value before returning the Scaffold, then put the created variable as one of the Column children:
#override
Widget build(BuildContext context) {
Widget bottomDropdown;
if (firstDropdownValue == 'Option A'){
bottomDropdown = DropdownButton(/*first dropdown*/);
} else {
bottomDropdown = DropdownButton(/*second dropdown*/);
}
return Scaffold(
appBar: AppBar(
//...other code
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: firstDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
firstDropdownValue = newValue!;
});
},
items: <String>['Option A', 'Option B']
.map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
bottomDropdown,
You can use the Dart short if form: if ? then : else.
Since the only thing that changes are the dropdown options, you can dynamically change them without the need to use another widget.
import 'package:flutter/material.dart';
class FilterPage extends StatefulWidget {
const FilterPage({Key? key}) : super(key: key);
#override
State<FilterPage> createState() => _FilterPageState();
}
class _FilterPageState extends State<FilterPage> {
String firstDropdownValue = '';
String secondDropdownValue = 'Option B';
#override
void dispose() {
super.dispose();
}
static const String _title = 'Filter Code Sample';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: EdgeInsets.all(4),
),
],
title: Column(
children: [
Text(
'Filter',
style: TextStyle(fontSize: 22, color: Colors.amberAccent),
),
],
),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text(
'What are the Options ?',
style: TextStyle(fontSize: 16, color: Colors.white),
),
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: firstDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
firstDropdownValue = newValue!;
});
},
items: <String>['Option A', 'Option B']
.map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
if(firstDropdownValue.isNotEmpty)
DropdownButton<String>(
dropdownColor: Colors.blueGrey.shade700,
value: secondDropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.blueGrey.shade900,
),
onChanged: (String? newValue) {
setState(() {
secondDropdownValue = newValue!;
});
},
items: <String>[
firstDropdownValue == 'Option A' ?
...['Option 1',
'Option 2',
'Option 3'] : ...['Option 5',
'Option 6',
'Option 7']
].map<DropdownMenuItem<String>>((String doaValue) {
return DropdownMenuItem<String>(
value: doaValue,
child: Text(doaValue),
);
}).toList(),
),
],
),
),
),
);
}
}
I am working on a dropdown and I have the values in a variable, but I can't get the selected value to be shown, instead is always a static value on.
So far I did this:
class DropDownWidget extends State {
String dropdownValue = 'Activities';
String holder = '';
List<String> postType = ['Activities', 'Sell/buy', 'New Friends', 'City Recommendations', 'Post'];
void getDropDownItem() {
setState(() {
holder = dropdownValue;
});
}
#override
Widget build(BuildContext context) {
return Container(
height: 100,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/blue.png'),
fit: BoxFit.cover,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Container(
child: DropdownButton<String>(
value: dropdownValue,
icon: Icon(
Icons.keyboard_arrow_down_outlined,
color: TheBaseColors.lightRed,
),
iconSize: 30,
elevation: 16,
style: TextStyle(color: TheBaseColors.lightRed, fontSize: 18),
onChanged: (String data) {
setState(() {
dropdownValue = data;
});
switch (data) {
case 'Activities':
Navigator.push(
context,
MaterialPageRoute(builder: (context) => CreateActivity()),
);
break;
case 'Sell/buy':
Navigator.push(
context,
}
},
items: postType.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
],
),
);
}
}
I have saved the dropdow value in a variable and then set it in the SetState, but how to show the selected item each time?
Try This
String dropdownValue = 'Activities';
DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['Activities', 'Sell/buy', 'New Friends', 'City Recommendations', 'Post']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
Please someone help me. I created DropdownButton with map key and values on StatefullWidget
I am using a map here because I am sending the selected key of value to the parent widget.
I am getting error:
There should be exactly one item with [DropdownButton]'s value: Все подряд.
Either zero or 2 or more [DropdownMenuItem]s were detected with the same value
As soon as I remove the value in the DropdownButton Widget, no error is thrown
title: Container(
child: DropdownButtonHideUnderline(
child: DropdownButton(
**value: _options[dropDownValue]**,
But in this case there is no value for the dropdown button
Full Code:
class _MyAppBarState extends State<MyAppBar> {
String dropDownValue = 'created';
Map<String, String> _options = {
'rating': 'Лучшие',
'seen': 'Интересные',
'created': 'Все подряд',
};
#override
Widget build(BuildContext context) {
return AppBar(
backgroundColor: Color(0xff62858F),
elevation: 20,
shadowColor: Colors.grey,
title: Container(
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: _options[dropDownValue],
icon: const Icon(
Icons.arrow_drop_down,
color: Colors.white,
size: 25,
),
iconSize: 24,
elevation: 2,
selectedItemBuilder: (BuildContext context) {
return _options
.map((String key, String value) {
print(key);
print(value);
return MapEntry(
key,
Padding(
padding: EdgeInsets.symmetric(vertical: 15),
child: Text(
value,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 17,
),
),
),
);
})
.values
.toList();
},
//underline:,
items: _options
.map(
(String key, String value) {
return MapEntry(
key,
DropdownMenuItem<String>(
value: key,
child: Text(
value,
style: TextStyle(
color: Colors.black,
),
),
),
);
},
)
.values
.toList(),
onChanged: (String newValue) {
widget.sortBy(newValue);
setState(() {
dropDownValue = newValue;
});
},
),
),
),
actions: <Widget>[
IconButton(
icon: Icon(
Icons.search,
size: 25,
),
onPressed: () {
Navigator.of(context).pushNamed(SearchPage.tag);
},
)
],
);
}
}
change your DropdownButton value property to just dropDownValue only (not _options[dropDownValue]).
Because in DropdownMenuItem you are setting value as the key of the MapEntry.
I'm trying to make a custom function which returns a dropdown menu. The function takes 3 arguments: menuTitle, selectedValue and list of Strings.
List<String> cities= ['Rome', 'London', 'Paris'];
String selectedCity;
String title='Select a city';
Widget _buildDropdown(
String menuTitle, String selectedItem, List<String> list) {
return Container(
width: MediaQuery.of(context).size.width * 0.8,
margin: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]),
borderRadius: BorderRadius.circular(10.0)),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButton<String>(
isExpanded: true,
hint: Text(
menuTitle.toUpperCase(),
style: GoogleFonts.lato(
textStyle: TextStyle(
color: Colors.grey[500],
fontWeight: FontWeight.bold,
)),
),
value: selectedItem,
items: list.map((String val) {
return DropdownMenuItem(
value: val,
child: Text(
val.toUpperCase(),
style: GoogleFonts.lato(
textStyle: TextStyle(
color: Colors.grey[500],
)),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
selectedItem = value;
});
},
),
),
),
);
}
Dropdown menu shows all options available, but after choosing one, I can only see the hint text and not the selected value. I would appreciate any help.
For your dropdown to reflect on your selectedValue, it should be a part of the state. So follow as shown in the code below. I have tested this code and its working for me.
class _MyAppState extends State<MyApp> {
List<String> cities= ['Rome', 'London', 'Paris'];
//Initialize your selectedItem to selectedCity
//default selectedCity
String selectedCity='Rome';
String title='Select a city';
#override
Widget build(BuildContext context) {
Color color=Colors.blueAccent;
int x=color.value;
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[_buildDropdown("Title","",cities)],
),
),
);
}
Widget _buildDropdown(
String menuTitle, String selectedItem, List<String> list) {
return Container(
margin: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]),
borderRadius: BorderRadius.circular(10.0)),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButton<String>(
isExpanded: true,
hint: Text(
menuTitle.toUpperCase(),
),
value: selectedCity,
items: list.map((String val) {
return DropdownMenuItem(
value: val,
child: Text(
val.toUpperCase(),
),
);
}).toList(),
onChanged: (String value) {
setState(() {
selectedCity = value;
});
},
),
),
),
);
}
}