The Passed Value isn't updating in Flutter - 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 ?? '';
});
}),

Related

All checkboxes in Flutter checkbox Listtile are checked at the same time

I created a checkbox Listtile, where I can click on an Info button to receive further info if neccessary. Therefore I created a main Widget which contains the Listtile widget. When the Info button gets clicked, the detail page opens and reads the specific details from the model class.Up to that point everything works fine.
My leading is a checkbox. If it gets clicked not just one checkbox gets checked, but all of them. how can I write my code, that they arent checked all at the same time automatically?
thank you very much for your help
Kind regards
Here is my code:
//This is my model
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
class Info {
String title;
String description;
//String image;
Info(
{required this.title,
required this.description,
// #required this.image
});
}
List<Info> ModelList = [
Info(
title: 'title1',
description: 'description1'
),
Info(
title: 'title2',
description: 'description2'
),
];
//This is the widget
class MainWidget extends StatefulWidget {
const MainWidget({Key? key}) : super(key: key);
#override
State<MainWidget> createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder
(itemCount: ModelList.length,
itemBuilder: (context,index)
{Info cardname = ModelList[index];
return Card(
child: CheckboxListTile(
tileColor: const Color(0xFF5D6D7E),
shape: RoundedRectangleBorder(
side: const BorderSide (color:Colors.white,width: 2),
borderRadius: BorderRadius.circular(10),
),
contentPadding: const EdgeInsets.symmetric(vertical: 10.0),
value: timeDilation !=1.0,
onChanged: (bool? value) {
setState (() {
timeDilation = value! ? 5.0 : 1.0;
});
},
title: Text(
cardname.title,
style: const TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w600,
color: Colors.white),
),
//an welcher Stelle die Checkbox ist (links oder rechts)
controlAffinity:
ListTileControlAffinity.leading,
secondary: IconButton(icon: const Icon(Icons.info_outlined,size: 40,color: Colors.orange,), onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context)=> DetailScreen(cardname)
));
},),
)
);
}
,),
)
;
}
}
//Detail screen
//Detail Screen;
class DetailScreen extends StatelessWidget {
final Info cardname ;
const DetailScreen (this.cardname, {super.key});
//const DetailScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: customAppBar
((cardname.title),),
body: Padding(
padding: const EdgeInsets.all(8.0) ,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Image.network(
// cardname.imageUrl,
//height: 500,
//),
//
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
cardname.description,
textAlign: TextAlign.justify,
style: const TextStyle(fontSize: 22.0),
),
),
],
),
),
) ,
);
}
}
I tried to put the Item builder in an extra widget and return it into the main widget as shown below, but this didn`t work as well
class SubWidget extends StatefulWidget {
const SubWidget({Key? key}) : super(key: key);
#override
State<SubWidget> createState() => _SubWidgetState();
}
class _SubWidgetState extends State<SubWidget> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder
(itemCount: ModelList.length,
itemBuilder: (context,index)
{Info cardname = ModelList[index];
return MainWidget()
Add variable is isSelected bool for Info class
And after onChanged change value: isSelected = !isSelected
Try this:
class Info {
String title;
String description;
bool isSelected;
//String image;
Info({
required this.title,
required this.description,
required this.isSelected,
// #required this.image
});
}
List<Info> ModelList = [
Info(title: 'title1', description: 'description1', isSelected: false),
Info(title: 'title2', description: 'description2', isSelected: false),
];
//This is the widget
class MainWidget extends StatefulWidget {
const MainWidget({Key? key}) : super(key: key);
#override
State<MainWidget> createState() => _MainWidgetState();
}
class _MainWidgetState extends State<MainWidget> {
double timeDilation = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: ModelList.length,
itemBuilder: (context, index) {
Info cardname = ModelList[index];
return Card(
child: CheckboxListTile(
tileColor: const Color(0xFF5D6D7E),
shape: RoundedRectangleBorder(
side: const BorderSide(color: Colors.white, width: 2),
borderRadius: BorderRadius.circular(10),
),
contentPadding: const EdgeInsets.symmetric(vertical: 10.0),
value: cardname.isSelected,
onChanged: (bool? value) {
setState(() {
cardname.isSelected = !cardname.isSelected;
});
},
title: Text(
cardname.title,
style: const TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w600,
color: Colors.white),
),
//an welcher Stelle die Checkbox ist (links oder rechts)
controlAffinity: ListTileControlAffinity.leading,
secondary: IconButton(
icon: const Icon(
Icons.info_outlined,
size: 40,
color: Colors.orange,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(cardname),
),
);
},
),
),
);
},
),
);
}
}

How to change a variable when changing DropDownButton

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
},
),

Flutter Gesture Detector onTap not working but on double tap working

I am creating a todo list application and I have wrapped a textform field in a gesture detector in a list tile however the onTap gesture does not fire but the onDoubleTap gesture does:
todolist_screen.dart:
class TodoListScreen extends StatefulWidget {
final void Function() onSettingsPress;
final void Function() onInit;
final void Function(int id) deleteTodo;
final void Function(Todo todo) toggleComplete;
final List<Todo> todos;
TodoListScreen(
{required this.onSettingsPress,
required this.onInit,
required this.deleteTodo,
required this.toggleComplete,
this.todos = const []});
#override
_TodoScreenState createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoListScreen> {
dynamic myList = [];
#override
void initState() {
super.initState();
widget.onInit();
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(top: Radius.circular(25))),
backgroundColor: Colors.white,
builder: (context) => FractionallySizedBox(
heightFactor: 0.8,
child: AddTodoModalContainer(),
));
},
child: const Icon(Icons.add),
),
appBar: AppBar(
title: Text('Todos'),
backgroundColor: Colors.blue,
actions: [
IconButton(
icon: Icon(Icons.settings), onPressed: widget.onSettingsPress)
],
),
body: SafeArea(
child: Column(
children: [
Expanded(
child: ListView(
children: widget.todos
.map((todo) => TodoItem(
id: todo.id,
title: todo.title,
completed: todo.completed,
deleteTodo: widget.deleteTodo,
toggleComplete: (value) {
widget.toggleComplete(todo);
}))
.toList(),
),
)
],
),
),
);
}
}
todo_item.dart:
class TodoItem extends StatefulWidget {
const TodoItem(
{Key? key,
required this.id,
required this.title,
required this.completed,
required this.toggleComplete,
required this.deleteTodo})
: super(key: key);
final int id;
final String title;
final bool completed;
final void Function(bool?) toggleComplete;
final void Function(int id) deleteTodo;
#override
_TodoItemState createState() => _TodoItemState();
}
class _TodoItemState extends State<TodoItem> {
late final TextEditingController _controller;
late final FocusNode _focusNode;
#override
void initState() {
super.initState();
_controller = TextEditingController();
_focusNode = FocusNode();
}
#override
void dispose() {
super.dispose();
_controller.dispose();
_focusNode.dispose();
}
bool isActive = false;
#override
Widget build(BuildContext context) {
return ListTile(
leading: Checkbox(
value: widget.completed,
onChanged: widget.toggleComplete,
),
trailing: IconButton(
icon: Icon(Icons.delete_forever_sharp),
onPressed: () {
widget.deleteTodo(widget.id);
}),
title: GestureDetector(
onTap: () {
print('tapped');
setState(() {
isActive = !isActive;
});
},
child: TextFormField(
focusNode: _focusNode,
initialValue: widget.title,
decoration: InputDecoration(
hintText: widget.title,
border: isActive
? UnderlineInputBorder(
borderSide: BorderSide(
width: 5,
style: BorderStyle.solid,
color: Colors.black))
: InputBorder.none),
readOnly: !isActive,
style: TextStyle(fontSize: 20),
),
),
);
}
}
P.S I tried adding the behaviour property to the Gesture Detector but they all did not work
After tweaking a few widgets I realised the problem. TextFormField already has an onTap property hence it took precedence over the Gesture Detector's onTap property.

How do I make a single DropDownButton widget be used multiple times to save space?

I have a registration form with many DropDownButton fields that take enums in the menu. Currently, I'm making a separate widget for every single button. Is there a way to just make a single widget and then using its parameters to change it?
Is there a better way to do this? Currently, I'm just copy-pasting and renaming it for every DropDownButton I want to create.
class _BloodTypeDropDownFieldState extends State<BloodTypeDropDownField> {
BloodType _currentSelectedValue;
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<BloodType>(
value: _currentSelectedValue,
hint: Text(
"Blood Group",
style: GoogleFonts.roboto(
textStyle: Theme.of(context).textTheme.headline4,
fontSize: 13,
fontWeight: FontWeight.w700,
color: Color(0xffffffff),
),
),
isDense: true,
onChanged: (BloodType newValue) {
setState(() {
_currentSelectedValue = newValue;
});
},
selectedItemBuilder: (BuildContext context) {
return BloodType.getValues().map((BloodType bloodType) {
return Text(
bloodType.toString(),
style: GoogleFonts.roboto(
textStyle: Theme.of(context).textTheme.headline4,
fontSize: 13,
fontWeight: FontWeight.w700,
color: Color(0xffffffff),
),
);
}).toList();
},
items: BloodType.getValues().map((BloodType bloodType) {
return DropdownMenuItem<BloodType>(
value: bloodType,
child: Text(
bloodType.toString(),
style: GoogleFonts.roboto(
textStyle: Theme.of(context).textTheme.headline4,
fontSize: 15,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
);
}).toList()
),
);
}
}
class BloodType {
final value;
const BloodType._internal(this.value);
toString() => '$value';
static const A_PLUS = const BloodType._internal('A+');
static const A_MINUS = const BloodType._internal('A-');
static const B_PLUS = const BloodType._internal('B+');
static const B_MINUS = const BloodType._internal('B-');
static const AB_PLUS = const BloodType._internal('AB+');
static const AB_MINUS = const BloodType._internal('AB-');
static const O_PLUS = const BloodType._internal('O+');
static const O_MINUS = const BloodType._internal('O-');
static List<BloodType> list = [
A_PLUS,
A_MINUS,
B_PLUS,
B_MINUS,
AB_PLUS,
AB_MINUS,
O_PLUS,
O_MINUS
];
static List<BloodType> getValues() => list;
}
You could define a generic class for your custom DropDown:
class MyDropDown<T> extends StatelessWidget {
final String hint;
final T value;
final List<T> values;
final ValueChanged<T> onChanged;
MyDropDown({
Key key,
this.hint,
this.value,
#required this.values,
this.onChanged,
}) : assert(values != null),
super(key: key);
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: value,
hint: Text(hint ?? 'Pick one'),
isDense: true,
onChanged: onChanged,
items: values.map((value) => DropdownMenuItem<T>(
value: value,
child: Text(value.toString()),
))
.toList(),
),
);
}
}
That you can easily use wherever you want:
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final _current = useState<BloodType>();
return Scaffold(
body: Center(
child: MyDropDown<BloodType>(
hint: 'Blood Type',
value: _current.value,
values: bloodTypes,
onChanged: (value) => _current.value = value,
),
),
);
}
}
For whatever type T. Here is an example for your BloodTypes:
enum BloodGroupEnum {
a,
b,
ab,
o,
}
extension BloodGroupEnumX on BloodGroupEnum {
String get label => describeEnum(this).toUpperCase();
}
enum RhdEnum {
positive,
negative,
}
extension RhdEnumX on RhdEnum {
String get label {
switch (this) {
case RhdEnum.positive:
return '+';
case RhdEnum.negative:
return '-';
default:
return '';
}
}
}
class BloodType {
final BloodGroupEnum group;
final RhdEnum rhd;
String toString() => '${group.label}${rhd.label}';
BloodType({
this.group,
this.rhd,
});
}
final List<BloodType> bloodTypes = BloodGroupEnum.values
.map((group) => RhdEnum.values.map((rhd) {
print('NEW');
print(BloodType(group: group, rhd: rhd).toString());
return BloodType(group: group, rhd: rhd);
}).toList())
.expand((i) => i)
.toList();
The only requirement for the class you want to use within MyDropDown is to properly implement toString() that is used as a label for the DropdownMenuItem.
Full source code for easy copy-paste:
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:google_fonts/google_fonts.dart';
void main() {
runApp(AppWidget());
}
class AppWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
textTheme: GoogleFonts.robotoTextTheme(
Theme.of(context).textTheme,
),
),
home: HomePage(),
);
}
}
class HomePage extends HookWidget {
#override
Widget build(BuildContext context) {
final _current = useState<BloodType>();
return Scaffold(
body: Center(
child: MyDropDown<BloodType>(
hint: 'Blood Type',
value: _current.value,
values: bloodTypes,
onChanged: (value) => _current.value = value,
),
),
);
}
}
class MyDropDown<T> extends StatelessWidget {
final String hint;
final T value;
final List<T> values;
final ValueChanged<T> onChanged;
MyDropDown({
Key key,
this.hint,
this.value,
#required this.values,
this.onChanged,
}) : assert(values != null),
super(key: key);
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: value,
hint: Text(hint ?? 'Pick one'),
isDense: true,
onChanged: onChanged,
items: values
.map((value) => DropdownMenuItem<T>(
value: value,
child: Text(value.toString()),
))
.toList(),
),
);
}
}
enum BloodGroupEnum {
a,
b,
ab,
o,
}
extension BloodGroupEnumX on BloodGroupEnum {
String get label => describeEnum(this).toUpperCase();
}
enum RhdEnum {
positive,
negative,
}
extension RhdEnumX on RhdEnum {
String get label {
switch (this) {
case RhdEnum.positive:
return '+';
case RhdEnum.negative:
return '-';
default:
return '';
}
}
}
class BloodType {
final BloodGroupEnum group;
final RhdEnum rhd;
String toString() => '${group.label}${rhd.label}';
BloodType({
this.group,
this.rhd,
});
}
final List<BloodType> bloodTypes = BloodGroupEnum.values
.map((group) =>
RhdEnum.values.map((rhd) => BloodType(group: group, rhd: rhd)).toList())
.expand((i) => i)
.toList();
So I think what you are looking for is to reuse the dropdownbutton I would recommend to use a callback that will give back the type that is selected in the Widget to it's parent. We can use the ValueChanged.
class CustomDropDown extends StatelessWidget {
final ValueChanged<BloodType> onChanged;
final BloodType currentSelectedValue;
const CustomDropDown({Key key, #required this.onChanged, #required this.currentSelectedValue}) : super(key: key);
#override
Widget build(BuildContext context) {
return DropdownButtonHideUnderline(
child: DropdownButton<BloodType>(
value: currentSelectedValue,
hint: Text(
"Blood Group",
style: GoogleFonts.roboto(
textStyle: Theme.of(context).textTheme.headline4,
fontSize: 13,
fontWeight: FontWeight.w700,
color: Color(0xffffffff),
),
),
isDense: true,
onChanged: this.onChanged,
selectedItemBuilder: (BuildContext context) {
return BloodType.getValues().map((BloodType bloodType) {
return Text(
bloodType.toString(),
style: GoogleFonts.roboto(
textStyle: Theme.of(context).textTheme.headline4,
fontSize: 13,
fontWeight: FontWeight.w700,
color: Color(0xffffffff),
),
);
}).toList();
},
items: BloodType.getValues().map((BloodType bloodType) {
return DropdownMenuItem<BloodType>(
value: bloodType,
child: Text(
bloodType.toString(),
style: GoogleFonts.roboto(
textStyle: Theme.of(context).textTheme.headline4,
fontSize: 15,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
);
}).toList()
Now you can call this widget from anywhere in your app and can be easly reused.
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
BloodType currentSelectedValue;
#override
Widget build(BuildContext context) {
return CustomDropDown(
currentSelectedValue: this.currentSelectedValue,
onChanged: (bloodType){
setState((){
this.currentSelectedValue = bloodType
});
},
);
}
}

Flutter DropDownbutton not showing selected values

The dropdown doesn't show it as the chosen one, it just continues as though nothing was selected. Please help me to solve the problem.
I created this custom dropdown widget for multiple usages...
class _AddItemWidgetState extends State<AddItemWidget> {
static const categoryTypes = [
"SL",
"KA",
];
static const subCategoryType = [
"10KG",
"20KG",
"5KG",
];
static const Type = [
"Big Tray",
"Small Tray",
];
String categorySelectedValue;
String subCategorySelectedValue;
String itemType;
Widget categoryFieldWidget(
{String name, List<String> nameList, String selectedValue}) {
return Container(
height: 49,
child: FormField<String>(
builder: (FormFieldState<String> state) {
return InputDecorator(
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 10, right: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0))),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
icon: Icon(Icons.keyboard_arrow_down),
hint: Text(
name,
),
onChanged: (String newValue) {
setState(() {
selectedValue = newValue;
});
print(selectedValue);
},
value: selectedValue,
isDense: true,
items: nameList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
),
);
}
Usage:-
But when I use this custom dropdown widget in other widgets, the value is not showing on the Ui.
"categorySelectedValue"'s value changes...but it's not showing on the Ui...
Expanded(
child: categoryFieldWidget(
name: "Category",
nameList: categoryTypes,
selectedValue: categorySelectedValue)),
Just check out this example
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Users'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const categoryTypes = [
"SL",
"KA",
];
static const subCategoryType = [
"10KG",
"20KG",
"5KG",
];
static const itemtype = [
"Big Tray",
"Small Tray",
];
String categorySelectedValue;
String subCategorySelectedValue;
String itemType;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
Container(
height: 100,
child: CustomDropDown(
callback: (value) {
print('This is the category callbackValue : $value');
},
name: 'Category',
list: categoryTypes,
selectedValue: categorySelectedValue,
),
),
Container(
height: 100,
child: CustomDropDown(
callback: (value) {
print('This is the subcategory callbackValue : $value');
},
name: 'SubCategory',
list: subCategoryType,
selectedValue: subCategorySelectedValue,
),
),
Container(
height: 100,
child: CustomDropDown(
callback: (value) {
print('This is the type callbackValue : $value');
},
name: 'Type',
list: itemtype,
selectedValue: itemType,
),
),
],
),
),
);
}
}
typedef void StringCallback(String val);
class CustomDropDown extends StatefulWidget {
final StringCallback callback;
final List<String> list;
final String name;
final String selectedValue;
const CustomDropDown(
{Key key, this.list, this.name, this.selectedValue, this.callback})
: super(key: key);
#override
_CustomDropDownState createState() => _CustomDropDownState();
}
class _CustomDropDownState extends State<CustomDropDown> {
List<String> currentList = List();
String name;
String currentSelectedValue;
#override
void initState() {
super.initState();
currentList = widget.list;
name = widget.name;
currentSelectedValue = widget.selectedValue;
}
#override
Widget build(BuildContext context) {
return Container(
height: 49,
child: FormField<String>(
builder: (FormFieldState<String> state) {
return InputDecorator(
decoration: InputDecoration(
contentPadding: EdgeInsets.only(left: 10, right: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0))),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
icon: Icon(Icons.keyboard_arrow_down),
hint: Text(
widget.name,
),
onChanged: (String newValue) {
print('This is the value on select $newValue');
setState(() {
currentSelectedValue = newValue;
});
widget.callback(currentSelectedValue);
},
value: currentSelectedValue,
isDense: true,
items: currentList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
),
);
}
}
Let me know if it works.