Selected item not showing in DropdownButton in Flutter - flutter

I need your help. I'm making a DropdownButton and I'm facing the following problem - I can't see the items that are selected in the DropdownMenuItems. I do not understand what the problem is that nothing is displayed. I'm using the [getwidget][1] package it's a GFCheckboxListTile widget - which just adds a checkbox. Please tell me how can I fix this error?
dropdown
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:getwidget/getwidget.dart';
import 'package:joyn/constants/constants.dart' as constants;
class DropdownWidget extends StatefulWidget {
List<String> items;
SvgPicture? icon;
double width;
DropdownWidget({
Key? key,
required this.items,
required this.icon,
required this.width,
}) : super(key: key);
#override
State<DropdownWidget> createState() => _DropdownWidgetState();
}
class _DropdownWidgetState extends State<DropdownWidget> {
String? selectedValue;
bool selected = false;
final List _selectedTitles = [];
final List _selectedTitlesIndex = [];
final GFCheckboxType type = GFCheckboxType.basic;
#override
void initState() {
super.initState();
if (widget.items.isNotEmpty) {
_selectedTitles.add(widget.items[1]);
}
}
void _onItemSelect(bool selected, int index) {
if (selected == true) {
setState(() {
_selectedTitles.add(widget.items[index]);
_selectedTitlesIndex.add(index);
});
} else {
setState(() {
_selectedTitles.remove(widget.items[index]);
_selectedTitlesIndex.remove(index);
});
}
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
child: DropdownButtonHideUnderline(
child: DropdownButton(
items: List.generate(
widget.items.length,
(index) => DropdownMenuItem<String>(
value: widget.items[index],
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: constants.Colors.white.withOpacity(0.1),
width: 1,
),
),
),
child: GFCheckboxListTile(
value: _selectedTitles.contains(widget.items[index]),
onChanged: (bool selected) {
_onItemSelect(selected, index);
},
selected: selected,
title: Text(
widget.items[index],
style: constants.Styles.smallTextStyleWhite,
),
padding: const EdgeInsets.only(top: 14, bottom: 13),
margin: const EdgeInsets.only(right: 12, left: 2),
size: 22,
activeBgColor: constants.Colors.greyCheckbox,
activeBorderColor: Colors.red,
inactiveBgColor: constants.Colors.greyCheckbox,
activeIcon: SvgPicture.asset(constants.Assets.checkboxIcon),
inactiveBorderColor: constants.Colors.greyXMiddle,
type: type,
),
),
),
),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
});
},
icon: SvgPicture.asset(constants.Assets.arrowDropdown),
iconSize: 21,
itemHeight: 66,
selectedItemBuilder: (context) {
return _selectedTitles.map(
(item) {
return Row(
children: [
widget.icon ?? const SizedBox(),
const SizedBox(width: 8),
Text(
item,
style: constants.Styles.bigBookTextStyleWhite,
),
],
);
},
).toList();
},
),
),
);
}
}

