Flutter dropdownbutton bind key value array - dart

How can I bind key value array as shown below to flutter dropdownbutton? I want the key to be the drop-down list value and value to be the label.
final items = {
'1': 'item 1',
'2': 'item 2',
'3': 'item 3',
'4': 'item 4',
'5': 'item 5'
};

Use this:
DropdownButton<String> button = DropdownButton(
items: items.entries
.map<DropdownMenuItem<String>>(
(MapEntry<String, String> e) => DropdownMenuItem<String>(
value: e.key,
child: Text(e.value),
))
.toList(),
onChanged: (String newKey) {/* todo handle change */},
);

It's simple, the snippet below show you how to...
final items = {
'1': 'item 1',
'2': 'item 2',
'3': 'item 3',
'4': 'item 4',
'5': 'item 5'
};
// your list of DropDownMenuItem
List< DropdownMenuItem<String>> menuItems = List();
// loop in the map and getting all the keys
for(String key in items.keys){
menuItems.add(
DropdownMenuItem<String>(
// items[key] this instruction get the value of the respective key
child: Text( items[key] ), // the value as text label
value: key, // the respective key as value
) );
}
//later you will do something like this
DropdownButton<String>(
items: menuItems,
onChanged: (value){
// do your stuffs
},
);

Related

Generic type check - keep type and not dynamic

I have these classes
class CustomPopupAction<T> extends CustomAction {
final Icon icon;
final List<CustomPopupActionItem<T>> actions;
final void Function(T) onActionSelected;
CustomPopupAction({
required this.icon,
required this.actions,
required this.onActionSelected,
});
}
class CustomPopupActionItem<T> {
final T value;
final Widget Function(T) itemBuilder;
CustomPopupActionItem({
required this.value,
required this.itemBuilder,
});
}
and I am trying to create overflow menu which will work like this:
if the button is visible, I will create PopupMenuButton
if the button is overflown, I will create ListTile which will open dialog
it can hold multiple different types like CustomAction, CustomPopupAction<Locale>, CustomPopupAction<String>...
I am building that row like this
if (a is CustomPopupAction) {
return PopupMenuButton(
icon: a.icon,
onSelected: (i) => a.onActionSelected(i),
itemBuilder: (context) {
return a.actions.map((i) => PopupMenuItem(
value: i.value,
child: i.itemBuilder(i.value),
)).toList();
},
);
} else {
return IconButton(...);
}
and finally my main code:
...
return OverflowMenu(
actions: [
CustomPopupAction<Locale>(
icon: Icon(Icons.translate),
actions: [
CustomPopupActionItem<Locale>(
value: Locale('en'),
itemBuilder: (l) => ListTile(title: Text(l.toString()),
),
],
onActionSelected: (l) => print(l),
],
);
But this doesn't work for me, I am getting an exception Expected a value of type '(dynamic) => Widget', but got one of type '(Locale) => ListTile'.
I know it's because if (a is CustomPopupAction) is actually getting CustomPopupAction<dynamic>.
can I somehow convince Dart that a nas not dynamic type and that it should work with it's real type?
if not, why am I getting that exception? Locale can be assigned to dynamic variable and ListTile is clearly a Widget.
can I do this without going through dynamics at all?

DropdownButton with int items not working, it does not select new value

I need to create DropdownButton widget with int items, but it does not work as expected.
This is the code:
DropdownButton<int>(
hint: Text("Pick"),
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((int value) {
return new DropdownMenuItem<int>(
value: _number_tickets_total,
child: new Text(_number_tickets_total.toString()),
);
}).toList(),
onChanged: (newVal) {
setState(() {
_number_tickets_total = newVal;
});
})
The problem is that the widget never gets the value selected. I always see the "hint" text even when I choose a value.
What you are missing is value attribute of the DropdownButton. When that value is null, it will show the hint, otherwise latest selected value.
The values that you are using in the drop-down item is irrelevant from the whole list, that's why you should just pass them the related information. I modified your code with the value attribute below.
DropdownButton<int>(
hint: Text("Pick"),
value: _number_tickets_total,
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Text(value.toString()),
);
}).toList(),
onChanged: (newVal) {
setState(() {
_number_tickets_total = newVal;
});
});

insert checkbox while looping

am creating checkbox like this:
for (var a in user.poll.questions[i].options) {
children.add(
new Row(
children: [
new Checkbox(
value: checkBoxValue,
onChanged: (bool newValue) {
setState(() {
checkBoxValue = newValue;
});
}
),
new Text(a.text),
],
),
);
}
and this will create this:
Which is what i need .. but the problem is when one checked all will be updated and be checked and reverse ...
i want to check one .. and only this one will be checked and i get its value ..
how to do this? is there is checkbox id or something?
You can save selected question - something like:
Question selectedQuestion = null;
.....
for (var a in user.poll.questions[i].options) {
children.add(
new Row(
children: [
new Checkbox(
value: selectedQuestion == a,
onChanged: (bool newValue) {
setState(() {
selectedQuestion = a;
});
}
),
new Text(a.text),
],
),
);
}
UPD
For multiple checking:
1 - set list instead of one value
final List<Question> questionList = [];
2 - change CheckBox behavior
Checkbox(
value: questionList.contains(a),
onChanged: (bool newValue) {
setState(() {
newValue ? questionList.add(a) : questionList.remove(a);
});
}
)

Flutter reading from JSON if value matches

