hey i am new too flutter, i want to create combined filter screen alike in a e commerce app,for example product will have 3 field Brand,price range,size,i want to display data from firebase snapshot by combining, brand ,price and size
stream:
FirebaseFirestore.instance.collection("users").snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final brandss = FirebaseFirestore.instance
.collection('users')
.where('name', isEqualTo: 'zara')
.snapshots();
if (snapshot.hasData && snapshot.data != null) {
return Expanded(
child: ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
Map<String, dynamic> userMap =
snapshot.data!.docs[index].data()
as Map<String, dynamic>;
return ListTile(
leading: CircleAvatar(
backgroundImage:
NetworkImage(userMap["profilepic"]),//image
),
title: Text(
userMap["brand"] + " (${userMap["price"]})"),
subtitle: Text(userMap["email"]),
trailing: IconButton(
onPressed: () {
// Delete
},
icon: Icon(Icons.delete),
),
);
},
),
);
} else {
return Text("No data!");
}
``` please help me with logic
you can try to filter 3 conditions with multiple where
FirebaseFirestore.instance.Collection('product').where('Brand', isEqualTo:Brand).where('price', isLessThanOrEqualTo: maxPrice).where('price', isGreaterThanOrEqualTo: minPrice).where('size',isEqualTo:size)
Related
I am currently attempting to make a user search list within an alert dialog, which will query users from the project's database based on the user's search input. I am doing this in Android Studio, using Flutter's native language (Dart) and Firebase Cloud Firestore. I have the search bar itself working, but for some reason, whenever I try to actually get the results from the database, my code will access the stream for the Streambuilder being used, but will never touch the actual builder, skipping it entirely. What exactly am I doing wrong here?
The function responsible for creating the alert dialog:
Future createAlertDialog(BuildContext context){
String userToSearch = '';
bool showUsers = false;
return showDialog(context: context, builder: (context){
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Container(
child: CupertinoSearchTextField(
onChanged: (value) => {
setState(() {
showUsers = true;
}),
showUsers
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(value),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.connectionState ==
ConnectionState.none) {
return const Center(child: Text("Internet error"));
}
if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong."),
);
}
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProfileScreen(
uid: snapshot.data!.docs[index]['uid'],
),
),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data!.docs[index]['photoUrl'],
),
radius: 16,
),
title: Text(
snapshot.data!.docs[index]['username'],
),
);
},
);
},
),
)
: const Expanded(child: Text("error"))
}
),
),
)
);
});
}
Function responsible for querying the database:
Stream searchUsers(String userInput){
String? currentUserID = FirebaseAuth.instance.currentUser?.uid;
//String? valueFromFirebase = '';
Stream s = FirebaseFirestore.instance.collection('users').where('username', isGreaterThanOrEqualTo: userInput).orderBy('username', descending: false).snapshots();
return s;
}
To be clear, I expected this code to create a list of users from the database, under the search bar in the alert dialog, containing the users that match the current input. I tried debugging, changing the positioning of certain lines of code, and comparing and contrasting my code to code I found all over the internet. The actual result that I received was the ability to use the search bar and have the input saved properly, but literally nothing happens after pressing enter. No list is rendered, no error is thrown, and the program continues like nothing happened.
You need to place StreamBuilder inside widget tree to make it visible. Currently having inside onChanged which is just callback method for textFiled.
Future createAlertDialog(BuildContext context) {
String userToSearch = '';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Column(
children: [
CupertinoSearchTextField(
onChanged: (value) {
setState(() {
userToSearch = value;
});
},
),
userToSearch.isNotEmpty
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(userToSearch),
...........
),
)
: Text("Empty")
],
),
),
);
});
I'm creating an grocery app in Flutter with firebase but i am unable show the updated cart value to text widget.
This is my List view
var fire_storedb = FirebaseFirestore.instance.collection("vegetables").snapshots();
Container(
margin: const EdgeInsets.only(top: 2),
alignment: Alignment.center,
child: StreamBuilder(
stream: fire_storedb,
builder: ((context, snapshot) {
if (!snapshot.hasData) return const CircularProgressIndicator();
return (ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return (grocery_list(snapshot, index, context,values45));
},
));
})),
),
Below is my grocery_list function which is called from ListView ........
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
InkWell(
child: Icon(FontAwesomeIcons.minus),
onTap: () async {
String? grocery_id =
snapshot.data?.docs[index].reference.id;
FirebaseFirestore.instance
.collection("Cart")
.where("grocery_id", isEqualTo: grocery_id)
.get()
.then((value) {
value.docs.forEach((element) {
FirebaseFirestore.instance
.collection("Cart")
.doc(element.id)
.delete()
.then((value) {
print("Success!");
});
});
});
}), //Inkwell for delete item from cart
VerticalDivider(width: 10,), ////// Vertical Divider
VerticalDivider(width: 10,), ////// Vertical Divider
InkWell(
child: Icon(FontAwesomeIcons.plus),
onTap: () async {
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
var email =
sharedPreferences.getString("email").toString();
String? docid =
snapshot.data?.docs[index].reference.id;
Map<String, String?> data_to_save = {
"grocery_id": docid,
"quantity": "1",
"email": email,
"name": snapshot.data!.docs[index]['name'],
"price": snapshot.data!.docs[index]['price'],
"si": snapshot.data!.docs[index]['si'],
"image": snapshot.data!.docs[index]['image'],
};
var collectionRef = await FirebaseFirestore.instance
.collection("Cart");
collectionRef.add(data_to_save);
},
), // Inkwell for add item to cart
],
),
I want to place the below code between the two vertical divider as a text wideget to show the no of items added to cart. Can someone help.? I'm able to get the cart value in cart_value but unable to display it to Text widget.
FirebaseFirestore.instance.collection("Cart").get().then((value) {
value.docs.forEach((element) {
FirebaseFirestore.instance.collection("Cart").doc(element.id).get().then((value2) => {
if(value2.data()!['grocery_id']==docid)
cart_value = (value2.data()['quantity'])
});
});
});
You should be using a Future method to fetch data from Firestore and return an integer or double value of cart_value like this :
int cart_value = 0;
Future<int> cart() async {
var cart = await FirebaseFirestore.instance.collection("Cart").get();
for (var element in cart.docs) {
FirebaseFirestore.instance.collection("Cart").doc(element.id).get().then((value2) => {
if(value2.data()!['grocery_id']==docid){
cart_value = (value2.data()!['quantity'])
}
});
}
return cart_value;
}
and put the Future method cart in the future of your FutureBuilder widget:
FutureBuilder(
future: cart(),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.toString());
}})
Hi in my flutter app have FutureBuilder that return listview, my list listview create some button for update the hive table. when I click the first time on one of buttons everything is run smoothly, but when I click on same button again my hive key turn to null and program show my this error: "type 'Null' is not a subtype of type 'int' "
I write print all over my code but still I do not get it why the key turn null from the second time.
How can I Correct this? please help my.
my Futurebuilder body is:
FutureBuilder<List>(
future: controller.showTaskList(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return SizedBox(
height: Get.height,
child: const Center(
child: CircularProgressIndicator(),
),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List data = snapshot.data ?? [];
return ListView.separated(
scrollDirection: Axis.vertical,
physics:
const BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
// controller.taskIconCheckList
// .clear();
for (int i = 0;
i < data.length;
i++) {
if (data[i].status == true) {
controller.taskIconCheckList
.add(true.obs);
} else {
controller.taskIconCheckList
.add(false.obs);
}
}
return ListTile(
leading: Obx(
() => PageTransitionSwitcher(
transitionBuilder: (
child,
primaryAnimation,
secondaryAnimation,
) {
return SharedAxisTransition(
animation:
primaryAnimation,
secondaryAnimation:
secondaryAnimation,
transitionType:
SharedAxisTransitionType
.horizontal,
fillColor:
Colors.transparent,
child: child,
);
},
duration: const Duration(
milliseconds: 800),
child: controller
.taskIconCheckList[
index]
.value
? SizedBox(
child: IconButton(
icon: const Icon(
Icons
.check_circle_rounded,
color: Colors
.lightGreenAccent,
),
onPressed: () {
controller
.functionTaskIconCheckList(
index,
);
print('طول دیتا');
print(data.length.toString());
print('مقدار ایندکس');
print(index.toString());
print('مقدار کلید');
print(data[index].key.toString());
print(data[index].taskText.toString());
controller
.updateStatusTask(
index,
data[index]
.key); // here when i first click // return key currectly, but after that show null and updatestatusetask not run and show error.
},
),
)
: IconButton(
onPressed: () {
controller
.functionTaskIconCheckList(
index,
);
print('طول دیتا');
print(data.length.toString());
print('مقدار ایندکس');
print(index.toString());
print('مقدار کلید');
print(data[index].key.toString());
print(data[index].taskText.toString());
controller
.updateStatusTask(
index,
data[index]
.key); // here when i first click // return key currectly, but after that show null and updatestatusetask not run and show error.
},
icon: const Icon(
Icons
.radio_button_unchecked_outlined,
color: Colors.red,
),
),
),
),
title: Text(data[index].taskText,
style: normalTextForCategory),
subtitle: Text(
data[index]
.date
.toString()
.substring(0, 10),
textDirection:
TextDirection.ltr,
textAlign: TextAlign.right,
style: normalTextForSubtitle,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
myDefaultDialog(
'هشدار',
'آیا از حذف این گزینه اطمینان دارید؟',
'بله',
'خیر',
() {
Get.back();
mySnakeBar(
'',
'گزینه مورد نظر با موفقیت حذف شد.',
Icons
.warning_amber_rounded,
Colors.yellow);
},
);
},
icon: const Icon(
Icons.delete),
color: Colors.redAccent,
),
IconButton(
onPressed: () {
Get.offNamed(
Routs.editTaskScreen,
arguments: 'edit');
},
icon: const Icon(
Icons.edit_calendar,
color:
Colors.yellowAccent,
),
),
],
),
);
},
separatorBuilder:
(BuildContext context,
int index) {
return const Divider(
height: 2,
color: Colors.white70,
);
},
);
}
}
},
),
this is my functionTaskIconCheckList form controller:
functionTaskIconCheckList(int index) {
taskIconCheckList[index].value = !taskIconCheckList[index].value;}
and this the updatestatusetask function
updateStatusTask(int index,int taskKey) async {
print('در تابع آپدیت ایندکس هست: ${index.toString()}');
print('در تابع آپدیت کی هست: ${taskKey.toString()}');
var taskBox = await Hive.openBox('task');
var filterTask = taskBox.values.where((task) => task.key == taskKey).toList();
Task task = Task(
filterTask[0].taskText,
filterTask[0].date,
taskIconCheckList[index].value,
filterTask[0].deleteStatus,
null,
null,
filterTask[0].taskCatId,
filterTask[0].userId);
await taskBox.put(taskKey, task);}
and this is my showtasklist function:
Future<List> showTaskList() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var taskBox = await Hive.openBox('task');
var filterTask = taskBox.values
.where((task) => task.userId == sharedPreferences.getInt('key'))
.toList();
return filterTask;}
this is my model:
#HiveType(typeId: 2)
class Task extends HiveObject{
#HiveField(0)
String taskText;
#HiveField(1)
DateTime date;
#HiveField(2)
bool status;
#HiveField(3)
bool deleteStatus;
#HiveField(4)
int taskCatId;
#HiveField(5)
int userId;
#HiveField(6)
User? user;
#HiveField(7)
TaskCat? taskCat;
Task(this.taskText, this.date, this.status, this.deleteStatus, this.user,
this.taskCat, this.taskCatId, this.userId);
}
One possible solution would be to wait for the Future function to finish and then load the list. If it tries to load the list early before finishing up the Future function, it might presume the value to be null.
Hope this helps.
Still I do not know what is cause this problem, But I found an alternative temporary solution. I create temporary Int list. then just before the return listTile in the futureBuilder body, I write the Loop and save all of the key in that list. finally instead of pass the "data[index].key." I pass my key from that temporary Int list. so everything work fine now
this is my part of code change from before, but still I want know main solution.
return ListView.separated(
scrollDirection: Axis.vertical,
physics:
const BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
// controller.taskIconCheckList
// .clear();
for (int i = 0;
i < data.length;
i++) {
Get.find<
HomeScreenController>()
.taskKey.add(data[i].key);
if (data[i].status == true) {
Get.find<
HomeScreenController>()
.taskIconCheckList
.add(true.obs);
} else {
Get.find<
HomeScreenController>()
.taskIconCheckList
.add(false.obs);
}
}
return ListTile(
I want to make a to-do list with task due date as an optional field, so I need to check if some tasks have dueDate and add it as a subtitle based on that. How can I check if a field exists inside a doc in a StreamBuilder?
class _TaskListState extends State<TaskList> {
var myStream;
#override
void initState() {
myStream = FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.snapshots();
super.initState();
}
...
void _updateTaskDesc(
dynamic currTask, String newDesc, DateTime newDate, TimeOfDay newTime) {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.update({
'desc': newDesc,
'dueDate': newDate.toString(),
'dueTime': newTime.toString(),
});
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: myStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: SizedBox(
height: 100, width: 100, child: CircularProgressIndicator()),
);
} else {
final docs = snapshot.data.docs;
bool hasDateTime = ????? <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
return ListView.builder(
itemCount: docs.length,
itemBuilder: (ctx, index) {
final currTask = docs[index];
return InkWell(
highlightColor: Theme.of(context).secondaryHeaderColor,
splashColor: Theme.of(context).secondaryHeaderColor,
onLongPress: () {
showModalBottomSheet<dynamic>(
isScrollControlled: true,
context: context,
builder: (bCtx) {
FocusManager.instance.primaryFocus?.unfocus();
return TaskOptions(_updateTaskDesc,
() => _updateHasImage(docs[index]), currTask);
},
);
},
child: Dismissible(
direction: DismissDirection.startToEnd,
key: UniqueKey(),
onDismissed: (_) async {
FirebaseFirestore.instance
.collection('tasks')
.doc(widget.uid)
.collection('mytasks')
.doc(currTask['id'])
.delete();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("${currTask['desc']} dismissed"),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
FirebaseFirestore.instance
.collection("tasks")
.doc(widget.uid)
.collection("mytasks")
.doc(currTask['id'])
.set({
"desc": currTask['desc'],
"id": currTask['id'],
"isDone": currTask['isDone'],
"hasImage": currTask['hasImage'],
});
try {
FirebaseFirestore.instance
.collection("tasks")
.doc(widget.uid)
.collection("mytasks")
.doc(currTask['id'])
.update({
"dueDate": currTask['dueDate'],
"dueTime": currTask['dueTime'],
});
} catch (e) {}
},
),
),
);
},
child: ListTile(
...
subtitle: Text(hasDateTime
? DateFormat('dd/MM')
.format(DateTime.parse(currTask['dueDate']))
: ''),
...
I saw that a containsKey('key') method works for some people but I get NoSuchMethod when I try that. What can I do?
The single document is just a normal Dart Map, so you can check if a key exists or not using containsKey method.
So you condition becomes the following:
bool hasDateTime = currTask.containsKey('dueDate`);
NOTE: In the question I can see that you are defining the condition in the wrong place which is outside the itemBuilder method in the ListView so that it is not item based and well not work because it does not make sense.
You can have it in this place:
...
itemBuilder: (ctx, index) {
final currTask = docs[index];
bool hasDateTime = currTask.containsKey('dueDate`);
return InkWell(
...
I'm new with Flutter. Currently I am trying to do the CRUD. But then I got some error to delete the data by ID. I did manage to do the delete operation but then it will delete the latest inserted data instead, not the data that onTap. Here I attach my source code.
String docId;
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('taks');
DocumentSnapshot ds;
return new StreamBuilder(
stream: users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
ds = snapshot.data.docs[index];
// children: snapshot.data.docs.map((document) {
return new ListTile(
title: new Text(ds['task']),
subtitle: Wrap(
children: <Widget>[
Text("Priority: " + ds['priority']),
Text(" | Status: " + ds['status']),
],
),
onTap: (){
docId = ds.id;
print(docId);
},
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.update_rounded,
size: 20.0,
color: Colors.brown[900],
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => UpdateScreen(docId)));
}
),
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: Colors.brown[900],
),
onPressed: () async {
try {
FirebaseFirestore.instance
.collection("taks")
.doc(docId)
.delete()
.then((_) {
print("success!");
});
}
catch (e) {
print("ERROR DURING DELETE");
}
// _onDeleteItemPressed(index);
},
),
],
),
// subtitle: new Text(document['priority']),
);
});
// );
},
);
So, I tried to print the docId on which row that been selected. I tap all the data but it will only read the latest data id only.
So can anyone help me to sort out this problem on how to delete the data that been selected only, not always delete the latest data? Thank you in advanced
I'm sure I understand what exactly it is you want to delete, but your function tells Firebase to delete the entire document with the ID you are passing.
You also are defining `String docId' to your whole widget and using it for all your ListView.Builder items.
Try this:
ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
ds = snapshot.data.docs[index];
String docIdTobeDeleted= ds.id;
// children: snapshot.data.docs.map((document) {
return new ListTile(
title: new Text(ds['task']),
subtitle: Wrap(
children: <Widget>[
Text("Priority: " + ds['priority']),
Text(" | Status: " + ds['status']),
],
),
onTap: (){
//you won't be needing this anymore, instead you can type:
print(docIdTobeDeleted);
//docId = ds.id;
//print(docId);
},
and for firebase below, use this:
onPressed: () async {
try {
FirebaseFirestore.instance
.collection("taks")
.doc(docIdTobeDeleted)
.delete()
.then((_) {
print("success!");
});
}
catch (e) {
print("ERROR DURING DELETE");
}
It should work.
your Listtile onTap will set the docID to the selected tileID.. and the deleteIconButton will delete the id of docID.. so if you tap on the first ListTile and tap on any of the deleteIconButton.. It will delete the first ListTile
You can use the direct ds.id instead of docID in the deleteIconButton
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: Colors.brown[900],
),
onPressed: () async {
try {
FirebaseFirestore.instance
.collection("taks")
.doc(ds.Id)
.delete()
.then((_) {
print("success!");
});
}
catch (e) {
print("ERROR DURING DELETE");
}
// _onDeleteItemPressed(index);
},
),