While the UI is need to update on DropdownMenuItem you can wrap GFCheckboxListTile with StatefulBuilder.
child: Container(
child: StatefulBuilder(
builder: (context, setStateSB) => GFCheckboxListTile(
value: _selectedTitles.contains(widget.items[index]),
onChanged: (bool selected) {
_onItemSelect(selected, index);
setStateSB(() {}); /// we are using StatefulBuilder's setState
///add your logic then
setState(() {
selectedValue = widget.items[index];
});
},
DropdownMenuItem ontap is outSide the GFCheckboxListTile therefore onChanged is not calling. I am using onChanged from GFCheckboxListTile to update the selectedValue.

Related

My Flutter ListView is always removing the last item from the list

I'm creating a Flutter Widget and when I try to remove an item from the list I'm using, it always removes the last one, I was thinking it could be a Key problem, but nothing suits it, do anyone know how I could solve this?
The code
create_game.dart
import 'package:flutter/material.dart';
import 'package:pontinho/components/custom_input.dart';
class CreateGame extends StatefulWidget {
const CreateGame({super.key});
#override
State<CreateGame> createState() => _CreateGameState();
}
class _CreateGameState extends State<CreateGame> {
List<String> names = [''];
void changeName(int nameIndex, String change) {
setState(() {
names[nameIndex] = change;
});
}
void removeName(int nameIndex) {
print(names);
print(nameIndex);
setState(() {
names.removeAt(nameIndex);
});
}
ListView createNamesInput() {
return ListView.builder(
itemCount: names.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return ListTile(
key: ObjectKey(index),
title: CustomInput(
key: ObjectKey(index),
labelText: "Nome",
onChanged: (String changed) => changeName(index, changed),
text: names[index],
onRemoved: () => removeName(index),
),
);
},
);
// return names
// .asMap()
// .entries
// .map((el) => CustomInput(
// key: ObjectKey('${el.key}'),
// labelText: "Nome",
// onChanged: changeName,
// index: el.key,
// text: names[el.key],
// onRemoved: removeName,
// ))
// .toList();
}
void addName() {
setState(() {
names.add('');
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: GestureDetector(
onTap: (() => Navigator.pop(context)),
child: const Icon(
Icons.arrow_back,
color: Colors.black,
size: 40,
),
),
backgroundColor: Colors.white,
titleTextStyle: const TextStyle(
color: Colors.black,
fontSize: 20,
),
title: const Text("CRIE SEU JOGO"),
),
body: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
// child: createNamesInput(),
child: Column(
children: [
createNamesInput(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextButton(
onPressed: addName,
child: Row(
children: const [
Icon(Icons.add),
Text('Adicionar Jogador'),
],
),
),
],
),
),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: () => print('Iniciar!'),
child: const Text('Iniciar!'),
),
)
],
),
),
);
}
}
custom_input.dart
import 'package:flutter/material.dart';
typedef OneArgumentCallback = void Function(String changed);
class CustomInput extends StatefulWidget {
final OneArgumentCallback onChanged;
final VoidCallback onRemoved;
final String labelText;
final String text;
const CustomInput({
super.key,
required this.onChanged,
required this.labelText,
required this.text,
required this.onRemoved,
});
#override
State<CustomInput> createState() => _CustomInputState();
}
class _CustomInputState extends State<CustomInput> {
late final TextEditingController inputController;
#override
void initState() {
super.initState();
inputController = TextEditingController(text: widget.text);
}
void changeContent(String value) {
widget.onChanged(
value,
);
}
#override
Widget build(BuildContext context) {
return TextFormField(
key: widget.key,
controller: inputController,
textDirection: TextDirection.ltr,
decoration: InputDecoration(
border: const UnderlineInputBorder(),
labelText: widget.labelText,
suffixIcon: IconButton(
onPressed: () => widget.onRemoved(),
icon: const Icon(
Icons.close,
color: Colors.red,
),
),
),
autocorrect: false,
onChanged: (value) => changeContent(value),
);
}
}
Indeed it is a key issue, you have to create a combined key that must be unique for each item, I merged the index with names[index],
CustomInput(
key: ObjectKey('$index:${names[index]}'),
labelText: "Nome",
onChanged: (String changed) => changeName(index, changed),
text: names[index],
onRemoved: () => removeName(index),
),
note that if you try this code alone the textfield will lose focus because the key has changed, this will be solved by removing the setState inside the onChange
void changeName(int nameIndex, String change) {
names[nameIndex] = change;
}
here you don't need setState because the UI will be updated by default when you are typing in the textfield
I hope I made it clear
I was thinking it could be a Key problem
That's correct; You need to use names[index] as the value for your Key:
ListTile(
key: ObjectKey(names[index]),
title: CustomInput(

Update dropdown items with setstate on focus not working

I want to update dropdown items with setState when data received from api. Dropdown items not update even though widget is rebuilt. I can only get updated dropdown items after unfocus and focus. Please point me out if something wrong in my code..
Dropdown
import 'dart:async';
import 'package:eas/app/core/theme/colors.dart';
import 'package:eas/app/core/theme/theme.dart';
import 'package:eas/app/core/widgets/form_field_input.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
class AppAsyncDropdown extends StatefulWidget {
final double width;
final String hint;
final bool loading;
final List<DropdownMenuItem> items;
final Function(String search) onChanged;
const AppAsyncDropdown({
Key? key,
required this.width,
required this.hint,
required this.loading,
required this.items,
required this.onChanged,
}) : super(key: key);
#override
State<AppAsyncDropdown> createState() => _AppAsyncDropdownState();
}
class _AppAsyncDropdownState extends State<AppAsyncDropdown> {
Timer? _timer;
void debounceSearch(String search) {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(
const Duration(milliseconds: 400), (() => widget.onChanged(search)));
}
#override
void dispose() {
if (_timer != null) {
_timer!.cancel();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
print('why.. ${widget.loading} - ${widget.items}');
final items = [
DropdownMenuItem(
enabled: false,
child: FormFieldInput(
controller: TextEditingController(),
width: widget.width,
hintText: 'search product',
onChanged: (value) => debounceSearch(value),
),
),
];
if (widget.loading) {
items.add(
DropdownMenuItem(
enabled: false,
child: SizedBox(
width: widget.width,
child: Center(
child: Lottie.asset(
'assets/animation/search_loading.json',
width: 150,
)),
),
),
);
}
if (widget.items.isNotEmpty) {
items.addAll(widget.items);
}
return SizedBox(
width: widget.width,
child: DropdownButtonFormField<dynamic>(
isExpanded: true,
decoration: InputDecoration(
hintText: widget.hint,
hintStyle: AppTheme.of(context).subtitle4,
border: const UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black)),
focusedBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: AppColors.primary),
),
),
items: items,
onChanged: (dynamic e) {
print('hii $e');
},
),
);
}
}
I added debounce to prevent requesting on every keystroke.And by debugging with print and some ui changes, it is verified that states are updated..
Script That Update State
import 'dart:async';
import 'package:eas/app/core/layout/desktop/content_desktop.dart';
import 'package:eas/app/core/layout/desktop/header_desktop.dart';
import 'package:eas/app/core/layout/desktop/layout_desktop.dart';
import 'package:eas/app/core/theme/colors.dart';
import 'package:eas/app/core/theme/miscs.dart';
import 'package:eas/app/core/theme/sizedBoxes.dart';
import 'package:eas/app/core/theme/sizes.dart';
import 'package:eas/app/core/theme/theme.dart';
import 'package:eas/app/core/widgets/button.dart';
import 'package:eas/app/core/widgets/dropdown.dart';
import 'package:eas/app/core/widgets/dynamic_list.dart';
import 'package:eas/app/routes/app_pages.dart';
import 'package:flutter/material.dart';
class TransferStockForm extends StatefulWidget {
const TransferStockForm({Key? key}) : super(key: key);
#override
State<TransferStockForm> createState() => _TransferStockFormState();
}
class _TransferStockFormState extends State<TransferStockForm> {
Timer? _timer;
List<DropdownMenuItem> _branchWarehouses = [];
bool _isLoading = false;
void updateBranchWarehouse(String search) {
print('searched - $search');
setState(() {
_isLoading = true;
_branchWarehouses = [];
});
_timer = Timer(
const Duration(seconds: 2),
() {
setState(() {
_isLoading = false;
_branchWarehouses = const [
DropdownMenuItem(
value: '1',
child: Text('one'),
),
DropdownMenuItem(
value: '2',
child: Text('two'),
),
DropdownMenuItem(
value: '3',
child: Text('three'),
),
];
});
},
);
}
#override
void dispose() {
if (_timer != null) {
_timer!.cancel();
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: AppDesktopLayout(
header: const AppDesktopHeader(
title: "Transfer Stock",
pageName: AppRoutes.transferStockForm,
isFavourable: true,
),
currentNavi: AppRoutes.home,
content: AppDesktopContent(
child: Container(
padding: const EdgeInsets.all(AppSizes.md),
child: Container(
padding: const EdgeInsets.all(AppSizes.md),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.all(
Radius.circular(AppSizes.sm),
),
boxShadow: [
AppThemeMiscs.shadow2,
],
),
child: LayoutBuilder(builder: (context, constraint) {
return Row(
children: [
SizedBox(
width: constraint.maxWidth * 0.35,
height: constraint.maxHeight,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'To',
style: AppTheme.of(context).headline3,
),
AppSizeBoxes.sm,
AppAsyncDropdown(
width: constraint.maxWidth * 0.35,
hint: 'select branch or warehouse to transfer to',
loading: _isLoading,
items: _branchWarehouses,
onChanged: (String search) =>
updateBranchWarehouse(search),
),
AppSizeBoxes.md,
Button(onPressed: () {}, text: 'Transfer'),
],
),
),
const Expanded(
child: DynamicList(
type: 'stock',
)),
],
);
}),
),
),
)),
);
}
}
faked api response with timer

