In my code I added a dropdown which looks like below, When I switched the selection in dropdown its not getting updated its showing exception, I have declared a variable in statefull widget , In my dropdown function I am assigning that as A value to the dropdown Button, and in Onchanged I am passing the json to another function there I am taking the value from a variable and assigning it to a opSelected variable inside the setState
class _ReportFilterState extends State<ReportFilter> {
String opSelected;
//declared string to hold the selected value in dropdown within the state class.
buildMainDropdown(List<Map<String, Object>> items, StateSetter setState) {
return Container(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 27.0,
vertical: 16.0,
),
child: Align(
alignment: Alignment.topLeft,
child: DropdownButtonHideUnderline(
child: DropdownButton(
isExpanded: true,
hint: Text("Choose Filters"),
value: opSelected, // Here assigning the value
items: items
.map((json) => DropdownMenuItem(
child: Text(json["displayName"]), value: json))
.toList(),
onChanged: (json) {
manageIntState(json, setState);
},
),
),
),
),
);
}
void manageIntState(Map<String, Object> jsonSelected, StateSetter setState) {
setState(() {
dispName = jsonSelected["displayName"];
//here I am setting the selected value
opSelected = dispName;
//Doing some operations
id = jsonSelected['id'];
type = jsonSelected['type'];
selectedFilterOption = jsonSelected;
if (jsonSelected.containsKey("data")) {
List<Map<String, Object>> tempList;
List<String> dailogContent = List<String>();
tempList = jsonSelected['data'];
tempList
.map((val) => {
dailogContent.add(val['displayId']),
})
.toList();
_showReportDialog(dailogContent);
}
});
}
But when I run I will end up with error
items==null||
items.isEmpty||value==null||itsems.where((DropdownMenuItem
item)=>item.value==value).length==1 is not true ..
Let me know what I have done wrong in code so its giving me like this, if I commented its not showing the selected dropdown value.
That error happens when the selected value of the DropdownButton is not one of the values of it's items.
In your case, your items values are json which is a Map<String, Object>, and the value of the DropdownButton is opSelected which is a String.
So you need to change the type of opSelected like this:
Map<String, Object> opSelected;
Also make sure you are passing a reference of the same list of items to buildMainDropdown(), because if you are creating a new list while calling buildMainDropdown() then the DropdownButton will have another reference of options and it's not allowed
Note: you may want yo use dynamic instead of Object for the Map, like this:
Map<String, dynamic> opSelected;
Here is why: What is the difference between dynamic and Object in dart?
Related
I am trying to build a dropdownbutton in flutter, but I am getting an error
type 'String' is not a subtype of type 'MorphShape' of 'function result'
I have a class:
class MorphShape {
Shape value;
String name;
MorphShape(this.value, this.name);
}
I init a list of possible values for the dropdown
final List<MorphShape> morphShapes = [
MorphShape(Shape.rect, 'rect'),
MorphShape(Shape.cross, 'cross'),
MorphShape(Shape.ellipse, 'ellipse')
];
late MorphShape morphKernelShape = morphShapes[2];
and finally setup the dropdown
Center(
child: Padding(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 25),
child: DropdownButton(
value: morphKernelShape,
onChanged: (MorphShape? morphShape) {
setState(() {
morphKernelShape = morphShape!;
});
},
items: morphShapes.map<DropdownMenuItem<MorphShape>>(
(MorphShape value) {
return DropdownMenuItem(
value: value, child: Text(value.name));
}).toList(),
),
)),
The IDE itself doesn't highlight anything as a problem, but when I try to run my app it gives me the above stated error. I can't seem to figure out what is the problem here?
Here you're passing the MorphShape object to the value which accepts String :
value: morphKernelShape, // this should be String
onChanged: (MorphShape? morphShape) {
setState(() {
morphKernelShape = morphShape!; // here you're passing to it MorphShape object.
});
// ...
maybe you wanted to do this instead:
value: morphKernelShape.name,
The code works, if anyone else runs into a similar issue, it most likely is due to hot reload.
I add a button to add a list of widget in my screen, like this:
List<Widget> timeWidget = [];
buildTime() {
setState(
() {
timeWidget.add(Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
HourScheduleItem(
enabled: true,
day: _day,
onRemove: () {
timeWidget.remove(data);
},
onChanged: (date, hour) {
setState(
() {
_day = date;
_hour = hour;
print(_hour);
},
);
},
)
],
),
));
},
);
}
When a click onTap button buildTime(); the return is a constructor into Column in my screen:
Column(children: timeWidget.map((data) {
return data;
}).toList()),
But when I choose an option in the next widget added, the option chosen is shown only in the first widget, I believe this must be due to the fact that it does not get an index?
enter image description here
This due to how states and widgets in the widget tree are linked together. The flutter YouTube channel has a great in depth explanation of this topic, but TL;DR flutter doesn't "know" which widget in the list you actually clicked on unless you give each widget a unique identifier to tell them apart!
In almost all cases, you can do this by adding a key: UniqueKey() parameter to the constructor of the widget you're putting in a list. In your case, you would pass it in to your HourScheduleItem which would then pass it into the super() constructor.
I'm trying to allow a user to mark an item being built by a ListViewBuilder as a favorite. With my current code, when a user favorites one episode, all episodes are marked as favorite. I would like the user to be able to add each episode individually as a favorite and persist that favorite after a restart. I have the data saved to a firebase database but it seems like this should be handled in the app itself.
What is the best way to do this? Thanks!
Here is my current code:
class Epi {
final String endTime;
final String name;
final String networkName;
final String showName;
final String startTime;
Epi({this.endTime, this.name, this.networkName, this.showName, this.startTime});
factory Epi.fromJson(Map<dynamic, dynamic> parsedJson) {
DateTime endTimeCon = DateTime.parse(parsedJson['endTime']);
String newEndTime = formatDate(endTimeCon, [yyyy, '/', mm, '/', dd, ' ', hh, ':', nn, ':', ss, ' ', am]);
DateTime startTimeCon = DateTime.parse(parsedJson['startTime']);
String newStartTime = formatDate(startTimeCon, [yyyy, '/', mm, '/', dd, ' ', hh, ':', nn, ':', ss, ' ', am]);
return Epi(
endTime: newEndTime,
name: parsedJson['name'],
networkName: parsedJson['networkName'],
showName: parsedJson['showName'],
startTime: newStartTime,
);
}
}
bool _isFavorited = true;
void _toggleFavorite() {
setState(() {
if (_isFavorited) {
_isFavorited = false;
} else {
_isFavorited = true;
}
});
}
body: Column(
children: <Widget>[
SizedBox(height: 5.0),
Expanded(
child: ListView.builder(
itemCount: elist.length,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
selectEpisode(index);
},
child: Card(
child: Column(
children: <Widget>[
ListTile(
title: Text(elist[index].name),
subtitle: Text(elist[index].startTime),
leading: IconButton(
icon: (_isFavorited ? Icon(Icons.favorite_border) : Icon(Icons.favorite)),
color: Colors.red[500],
onPressed: _toggleFavorite,
),
trailing: Icon(Icons.arrow_forward_ios)
)
],
),
),
);
}),
),
],
)
In my Congress Fahrplan App (Github) I'm doing exactly what you want to achieve.
In favorite_provider I store the value in the object itself and add it to my list of favorited objects. Whenever an object is added to this list, the list is written to the disk as JSON with my file_storage class.
When the app is restarted, the objects are fetched from a REST API. Then I match their IDs with the objects from the local JSON and set whether they are favorited or not to restore the favorite state.
Making a favorite list of items basically differs based on the app design and you might as well develop your own logic for this purpose. Now, while what #benjaminschilling33 posted is true, you can also achieve this in a simple way.
What I would do is, add a boolean called isFavorite on the constructor like this:
class Epi {
final String endTime;
final String name;
final String networkName;
final String showName;
final String startTime;
bool isFavorite;
}
//initialize the isFavorite to false cause no item in your list is is favorite at the beginning
Epi({this.endTime, this.name, this.networkName, this.showName, this.startTime, this.isFavorite=false});
//lets create a list _episode which contains all the movie for demonstration purpose
List<Epi> _episode = [Epi(initialized data)];
//create a getter for the list
List<Epi> get episode{
return _episode.where((Epi episode) => episod.isFavorite).toList(); //this will return a list where the isFavorite is true
}
//You can then set your icon (in the list-tile) based on your isFavorite result
ListTile(
...
icon: Icons(elist[index].isFavorite?Icon(Icons.favorite):Icon(Icons.favorite_border);
)
//Then adjust the logic on your onPress
onPressed: (){
setState((){
elist[index].isFavorite=!elist[index].isFavorite //this will vary from true to false and vice versa when pressed
});
}
This is the simplest way to add list of items that is favorited by the user rather than building another list for the favorite section. What I wrote here is offline based test you can achieve and the key take away is the where property which is:
List<Epi> episode=[some data]
epsode.where((Epi episode)=>episode.isFavorite).toList();
You can use this method even after deploying your app to the cloud database by creating that attribute in your database based on the user's id.
I need to implement lists of buttons in a column depending on the data entry. So, for that I have to use for loop. Each button requires two entires id, text. I can make it with List. But it accepts only string value not the integer.
This is the code I tried.
code
Widget getTextWidgets(List<String> strings)
{
List<Widget> list = new List<Widget>();
for(var i = 0; i < strings.length; i++){
list.add(new ButtonForHome(
lable: strings[i],
onPressed: (){},
));
}
return new Column(children: list);
}
I want to put id in onPressed event. How can I implement in the Flutter?
You should use Listview instead of Column
SAMPLE CODE
getTextWidgets() {
return ListView.builder(
itemCount: yourList.length,
itemBuilder: (context, itemIndex) {
return MaterialButton(
child: Text(yourList[itemIndex]),
onPressed: () {
debugPrint('Clicked index $itemIndex');
});
});
}
Now your question
I want to put id in onPressed event. How can I implement in the Flutter?
You can create a POJO class like this
class DataModel{
String name;
int id;
DataModel(this.name, this.id);
}
Now create list of your POJO class
List<DataModel> list= List<DataModel>();
Now add data in your list like this
list.add(DataModel("name", 1));
list.add(DataModel("name", 2));
list.add(DataModel("name", 3));
Now you can use it like this way
getTextWidgets() {
return ListView.builder(
itemCount: list.length,
itemBuilder: (context, itemIndex) {
return MaterialButton(
child: Text(list[itemIndex].name),
onPressed: () {
debugPrint('Clicked item name '+list[itemIndex].name);
debugPrint('Clicked item ID '+list[itemIndex].id.toString());
});
});
}
Nilesh Rathod has indeed given the descriptive answer for the same. In flutter there is also, a way to achieve this, which is quite similar to POJO class, is
To create own widget and specify the fields needs to be passed when we are using the widget
Add the widget to the list, with the data specified for passing
You can track, the id, by pressing itself also
I can clearly see that, you have created your own widget named as ButtonForHome, which takes in label for now. What you can do is, to make your widget takes in two argument, and you can do it like this:
class ButtonForHome extends StatelessWidget {
final String label;
final int id; // this will save your day
// #required will not let user to skip the specified key to be left null
ButtonForHome({#required this.label, #required this.id});
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: RaisedButton(
color: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
child: Text(this.label),
onPressed: () => print(this.id) // Here you print your id using your button only
)
);
}
}
Now creating your button with a list, or adding via list
You can do it via ListView.builder()
You can do it via your way, i.e., List<Widget>.add()
I am gonna show the solution in your way only:
Widget getTextWidgets(List<String> strings){
List<Widget> list = new List<Widget>();
for(var i = 0; i < strings.length; i++){
list.add(ButtonForHome(
id: i, // passing the i value only, for clear int values
label: strings[i]
));
}
return Column(children: list);
}
With the new flutter in place, you don't need to do new every time while defining a widget. It understands now, so no need of const, new at all
So, wherever you populate your getTextWidget, it will show up the Widgte ButtonForHome, which has unique id, and label. Now the ButtonForHome, prints the id of that particular widget which was passed uniquely. So now, you can see your result happening.
I hope this is what you were looking for. Try it, and let me know.
I am trying to retrieve data from my firestore database and assign those values to a List in flutter
But the problem is even though I am able to retrieve the data, I can't assign it to a List
Here is how my data retrieving method:
Stream<List<News>> getNews(){
return _db.collection("news")
.snapshots()
.map((snapshot) => snapshot.documents.map((doc) => News.fromMap(doc.data, doc.documentID)).toList(),);
}
This is where I I try to get this data from the firestore to a List
Widget _showSearchBar(BuildContext context) {
List = FireStoreServiceApi().getNews(); //this produces an error, see below to see the error
List list = [
"Banuka",
"Banuka",
"Banuka",
];
return GFSearchBar(
// overlaySearchListHeight: 160.0,
searchList: list,
searchQueryBuilder: (query, list) {
return list
.where((item) => item.toLowerCase().contains(query.toLowerCase()))
.toList();
},
overlaySearchListItemBuilder: (item) {
return Container(
padding: const EdgeInsets.all(3),
child: Text(
item,
style: const TextStyle(fontSize: 18),
),
);
},
onItemSelected: (item) {},
);
}
But this produces:
A value of type 'Stream>' can't be assigned to a variable of type 'List'.
Try changing the type of the variable, or casting the right-hand type to 'List'
I don't know how to fix this and Can someone please help me?
You need to add type of list like below,
List<News> newsList = FireStoreServiceApi().getNews();