I have a json of customers:
customer.json
[
{
"name": "Customer 1",
"id": "1"
},
{
"name": "Customer 2",
"id": "2"
},
{
"name": "Customer 3",
"id": "3"
}
]
This is the dart file using this json data:
customerslist.dart
Future Method
Future<String> loadCustomers() async{
var res = await http.get(
Uri.encodeFull(url),
headers: {"Accept": "application/json"});
return res.body;
}
Widget
widgets.add(new FutureBuilder(
future: loadCustomers(),
builder: (context, snapshot){
if(snapshot.hasData){
//get snapshot data from JSON tree
var jsondecode = json.decode(snapshot.data);
return new ListView.builder(
shrinkWrap: true,
itemCount: jsondecode.length,
itemBuilder: (context, index){
String name = jsondecode[index]["name"];
String id = jsondecode[index]["id"];
if(name == "Customer 2"){
return new Column(
children: <Widget>[
new ListTile(
title: new Text("Name"),
trailing: new Text(name),
),
new ListTile(
title: new Text("ID"),
trailing: new Text(id),
)
],
);
}
});
}else{
return new Center(
child: new CircularProgressIndicator(),
);
}
}));
What I am trying to do is bringing out the values if the name matches as you can see from if(name == "Customer 2").
But the problem I am having is:
When i change the if statement to if(name == "Customer 1"), the output is as you would expect:
But if i were to change it to if(name == "Customer 2"), the output turns out blank as such:
Could someone explain why is it turning out like this or is there another method I could go about doing to fix this issue?
I think it's because Column widget is taking full height. You could try using the minimum height for your column and add else condition.
if(name == "Customer 2"){
return new Column(
children: <Widget>[
new ListTile(
title: new Text("Name"),
trailing: new Text(name),
),
new ListTile(
title: new Text("ID"),
trailing: new Text(id),
)
],
);
} else {
return new Container();
}
When you use 'Customer 1' it works fine because your first element is 'Customer 1' , probably you have an error on your console because for other items there are no returning widgets.
In the case of 'Customer 2' the first item is not returned, so check your console log.
itemBuilder expects you return a widget for all the cases.
You just convert the var string to list
var jsondecode = json.decode(snapshot.data);
List list = jsondecode;
String name = list[index]["name"];
String id = list[index]["id"];
then pass the list to get the index may be this will work bocasue the var type may be get the first json string only
enter code here

Is it possible to require a Dropdown selection as part of form validation in Flutter?

I'm familiar with form validation using a TextFormField in Flutter, but is it possible to integrate a DropdownButton into a Form and require one of its value be selected before submission?
Basically, integrate DropdownButton validation into this basic Flutter validation example:
https://flutter.io/cookbook/forms/validation/
Dart Package have already the widget DropdownButtonFormField for this. Here is an example of how to use it:
List<String> typeNeg = [
"One",
"Two",
"Three",];
String dropdownValue = "One";
DropdownButtonFormField<String>(
value: dropdownValue,
hint: Text("Type of business"),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
validator: (String value) {
if (value?.isEmpty ?? true) {
return 'Please enter a valid type of business';
}
},
items: typeNeg
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onSaved: (val) => setState(() => _user.typeNeg = val),
),
The user model is as follows:
class User {
int id;
String name;
String email;
String typeNeg;
User({this.id, this.name, this.email, this.typeNeg});
factory User.fromJson(Map<String, dynamic> parsedJson) {
return User(
id: parsedJson["id"],
name: parsedJson["name"] as String,
email: parsedJson["email"] as String,
typeNeg: parsedJson["typeNeg"] as String,
);
}
save(){
print("User saved");
}
}
To try the validator option change String dropdownValue = "One"; to String dropdownValue = null;
From text_form_field.dart file in Flutter's source code you can see that TextFormField is no more than a FormField emitting a TextField widget in its builder callback. You can write your own DropdownFormField using a similar pattern. Here's mine:
import 'package:flutter/material.dart';
class DropdownFormField<T> extends FormField<T> {
DropdownFormField({
Key key,
InputDecoration decoration,
T initialValue,
List<DropdownMenuItem<T>> items,
bool autovalidate = false,
FormFieldSetter<T> onSaved,
FormFieldValidator<T> validator,
}) : super(
key: key,
onSaved: onSaved,
validator: validator,
autovalidate: autovalidate,
initialValue: items.contains(initialValue) ? initialValue : null,
builder: (FormFieldState<T> field) {
final InputDecoration effectiveDecoration = (decoration ?? const InputDecoration())
.applyDefaults(Theme.of(field.context).inputDecorationTheme);
return InputDecorator(
decoration:
effectiveDecoration.copyWith(errorText: field.hasError ? field.errorText : null),
isEmpty: field.value == '' || field.value == null,
child: DropdownButtonHideUnderline(
child: DropdownButton<T>(
value: field.value,
isDense: true,
onChanged: field.didChange,
items: items.toList(),
),
),
);
},
);
}
The key is to bind DropdownButton's onChanged to field.didChange. Usage is pretty straightforward:
DropdownFormField<String>(
validator: (value) {
if (value == null) {
return 'Required';
}
},
onSaved: (value) {
// ...
},
decoration: InputDecoration(
border: UnderlineInputBorder(),
filled: true,
labelText: 'Demo',
),
initialValue: null,
items: [
DropdownMenuItem<String>(
value: '1',
child: Text('1'),
),
DropdownMenuItem<String>(
value: '2',
child: Text('2'),
)
],
),
I got the idea from this site. The difference is that my version of DropdownFormField is closer to Flutter's native implementation (which extends TextFormField instead of wrapping it inside a StatefulWidget).

Resources