Instantly mark all checkboxes in dropdown without closing

I have a drop down list with checkboxes. When selecting the first element All EV's - all elements are selected. But visually, when I select the first element, I only mark the first element that I clicked on. I need to close and reopen the dropdown to see the checkmarks everywhere. I want that when the first element is selected, the checkboxes are also checked everywhere, but it does not close the drop-down list, but immediately sees the checked checkboxes. How to do it?
dropdown
class CheckboxDropdown extends StatefulWidget {
final List<String> items;
final SvgPicture? icon;
final double width;
const CheckboxDropdown({
Key? key,
required this.items,
this.icon,
required this.width,
}) : super(key: key);
#override
State<CheckboxDropdown> createState() => _CheckboxDropdown();
}
class _CheckboxDropdown extends State<CheckboxDropdown> {
String? selectedValue;
bool selected = false;
List _selectedTitles = [];
List selectedItems = [];
List _selectedTitlesIndex = [];
final GFCheckboxType type = GFCheckboxType.basic;
#override
void initState() {
super.initState();
if (widget.items.isNotEmpty) {
_selectedTitles.add(widget.items[1]);
}
}
void _onItemSelect(bool selected, int index) {
if (selected == true) {
setState(() {
if (index == 0) {
_selectedTitles = List.from(widget.items);
_selectedTitlesIndex =
List.generate(widget.items.length, (index) => index);
} else {
_selectedTitles.add(widget.items[index]);
_selectedTitlesIndex.add(index);
}
});
} else {
setState(() {
if (index == 0) {
_selectedTitles.clear();
_selectedTitlesIndex.clear();
} else {
_selectedTitles.remove(widget.items[index]);
_selectedTitlesIndex.remove(index);
}
});
}
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
child: DropdownButtonHideUnderline(
child: DropdownButton2(
offset: const Offset(0, -12),
isExpanded: true,
items: List.generate(
widget.items.length,
(index) => DropdownMenuItem<String>(
value: widget.items[index],
child: Container(
decoration: BoxDecoration(
border: index == widget.items.length - 1
? null
: Border(
bottom: BorderSide(
color: Colors.white.withOpacity(0.1),
width: 1,
),
),
),
child: StatefulBuilder(
builder: (context, setStateSB) => GFCheckboxListTile(
value: _selectedTitles.contains(widget.items[index]),
onChanged: (bool selected) {
_onItemSelect(selected, index);
setStateSB(() {});
},
selected: selected,
title: Text(
widget.items[index],
style: constants.Styles.smallTextStyleWhite,
),
padding: const EdgeInsets.only(top: 12, bottom: 13),
margin: const EdgeInsets.only(right: 0, left: 0),
size: 22,
activeBgColor: constants.Colors.greyCheckbox,
activeBorderColor: constants.Colors.greyXMiddle,
inactiveBgColor: constants.Colors.greyCheckbox,
activeIcon: SvgPicture.asset(constants.Assets.checkboxIcon),
inactiveBorderColor: constants.Colors.greyXMiddle,
type: type,
),
),
),
),
),
hint: Row(
children: [
SvgPicture.asset(constants.Assets.carDropdown),
const SizedBox(width: 8),
_selectedTitles.length > 1
? const Text(
'Selected EV',
style: constants.Styles.xSmallLtStdDropDownTextStyleWhite,
)
: Text(
_selectedTitles.join().toString(),
style: constants.Styles.xSmallLtStdDropDownTextStyleWhite,
),
if (_selectedTitles.isEmpty)
const Text(
'Select EV',
style: constants.Styles.xSmallLtStdDropDownTextStyleWhite,
),
],
),
onChanged: (value) {
setState(() {
selectedValue = value as String;
});
},
icon: SvgPicture.asset(constants.Assets.arrowDropdown),
iconSize: 21,
buttonHeight: 27,
dropdownMaxHeight: 185,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: constants.Colors.purpleMain,
),
color: constants.Colors.greyDark),
),
),
);
}
}
list
final List<String> carList = const [
"All EV's",
'Main EV',
'<EV2>',
];

