Flutter: Selected value doesn't display in the dropdown - list

I'm populating cities name from SQLite database and trying to display as a drop down list. I make it work by following a tutorial, but having a small issue. The selected value is not displayed in dropdown, it keep displaying default hint value. However, I was able to assign and retrieve correct selected value.
Here is my code:
cities.dart
class Cities {
int id;
String name;
Cities(this.id, this.name);
Cities.fromMap(Map<String, dynamic> json) {
this.id = json["id"];
this.name = json["name"];
}
Map<String, dynamic> toMap() => {
'id': null,
'name': name,
};
}
Function that retrieve and returns value from db:
Future<List<Cities>> getCitiesList() async {
Database db = await instance.database;
final citiesData = await db.query('cities');
if (citiesData.length == 0) return null;
List<Cities> citiesList = citiesData.map((item) {
return Cities.fromMap(item);
}).toList();
return citiesList;
}
The code which builds drop down, inside Widget build:
//these are defined above in the code
Cities _city;
final databaseHelper = DatabaseHelper.instance;
FutureBuilder<List<Cities>>(
future: databaseHelper.getCitiesList(),
builder: (BuildContext context, AsyncSnapshot<List<Cities>> snapshot) {
if (!snapshot.hasData) return CircularProgressIndicator();
return DropdownButton<Cities>(
items: snapshot.data
.map((city) => DropdownMenuItem<Cities>(
child: Text(city.name),
value: city,
))
.toList(),
onChanged: (Cities value) {
setState(() {
_city = value;
});
},
isExpanded: true,
// value: _city, //uncommenting this line breaks the layout
hint: Text('Select City'),
);
},
),
Error in the console:
'package:flutter/src/material/dropdown.dart': Failed assertion: line 620 pos 15: 'items == null || items.isEmpty || value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1': is not true.
Un-commenting this value: _city, add same error in display (displays error 8 times, instead of dropdown list).
Questions:
How can I fix this issue?
How can I set default value from the list? (which will be selected by default)

You can do it in simple way, just create a simple list of strings and pass that list to dropdown menu.
Here's how:
Update your getCitiesList() function:
Future<List<String>> getCitiesList() async {
Database db = await instance.database;
final citiesData = await db.query(tblCities);
if (citiesData.length == 0) return null;
return citiesData.map((Map<String, dynamic> row) {
return row["name"] as String;
}).toList();
}
Add this inside your form page:
//initialize these at top
List<String> _citiesList = <String>[];
String _city;
void _getCitiesList() async {
final List<String> _list = await databaseHelper.getCitiesList();
setState(() {
_citiesList = _list;
});
}
Call _getCitiesList(); inside initState().
Add this inside your build method:
DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _city,
items: _citiesList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String newValue) {
setState(() {
_city = newValue;
});
},
)),

Related

Using where clauses in firestore query with a list in parameter