Checkbox doesn't change when clicked in dropdownbutton

I am using DropdownButton and I am facing the following issue. I'm using a checkbox in elements, but when I click on an element, I don't get a checkmark indicating that the checkbox has been clicked. As a result, I need to close and reopen it, and then I will see the changes that were clicked on the "checkbox". The second problem is that when I select one element, all elements are selected for me. As a final result, I need to get so that I can select an element and the checkbox is immediately marked, if 2 elements are needed, then two, and so on. Tell me how to fix these problems, I will be grateful for the help?
dropdown
class DropdownWidget extends StatefulWidget {
List<String> items;
SvgPicture? icon;
double width;
DropdownWidget({
Key? key,
required this.items,
required this.icon,
required this.width,
}) : super(key: key);
#override
State<DropdownWidget> createState() => _DropdownWidgetState();
}
class _DropdownWidgetState extends State<DropdownWidget> {
String? selectedValue;
bool isChecked = false;
#override
void initState() {
super.initState();
if (widget.items.isNotEmpty) {
selectedValue = widget.items[1];
}
}
#override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
child: DropdownButtonHideUnderline(
child: DropdownButton2(
items: widget.items
.map((item) => DropdownMenuItem<String>(
value: item,
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: constants.Colors.white.withOpacity(0.1),
width: 1,
),
),
),
child: Center(
child: Row(
children: [
if (item == selectedValue)
const SizedBox(
width: 0,
),
Expanded(
child: Text(
item,
style: constants.Styles.smallTextStyleWhite,
),
),
Checkbox(
checkColor: Colors.black,
value: isChecked,
onChanged: (bool? value) {
setState(() {
isChecked = value!;
});
},
),
],
),
),
),
))
.toList(),
value: selectedValue,
onChanged: (value) {
setState(() {
selectedValue = value as String;
});
},
icon: SvgPicture.asset(constants.Assets.arrowDropdown),
iconSize: 21,
buttonHeight: 27,
itemHeight: 47,
dropdownMaxHeight: 191,
dropdownWidth: 140,
dropdownDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: constants.Colors.purpleMain,
),
color: constants.Colors.greyDark,
),
selectedItemBuilder: (context) {
return widget.items.map(
(item) {
return Row(
children: [
widget.icon ?? const SizedBox(),
const SizedBox(width: 8),
Text(
item,
style: constants.Styles.bigBookTextStyleWhite,
),
],
);
},
).toList();
},
),
),
);
}
}
items
final List<String> items = const [
"All EV's",
'Main EV',
'<EV2>',
];
I hope this example explains the concept. For simplcity I made simple a new file, run it and see the results:
Then main idea in two lists, _checkList contain values of the CheckBox and _selectedList handles the main dropdown widget to show the selection.
Feel free to ask any questions and I'm happy to help
import 'package:flutter/material.dart';
class TestPage extends StatelessWidget {
const TestPage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const AnimationDemo(number: 5);
}
}
class AnimationDemo extends StatefulWidget {
const AnimationDemo({Key? key, this.number = 2}) : super(key: key);
final int number;
#override
State<AnimationDemo> createState() => _AnimationDemoState();
}
class _AnimationDemoState extends State<AnimationDemo> {
late List<bool> _checkList;
late List<int> _selectedIndex;
bool _isOpen = false;
#override
void initState() {
_checkList = List.filled(widget.number, false);
_selectedIndex = <int>[];
super.initState();
}
List<DropDownItem> generateItems() {
var tmp = <DropDownItem>[];
for (var i = 0; i < _checkList.length; i++) {
tmp.add(DropDownItem(
isChecked: _checkList[i],
onChanged: (value) {
setState(() {
_checkList[i] = value!;
if (value && !_selectedIndex.contains(i)) {
_selectedIndex.add(i);
} else {
_selectedIndex.remove(i);
}
});
},
));
}
return tmp;
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
Expanded(
child: Text((_selectedIndex.isEmpty)
? 'Nothing Selected'
: _selectedIndex.join(',')),
),
GestureDetector(
onTap: () {
setState(() {
_isOpen = !_isOpen;
});
},
child: const Icon(Icons.arrow_downward),
),
],
),
AnimatedOpacity(
opacity: (_isOpen) ? 1 : 0,
duration: const Duration(milliseconds: 300),
child: Column(
mainAxisSize: MainAxisSize.min,
children: generateItems(),
),
)
],
),
);
}
}
class DropDownItem extends StatelessWidget {
final bool isChecked;
final Function(bool?)? onChanged;
const DropDownItem({Key? key, this.onChanged, this.isChecked = false})
: super(key: key);
#override
Widget build(BuildContext context) {
return Row(
children: [
const Expanded(child: Text('Demo item')),
Checkbox(value: isChecked, onChanged: onChanged)
],
);
}
}
Here's how to achieve the Multiselect dropdown with DropdownButton2:
final List<String> items = [
'Item1',
'Item2',
'Item3',
'Item4',
];
List<String> selectedItems = [];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: DropdownButtonHideUnderline(
child: DropdownButton2(
isExpanded: true,
hint: Align(
alignment: AlignmentDirectional.center,
child: Text(
'Select Items',
style: TextStyle(
fontSize: 14,
color: Theme.of(context).hintColor,
),
),
),
items: items.map((item) {
return DropdownMenuItem<String>(
value: item,
//disable default onTap to avoid closing menu when selecting an item
enabled: false,
child: StatefulBuilder(
builder: (context, menuSetState) {
final _isSelected = selectedItems.contains(item);
return InkWell(
onTap: () {
_isSelected
? selectedItems.remove(item)
: selectedItems.add(item);
//This rebuilds the StatefulWidget to update the button's text
setState(() {});
//This rebuilds the dropdownMenu Widget to update the check mark
menuSetState(() {});
},
child: Container(
height: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
_isSelected
? const Icon(Icons.check_box_outlined)
: const Icon(Icons.check_box_outline_blank),
const SizedBox(width: 16),
Text(
item,
style: const TextStyle(
fontSize: 14,
),
),
],
),
),
);
},
),
);
}).toList(),
//Use last selected item as the current value so if we've limited menu height, it scroll to last item.
value: selectedItems.isEmpty ? null : selectedItems.last,
onChanged: (value) {},
buttonHeight: 40,
buttonWidth: 140,
itemHeight: 40,
itemPadding: EdgeInsets.zero,
selectedItemBuilder: (context) {
return items.map(
(item) {
return Container(
alignment: AlignmentDirectional.center,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Text(
selectedItems.join(', '),
style: const TextStyle(
fontSize: 14,
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
),
);
},
).toList();
},
),
),
),
);
}
Also, I've added it as an example to the package doc "Example 4" so you can get back to it later.

How to implement dropdownbutton into a function in flutter

Im working on a company project. But I can't simply get the idea of how to implement a basic dropdown button into a function but I can't seem to make the values change in the dropdown function what do you think im doing wrong here's my code:
Widget buildDropdownField({
required String dropdownHeader,
required String dropdownValue,
}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
);
}
Wrap with StatefulBuilder it will work.
Widget buildDropdownField(
{required String dropdownHeader, required String dropdownValue}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
StatefulBuilder(
builder: (_, setDropState) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setDropState(() {
dropdownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
},
)
],
);
}
Try below code hope its help to you. use StatefulBuilder Refer here
Your dropdown function:
buildDropdownField({
required String dropdownHeader,
required String dropdownValue,
}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
StatefulBuilder(builder: (context, StateSetter setState) {
return DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
})
],
);
}
Your Widget:
buildDropdownField(
dropdownHeader: 'dropdownHeader',
dropdownValue: '-',
),
Result->
Result after selection->
First of all, you shouldn't update the parameter with the new value. It did update the parameter, but the function will still getting the value from it's calling.
I did not know the buildDropdownField function is inside a class or not, but it's okay and I will provide the solutions for both scenarios.
Within the Class
You need to create a variable within a class outside the functions.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _dropDownValue = '-';
Widget buildDropdownField({required String dropdownHeader}) {
return Column(
children: <Widget>[
Text(dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
DropdownButton<String>(
value: _dropDownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? newValue) {
setState(() {
_dropDownValue = newValue!;
});
},
items: <String>['-', 'Geçti', 'Kaldı',
'Belirsiz'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
);
}
}
Outside the Class
You need to turn it into Stateful Widget in order for the drop down text to change. Once the dropdown is a stateful widget, you can use the solution above or a callback to make the changes on parent class.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _dropDownValue = '-';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: DropDownWidget(
dropdownHeader: 'Name',
dropdownValue: _dropDownValue,
onChanged: (String? newValue) {
setState(() {
_dropDownValue = newValue!;
});
},
),
),
);
}
}
class DropDownWidget extends StatefulWidget {
final String dropdownHeader;
final String dropdownValue;
final Function(String?)? onChanged;
DropDownWidget({required this.dropdownHeader, required this.dropdownValue, this.onChanged, Key? key}) : super(key: key);
#override
_DropDownWidgetState createState() => _DropDownWidgetState();
}
class _DropDownWidgetState extends State<DropDownWidget> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text(widget.dropdownHeader),
const SizedBox(
height: 10,
),
//dropdownField
DropdownButton<String>(
value: widget.dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: widget.onChanged,
items: <String>['-', 'Geçti', 'Kaldı', 'Belirsiz'].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
],
);
}
}