I would like to use a where clause in my Firestore query, with a list in parameter.
I used a for loop to do this but when I want to add all my values in a variable, I have some problems with the add function (variable type problem).
I understand the problem but I don't what is the way I have to take to do this in another way ...
Can anyone help me?
Best regards :) ###It's my first question on stack! :D ###
in my main class:
var games = [
"uno",
"poker",
];
return StreamProvider<List<AppUserData>>.value(
initialData: [],
value: DatabaseService().users4(games),
child: Scaffold(....
in my database class
List<AppUserData> _list = [];
void addToList(AppUserData value) {
_list.add(value);
}
Stream<List<AppUserData>> users4 (List games) {
print(games);
var b;
var len = games.length;
for (var i = 0; i < len + 1; i++) {
b = FirebaseFirestore.instance.collection("users")
.where("games", isEqualTo: games[i]).snapshots().map(
_userListFromSnapshot);
print("$b");
addToList(b);
}
return b;
}
List<AppUserData> _userListFromSnapshot(
QuerySnapshot<Map<String, dynamic>> snapshot) {
return snapshot.docs.map((doc) {
return _userFromSnapshot(doc);
}).toList();
}
AppUserData _userFromSnapshot(DocumentSnapshot<Map<String, dynamic>> snapshot) {
var data = snapshot.data();
if (data == null) throw Exception("user not found");
return AppUserData(
uid: uid,
name: snapshot.data()['name'],
distance: snapshot.data()['distance'],
games: snapshot.data()['games'],
id: snapshot.data()['id'],
);
}
I think you're looking for whereIn
Try this:
Stream<List<AppUserData>> users4(List games) async* {
yield* FirebaseFirestore.instance
.collection("users")
.where("games", whereIn: games)
.snapshots()
.map((e) {
List<AppUserData> l =
e.docs.map((e) => _userFromMap(Map.from(e.data()))).toList();
return l;
});
}
AppUserData _userFromMap(Map<String, dynamic> map) {
if (map == null) throw Exception("user not found");
return AppUserData(
uid: uid,
name: map['name'],
distance: map['distance'],
games: map['games'],
id: map['id'],
);
}

item not removed in list when use list.remove(element) in flutter

I am having trouble removing an item in a list after adding the element.
List<CartItem> _items = [];
FirebaseFirestore? _instance;
void add(BuildContext context, CartItem item) {
_items.add(item);
AuthService authService = Provider.of<AuthService>(context, listen: false);
Map<String, dynamic> cartMap = Map();
_items.forEach((CartItem item) {
cartMap['title'] = (item.product as Product).title;
cartMap['name'] = (item.product as Product).name;
});
_instance = FirebaseFirestore.instance;
_instance!
.collection('cart')
.doc(authService.getCurrentUser()) //need to get logged in account's id
.update({
'cartProduct': FieldValue.arrayUnion([cartMap])
}).then((value) {
print(_items.length);
notifyListeners();
});
}
void remove(BuildContext context, CartItem item) {
_items.remove(item);
AuthService authService = Provider.of<AuthService>(context, listen: false);
Map<String, dynamic> cartMap = Map();
cartMap['title'] = (item.product as Product).title;
cartMap['name'] = (item.product as Product).name;
_instance = FirebaseFirestore.instance;
_instance!.collection('cart').doc(authService.getCurrentUser()).update({
'cartProduct': FieldValue.arrayRemove([cartMap]),
}).then((value) {
print(_items.length);
notifyListeners();
});
}
After I do add(context, widget.product) and print _items.length, the result is 1
However, after I do remove(context, widget.product) and print _items.length, the result is still 1.
Consumer<CartService>(
builder: (context, cart, child) {
Widget renderedButton;
if (cart.isProductAddedToCart(widget.product) == false) {
renderedButton = DefaultButton(
text: "Participate",
press: () {
print(cart.isProductAddedToCart(widget.product));
cartService.add(context, CartItem(product: widget.product));
print(cart.isProductAddedToCart(widget.product));
},
);
} else {
renderedButton = DefaultButton(
text: "Delete",
press: () {
print(cart.isProductAddedToCart(widget.product));
cartService.remove(
context, CartItem(product: widget.product));
print(cart.isProductAddedToCart(widget.product));
},
);
}
return renderedButton;
As in the code above, the remove() method is supposed to remove the same item that was added to the list using the add() method.
Just update the remove() to: (only change _items.remove(item); position)
void remove(BuildContext context, CartItem item) {
AuthService authService = Provider.of<AuthService>(context, listen: false);
Map<String, dynamic> cartMap = Map();
cartMap['title'] = (item.product as Product).title;
cartMap['name'] = (item.product as Product).name;
_instance = FirebaseFirestore.instance;
_instance!.collection('cart').doc(authService.getCurrentUser()).update({
'cartProduct': FieldValue.arrayRemove([cartMap]),
}).then((value) {
/// todo check firebase collection's deletion success first
_items.remove(item);
print(_items.length);
notifyListeners();
});
}
The item you're adding to the list may not be the same instance of the item you're removing. Make sure the item object/class have equality and hashcode implementation to compare the two items properly.
If you don't have control over the object, the following approach can be an easier alternative:
_items.removeWhere((_item) => _item.property == item.property);
^ where property can be the id of the product.

Future builder flutter firebase error: A value of type 'Iterable<BlogPost>' can't be assigned to a variable of type 'List<BlogPost>'

I am having an issue with creating a future builder. I am trying to create a List of Documents to show in a ListView but I can not find away to create the List in the custom class I have created(BlogPosts).
I keep receiving this error:
Error: A value of type 'Iterable<BlogPost>' can't be assigned to a variable of type 'List<BlogPost>'.
here is my code:
CollectionReference postsRef = FirebaseFirestore.instance.collection('posts');
late List<BlogPost> posts;
FutureBuilder(
future: postsRef.get().then((val) {
posts = val.docs.map((doc) => BlogPost.fromDocument(doc));
}),
builder: (context, snap) {
if(snap.connectionState == ConnectionState.done) {
return ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: 3,
itemBuilder: (context, index) {
return Text('data);
}
);
} else {
return Center(
child: Text('Loading')
);
}
}
)
here is the code for my custom class:
class BlogPost {
String displayName;
String postmsg;
String postId;
String UserId;
BlogPost(this.displayName, this.postmsg, this.postId, this.UserId);
factory BlogPost.fromDocument(DocumentSnapshot doc) {
return BlogPost(
doc['displayName'],
doc['postmsg'],
doc['postId'],
doc['UserId']
);
}
}
The map() function returns an Iterable. To turn that into a list, call toList() on its result. So:
posts = val.docs.map((doc) => BlogPost.fromDocument(doc)).toList()

How to delete duplicates of a List<MyDataModel> (Dart/Flutter)

I have a futurebuilder that builds the UI based on a List, it does the job, however I get duplicates due to the UI being built again and again whenever I navigate. My question is, is there a innate method in Dart that can remove duplicates from a list? I've tried this StackOverflow question however it doesn't work.
Here is my custom model:
class HomeList {
Widget navigateScreen;
String imagePath;
PatientInfo patientInfo;
HomeList({
this.navigateScreen,
this.imagePath = '',
this.patientInfo,
});
static List<HomeList> homeList = [];
}
Here is my function for the futureBuilder i'm getting the data from my cloud_firestore:
_getPatients() async {
if (didLoadpatients == 0) {
print('this is didloadpatients at start of func $didLoadpatients');
var document = await db
.collection('users')
.document(mUser.uid)
.collection('patients');
document.getDocuments().then((QuerySnapshot query) async {
query.documents.forEach((f) {
uids.add(f.data['uID']);
});
didLoadpatients++;
print('this is didloadpatients at end of func $didLoadpatients');
for (var i = 0; i < uids.length; i++) {
var userDocuments = await db.collection('users').document(uids[i]);
userDocuments.get().then((DocumentSnapshot doc) {
print(doc.data);
homeList.add(HomeList(
imagePath: 'assets/fitness_app/fitness_app.png',
patientInfo: new PatientInfo.fromFbase(doc.data)));
});
print(homeList);
}
});
} else
print('I am leaving the get patient function');
}
Future<bool> getData() async {
_getCurrentUser();
await Future.delayed(const Duration(milliseconds: 1500), () async {
_getPatients();
});
return true;
}
Any help would be appreciated thank you!
To remove duplicates you can use Set Data Structure instead of List.
Just use Set instead of List to get unique values only.
Before Adding you can Remove Element from model this will Work
dummymodel.removeWhere((m) => m.id == id);
dummymodel.add(dummymodel.fromJson(data));
To Remove Duplicates from Data Model simply use Set (Data structure),
Original List with Duplicate Entries:
List<MyDataModel> mList = [MyDataModel(1), MyDataModel(2), MyDataModel(1), MyDataModel(3)];
New List that removes duplicate Entries from your List<MyDataModel>:
List<MyDataModel> mNewList = list.toSet().toList();
Output:
The result will be like
MyDataModel(1), MyDataModel(2), MyDataModel(3)
To remove the duplicate elements from custom object list, you need to override == and hashcode methods in your POJO class and then add the items in Set and again convert set to list to remove duplicate objects. Below is the working code:-
class TrackPointList {
double latitude;
double longitude;
String eventName;
Time timeZone;
TrackPointList({
this.latitude,
this.longitude,
this.eventName,
this.timeZone,
});
#override
bool operator==(other) {
// Dart ensures that operator== isn't called with null
// if(other == null) {
// return false;
// }
if(other is! TrackPointList) {
return false;
}
// ignore: test_types_in_equals
return eventName == (other as TrackPointList).eventName;
}
int _hashCode;
#override
int get hashCode {
if(_hashCode == null) {
_hashCode = eventName.hashCode;
}
return _hashCode;
}
factory TrackPointList.fromJson(Map<String, dynamic> json) => TrackPointList(
latitude: json["latitude"].toDouble(),
longitude: json["longitude"].toDouble(),
eventName: json["eventName"],
timeZone: timeValues.map[json["timeZone"]],
);
Map<String, dynamic> toJson() => {
"latitude": latitude,
"longitude": longitude,
"eventName": eventName,
"timeZone": timeValues.reverse[timeZone],
};
}
Above is the POJO class. Now below is the method which helps you to filter the objects according to the eventName data member.
List<TrackPointList> getFilteredList(List<TrackPointList> list){
final existing = Set<TrackPointList>();
final unique = list
.where((trackingPoint) => existing.add(trackingPoint))
.toList();
return unique;
}
This will work definitely.
Please +1 if it helps you.
I've come up with quite a brute force solution. Instead of
_getPatients() async {
if (didLoadpatients == 0) {
print('this is didloadpatients at start of func $didLoadpatients');
var document = await db
.collection('users')
.document(mUser.uid)
.collection('patients');
document.getDocuments().then((QuerySnapshot query) async {
query.documents.forEach((f) {
uids.add(f.data['uID']);
});
didLoadpatients++;
print('this is didloadpatients at end of func $didLoadpatients');
for (var i = 0; i < uids.length; i++) {
var userDocuments = await db.collection('users').document(uids[i]);
userDocuments.get().then((DocumentSnapshot doc) {
print(doc.data);
homeList.add(HomeList(
imagePath: 'assets/fitness_app/fitness_app.png',
patientInfo: new PatientInfo.fromFbase(doc.data)));
});
print(homeList);
}
});
} else
print('I am leaving the get patient function');
}
I've done what #Jay Mungara says and clear my Set everytime my UI rebuilds:
_getPatients() async {
homeList.clear();
if (didLoadpatients == 0) {
print('this is didloadpatients at start of func $didLoadpatients');
var document = await db
.collection('users')
.document(mUser.uid)
.collection('patients');
document.getDocuments().then((QuerySnapshot query) async {
query.documents.forEach((f) {
uids.add(f.data['uID']);
});
didLoadpatients++;
print('this is didloadpatients at end of func $didLoadpatients');
for (var i = 0; i < uids.length; i++) {
var userDocuments = await db.collection('users').document(uids[i]);
userDocuments.get().then((DocumentSnapshot doc) {
print(doc.data);
homeList.add(HomeList(
imagePath: 'assets/fitness_app/fitness_app.png',
patientInfo: new PatientInfo.fromFbase(doc.data)));
});
print(homeList);
}
});
} else
print('I am leaving the get patient function');
}
Thank you for all your answers!
this is a small examples to remove duplicate element
removeDuplicate() {
List<dynamic> demoList = [
{"userId": 1, "id": 1, "name": "thappu1"},
{"userId": 2, "id": 2, "name": "appu"},
{"userId": 1, "id": 1, "name": "thappu1"},
{"userId": 2, "id": 2, "name": "appu"},
{"userId": 2, "id": 2, "name": "appu"},
{"userId": 2, "id": 2, "name": "appu"},
{"userId": 2, "id": 2, "name": "appu"},
];
var toRemove = {};
demoList.forEach((e) {
toRemove.putIfAbsent("$e", () => e);
});
print(toRemove.keys.toList());
}
output is
[{userId: 1, id: 1, name: thappu1}, {userId: 2, id: 2, name: appu}]

EXTJS grid store load - adding parameters?

I'm in the process on converting an asp repeater into an EXTJS grid. Above the repeater is a dropdown and a radiobutton list. The dropdown selects which clients' data the repeater shows, and the radiobuttonlist selects the query type (default, resource, or role). Currently, when the ddl or radiobutton is changed, the page postsback with the new data.
I'm not sure how to pass the value of these two objects into my static webservice on the backend via the extjs store api GET call.
The extjs store code...
store: Ext.create('Ext.data.Store', {
autoLoad: true,
autoSync: false,
model: 'Assembly',
proxy: {
type: 'ajax',
headers: { "Content-Type": 'application/json' },
api: {
read: '/Admin/BillRateData.aspx/Get'
},
reader: {
type: 'json',
root: function (o) {
if (o.d) {
return o.d;
} else {
return o.children;
}
}
},
writer: {
type: 'json',
root: 'jsonData',
encode: false,
allowSingle: false
},
listeners: {
exception: function (proxy, response, operation) {
Ext.MessageBox.show({
title: "Workflow Groups Error",
msg: operation.action + ' Operation Failed: ' + operation.getError().statusText,
icon: Ext.MessageBox.ERROR,
buttons: Ext.Msg.OK
});
}
}
}
And the webservice...(with some psuedocode)
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true)]
public static List<BillRate> Get()
{
using (TimEntities db = new TimEntities())
{
int tableId = Int32.Parse(ddlTable.SelectedValue);
var defaultQry = from t1 in db.BillCostTableDatas
where t1.TableId == tableId
&& t1.ResourceId == 0 && t1.RoleId == 0
orderby t1.Rate
select new
{
id = t1.Id,
resource = "",
role = "",
rate = t1.Rate,
TierName = ""
};
var resourceQry = from t1 in db.BillCostTableDatas
join t2 in db.Machines on t1.ResourceId equals t2.Machine_ID
join t3 in db.TOMIS_USER on t2.Machine_User_ID equals t3.User_ID
join t4 in db.PricingTierNames on t1.PricingTierID equals t4.TierID
where t1.TableId == tableId
&& t1.ResourceId != 0
&& t1.RoleId == 0
orderby t3.LName, t3.FName, t1.Rate, t4.TierName
select new
{
id = t1.Id,
resource = t3.LName + ", " + t3.FName,
role = "",
rate = t1.Rate,
TierName = t4.TierName
};
var roleQry = from t1 in db.BillCostTableDatas
join t2 in db.TaskRoles on t1.RoleId equals t2.Id
where t1.TableId == tableId
&& t1.ResourceId == 2 && t1.RoleId != 0
orderby t2.Name, t1.Rate
select new
{
id = t1.Id,
resource = "",
role = t2.Name,
rate = t1.Rate,
TierName = ""
};
if (this.rblOptions.SelectedValue == "resource")
{
var results = from Res in resourceQry.ToList()
select new BillRate
{
};
return results.ToList();
}
else if (this.rblOptions.SelectedValue == "role")
{
var results = from Res in roleQry.ToList()
select new BillRate
{
};
return results.ToList();
}
else
{
var results = from Res in defaultQry.ToList()
select new BillRate
{
};
return results.ToList();
}
return null;
}
}
If you trigger your store loading manually, you can pass the params options to the load method.
Example:
var store = Ext.create('Ext.data.Store', {
// prevent the store from loading before we told it to do so
autoLoad: false
...
});
store.load({
params: {clientId: 123, queryType: 'default'}
...
});
If you want the params to be sent for multiple subsequent queries, you can write them in the extraParams property of the proxy.
Example:
var store = Ext.create('Ext.data.Store', { ... });
Ext.apply(store.getProxy().extraParams, {
clientId: 321
,queryType: 'role'
});
// the store will still need a refresh
store.reload();
The way these params are passed to the server will depend on the type of request. For GET ones, they will be appended as query params; for POST they will be embedded in the request body.