Im trying to write code to get (doc id) from Firebase [closed] - flutter

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 months ago.
Improve this question
Look at my database structure:
and here is my code that I want to use ID in :
Widget build(BuildContext context) {
return SafeArea(
child: InkWell(
borderRadius: BorderRadius.circular(30),
child: Dismissible(
key: UniqueKey(),
direction: DismissDirection.startToEnd,
background: Container(
color: Colors.red,
child: Row(
children: [
Icon(Icons.delete),
Text(
'Move to trash',
style: TextStyle(
color: Colors.white,
fontFamily: 'Righteous',
),
)
],
),
),
confirmDismiss: (DismissDirection direction) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Delete Confirmation"),
content: Text("Are you sure you want to delete this item?"),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text("Delete")),
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text("Cancel"),
),
],
);
});
},
onDismissed: (DismissDirection direction) async {
if (direction == DismissDirection.startToEnd) {
print('item deleted');
}
await deleteCar(
'wam4jSgeIpWHIBLVXvmv'); //I want to get doc ID to delete it
},

There is some way:
FirebaseFirestore.instance
.collection('$YOUR_COLLECTION')
.where('uid', isEqualTo: "$UID_OF_THAT_ITEM").limit(1).get().then((value) => value.docs.first.id);
As you get it value.docs.first.id is what you need.

Not sure if I understand what you triying to achieve. But the way I see it, you can duplicate that id as an atribute of the element when you create it for example.
"aasdasd" :{
"id": "aasdasd",
"carName": "car",
}
or when you map cars, use the key you got as an atribute of your Car Model. This is an example for products.
static Future loadProducts() async {
final url = Uri.https(_baseUrl, 'products.json');
final res = await http.get(url);
final Map<String, dynamic> productsMap = json.decode(res.body);
final List<Product> products = productsMap.keys
.map((key) => Product(
id: key,
name: productsMap[key]['name'],
description: productsMap[key]['description'],
price: productsMap[key]['price'],
imagen: productsMap[key]['imagen'],
isAvailable: productsMap[key]['isAvailable'],
))
.toList();
return products;
}
'key' is the value you want.

this line solve the problem :
String gg = await FirebaseFirestore.instance
.collection('carsData')
.where('uid', isEqualTo: loggedInUser.uid)
.where('CarName', isEqualTo: nameCar)
.limit(1)
.get()
.then((value) => value.docs.first.id);
but when you have 2 items have the same CarName you must add another where() to get specific id.

FirebaseFirestore.instance
.collection('carsData')
.where('uid', isEqualTo: 'selected_car_uid')
.get()
.then((value) {
value.docs.forEach((element) {
print(element.id); // you will get your firestore id and then delete via this id.
FirebaseFirestore.instance
.collection("carsData")
.doc(element.id)
.delete()
.then((value_2) {
print('========> successfully deleted');
});
});
});
HAPPY CODING :)

Related

How to display all data in array?

I'm trying to display data from an array in firestore. I displayed it, but only [0] in the array is showing. I'm trying to get all the data in the array to show.
builder: (_, AsyncSnapshot<List<DocumentSnapshot>> snapshot){
if(snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: ((_, index) {
List<Widget> tiles = [];
for (Map post in snapshot.data![index]['posts']) {
tiles.add(
Expanded(
child: Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(1),
decoration: BoxDecoration(border: Border.all(color:Colors.black)),
child: Center(
child: ListTile(
title: Text(post['postText'], style: TextStyle(color: Colors.white),),
subtitle: Text(post['fromUser'], style: TextStyle(color: Colors.white),),
),
),
),
)
);
}
return Expanded(
child: ListView(
children: tiles,
),
);
}),
);
}
else{
return Center(child: CircularProgressIndicator(),);
}
},
enter image description here
Edit
To answer your qn about newest to oldest:
I suggest you put a FieldValue.timestamp field in your group chat documents! Then, you can order them like this:
Future<List<DocumentSnapshot>> getDoc(groupID) async {
var firestore = FirebaseFirestore.instance;
QuerySnapshot qn = await firestore.collection('groups')
.where('groupChatId', isEqualTo: groupID)
.orderBy('timestamp', descending: true) // <- Here!
.get();
return qn.docs;
}
(All of that I copied by hand, since you hadn't provided this code as text, as I asked you to!... 😆)
If you don't have a timestamp field, there is a way to still find out when a document was created... but I don't know how. Plus, in this case, I guess you want the time a certain FIELD was created in the document...! I don't know if that's possible. In fact, for that you'll probably have to do:
List<Map> posts = snapshot.data![index]['posts'];
// Sort list according to the 'date' field in each Map in the list:
posts.sort((mapA, mapB){
return mapA['date'].compareTo(mapB['date']);
});
// Then you'll use posts in your for-loop instead of snapshot.data![index]['posts']:
for (Map post in posts) {
tiles.add( /*etc*/);
}
Btw, if you want it to update when new messages come in, you can do like this:
import 'dart:async';
// Put the below in the State of a StatefullWidget:
StreamSubscription<QuerySnapshot<Map<String, dynamic>>>? qn;
List<DocumentSnapshot>? eventDocs;
void getDocStream(groupID) async {
var firestore = FirebaseFirestore.instance;
qn = firestore.collection('groups')
.where('groupChatId', isEqualTo: groupID)
.orderBy('timestamp', descending: true)
.snapshots().listen((event) {
// Put here everything you want to happen when new things happen in the stream!
// For example:
setState(() {
eventDocs = event.docs;
});
// Now, you can use eventDocs instead of getDoc(groupID), as you did before.
// Just remember that it will be null at first!
});
}
#override
void dispose() {
if (qn != null) qn!.cancel(); // This is to prevent the stream from going on, after you've left the page or even closed the app...
super.dispose();
}
Old answer:
But you're telling it to display only post [0]!...
If there are more posts in each document, and you want to display all of them, you need to make a for-loop or something. For example:
itemBuilder: ((_, index) {
List<Widget> tiles = [];
for (Map post in snapshot.data![index]['posts']) {
tiles.add(
ListTile(
title: Text(post['postText']),
subtitle: Text(post['fromUser']),
));
}
return Expanded(
child: Column(
children: tiles,
),
);
}),
And btw... Next time you ask a qn, plz paste your code as text rather than an image! So that we can copy-paste it into our answer, rather than having to retype it from the image. It's so easy to make a mistake and then you get an error coz we didn't copy it right.
try this
title: Text(snapshot.data![index]['posts']['postText']),

Flutter: Create unique values from Firestore for dropdown menu

I have a dropdown menu that is being populated from Firestore with this code.
FutureBuilder<QuerySnapshot>(
future: FirebaseFirestore.instance
.collection('field_management')
.orderBy('cultivar_name')
.get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox(
height: 15.0,
width: 15.0,
child: Center(
child: CircularProgressIndicator(),
),
);
}
return DropdownButton(
onChanged: (newValue) {
setState(() {
cultivarDropdownValue =
newValue.toString();
});
},
hint: Text(cultivarDropdownValue),
items: snapshot.data!.docs
.map((DocumentSnapshot document) {
return DropdownMenuItem<String>(
value: document['cultivar_name'],
child:
Text(document['cultivar_name']),
);
}).toList(),
);
},
),
How can I create a unique list from the Firestore data to avoid duplicates in the dropdown menu?
I have tried adding items to the list in the initState() method, still received all the values from the collection (code below). Not sure what else I can try to make this work.
addDropDownItems() async {
return await FirebaseFirestore.instance
.collection('field_management')
.get()
.then((snapshot) {
for (dynamic document in snapshot.docs) {
if (!blockList.contains(document.data())) {
blockList.add(document.data());
}
}
print('block list: ${blockList}');
});
}
I stumbled across this post (How can I distinct a complex object list in DART), which solved the problem.
I made use of the darq package and just had to add this line above my print statement:
var uniqueList = blockList.distinct();

Flutter FutureBuilder Snapshot is null but Future Does return data

While working with Flutter for a new application client for Kanboard, I encountered the following problem. I have a FutureBuilder that should return a select dropdown menu with items but, for some reason, the Snapshot data is null, although the Future method does resolves and has data on return.
Full page.dart code here: https://pastebin.com/J48nxsdZ
The block having the problem is the following:
Widget _columnSelect() {
return FutureBuilder(
future: columnProvider.getColumns(task.projectId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
List<DropdownMenuItem<String>> columnList = [];
if (snapshot.hasData) {
columnList.add(DropdownMenuItem<String>(
child: Text('Select Column'), value: 0.toString()));
_columns = snapshot.data;
} else {
columnList.add(DropdownMenuItem<String>(
child: Text('Loading..'), value: 0.toString()));
}
_columns.forEach((column) {
columnList.add(DropdownMenuItem<String>(
child: Container(
child: Text(
column.title,
),
),
value: column.id.toString()));
});
return Container(
// margin: EdgeInsets.only(left: 40.0),
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DropdownButtonFormField(
icon: Padding(
padding: const EdgeInsets.only(right: 12),
child: Icon(Icons.view_column, color: Colors.blue),
),
items: columnList,
value: _columnId,
decoration: InputDecoration(helperText: 'Optional'),
onChanged: (newValue) {
_columnId = newValue;
},
),
);
},
);
}
This is a duplicate of a widget in the same form for a user dropdown select. The original widget (in the same page) is the following:
Widget _ownerSelect() {
return FutureBuilder(
future: userProvider.getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
List<DropdownMenuItem<String>> usernameList = [];
if (snapshot.hasData) {
usernameList.add(DropdownMenuItem<String>(
child: Text('Select Owner'), value: 0.toString()));
_users = snapshot.data;
} else {
usernameList.add(DropdownMenuItem<String>(
child: Text('Loading..'), value: 0.toString()));
}
_users.forEach((user) {
usernameList.add(DropdownMenuItem<String>(
child: Container(
child: Text(
user.name,
),
),
value: user.id.toString()));
});
return Container(
// margin: EdgeInsets.only(left: 40.0),
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DropdownButtonFormField(
icon: Padding(
padding: const EdgeInsets.only(right: 12),
child: Icon(Icons.person, color: Colors.blue),
),
items: usernameList,
value: _ownerId,
decoration: InputDecoration(helperText: 'Optional'),
onChanged: (newValue) {
_ownerId = newValue;
},
),
);
},
);
}
For some reason, the "_columnSelect" AsyncSnapshot is null always, even when the Future method is working fine:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:kanboard/src/models/column_model.dart';
import 'package:kanboard/src/preferences/user_preferences.dart';
class ColumnProvider {
final _prefs = new UserPreferences();
Future<List<ColumnModel>> getColumns(projectId) async {
final Map<String, dynamic> parameters = {
"jsonrpc": "2.0",
"method": "getColumns",
"id": 887036325,
"params": {"project_id": projectId}
};
final credentials = "${_prefs.username}:${_prefs.password}";
Codec<String, String> stringToBase64 = utf8.fuse(base64);
String encoded = stringToBase64.encode(credentials);
final resp = await http.post(
Uri.parse(_prefs.endpoint),
headers: <String, String>{"Authorization": "Basic $encoded"},
body: json.encode(parameters),
);
final decodedData = json.decode(utf8.decode(resp.bodyBytes));
final List<ColumnModel> columns = [];
final List<dynamic> results = decodedData['result'];
if (decodedData == null) return [];
results.forEach((column) {
final columnTemp = ColumnModel.fromJson(column);
columns.add(columnTemp);
});
print(columns);
return columns;
}
}
The output of "print(columns)" returns:
I/flutter ( 9486): [Instance of 'ColumnModel', Instance of 'ColumnModel', Instance of 'ColumnModel', Instance of 'ColumnModel']
I don't know what I'm missing here. The form has 2 users dropdown select (with the original FutureBuilder Widget) which works just fine. The Column widget with the Future Builder is the one with the "null" problem in snapshot.data.
Thank you in advance for your time and support with this!
I just found where the problem was:
In the form page(new Task page), The columnProvider.getColumns(task.projectId)) wasn't executing because the "task.projectId" parameter is a String, but the API needs an int.
I was confused because the method were being called by the previous page (A project Page with all the tasks) and the getColumn's argument was indeed an integer: int.parse(projectId).
The Kanboard API doesn't return an error code if the ID parameter is other than INT with this specific call "getColumns" (for some reason).
Of course, Flutter (or Dart) is waiting for a response from http.post that would never arrive. When comparing the two calls from the two pages, I noticed the difference.
So, in conclusion, I specified the int data type argument in the getColumn definition in order to avoid any confusion:
Future<List<ColumnModel>> getColumns(int projectId) async {
Best Regards!

Only update UI when there are changes in the server, using streambuilder

I am using streambuilder to check whether a new order is placed or not.
I am checking the order status, if the order status is unknown I want to show a pop up, which works fine. but if i don't select an option to update the order status, streambuilder refreshes after a few seconds, and show another pop up on top of it.
Get Orders Function:
Future<Orders> getOrders() async {
String bsid = widget.bsid;
try {
Map<String, dynamic> body = {
"bsid": bsid,
};
http.Response response = await http.post(
Uri.parse(
"**API HERE**"),
body: body);
Map<String, dynamic> mapData = json.decode(response.body);
Orders myOrders;
print(response.body);
if (response.statusCode == 200) {
print("Success");
myOrders = Orders.fromJson(mapData);
}
return myOrders;
} catch (e) {}
}
Here's the stream function:
Stream<Orders> getOrdersStrem(Duration refreshTime) async* {
while (true) {
await Future.delayed(refreshTime);
yield await getOrders();
}}
StreamBuilder:
StreamBuilder<Orders>(
stream: getOrdersStrem(
Duration(
seconds: 2,
),
),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
CircularProgressIndicator.adaptive(),
);
}
var orders = snapshot.data.statedatas;
return ListView.builder(
itemCount: orders.length,
itemBuilder: (BuildContext context, int index) {
var orderResponse =
snapshot.data.statedatas[index].strAccept;
print(orderResponse);
if (orderResponse == "0") {
print("order status unknown");
Future.delayed(Duration.zero, () {
_playFile();
showCupertinoDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Center(
child: Text(
"#${orders[index].ordrAutoid}",
),
),
content: Row(
children: [
SizedBox(
width: 120,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty
.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(
MaterialState.pressed))
return Colors.black;
return Colors
.green; // Use the component's default.
},
),
),
onPressed: () async {
_stopFile();
Navigator.pop(context);
await changeOrderStatus(
orders[index].orid, "accept");
// setState(() {});
},
child: Text('Accept'),
),
),
SizedBox(
width: 15,
),
SizedBox(
width: 120,
child: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty
.resolveWith<Color>(
(Set<MaterialState> states) {
if (states.contains(
MaterialState.pressed))
return Colors.black;
return Colors
.red; // Use the component's default.
},
),
),
onPressed: () async {
_stopFile();
Navigator.pop(context);
await changeOrderStatus(
orders[index].orid, "Reject");
// setState(() {});
},
child: Text('Reject'),
),
),
// TextButton(
// onPressed: () async {
// _stopFile();
// Navigator.pop(context);
// await changeOrderStatus(
// orders[index].orid, "reject");
// },
// child: Text('reject'),
// ),
],
),
),
);
}).then((value) {
_stopFile();
print("ENDING");
});
}
return Container();
Create a variable to check for the last known order status, outside your if statement, and when a new value comes, compare it to the old value first, then do the if statement logic.
//This is outside the stream builder:
String orderResponseCheck = "";
.
.
.
//This is inside your streambuidler, if the orderResponseCheck is still equal to "", the if statement will be executed,
//and the value of orderResponse wil be assigned to it. This will only show the alert dialog if the orderResponse status changes from the one that previously triggered it.
var orderResponse =snapshot.data.statedatas[index].strAccept;
print(orderResponse);
if (orderResponseCheck != orderResponse && orderResponse == "0") {
orderResponseCheck = orderResponse;
.
.
.
//logic same as before
You shouldn't call showCupertinoDialog (and probably _playFile()) from your build method. Wrapping it with Future.delayed(Duration.zero, () { ... }) was probably a workaround for an error that was given by the framework.
The build method can get executed multiple times. You probably want a way to run _playFile and show the dialog that isn't depending on the UI. I don't think StreamBuilder is the right solution for this.
You could use a StatefulWidget and execute listen on a stream from the initState method. initState will only be called once.
From what I'm reading, you're querying your API every two seconds.
Every time your API answers, you're pushing the new datas to your StreamBuilder, which explains why you're having multiple pop-ups are stacking.
One simple solution to your problem would be to have a boolean set to true when the dialog is displayed to avoid showing it multiple times.
bool isDialogShowing = false;
...
if (orderResponse == "0" && !isDialogShowing) {
isDialogShowing = true;
...
But there are a few mistakes in your code that you should avoid like :
Infinite loops
Querying your API multiple times automatically (it could DDOS your service if plenty of users are using your app at the same time)
Showing your Dialog in a ListView builder

Favourite Button in Flutter got unFavourited on Relaunch app

I have a ListView Item with Favourite icon and I want to add functionality so that I can add list into favourite list. data is successfully added to favourite list.
Here is HomePage
body: ListView.builder(
itemCount: 100,
cacheExtent: 20.0,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => ItemTile(index),
),
and My ListTile class I used
var favoritesList = Provider.of<Favorites>(context);
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[index % Colors.primaries.length],
),
title: Text(
'Item $index',
key: Key('text_$index'),
),
trailing: IconButton(
key: Key('icon_$index'),
icon: favoritesList.items.contains(index)
? Icon(Icons.favorite, color: Colors.redAccent)
: Icon(Icons.favorite_border),
onPressed: () {
!favoritesList.items.contains(index)
? favoritesList.add(index)
: favoritesList.remove(index);
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(favoritesList.items.contains(index)
? 'Added to favorites.'
: 'Removed from favorites.'),
duration: Duration(seconds: 1),
),
);
},
),
),
I have a model class favourites.dart
class Favorites extends ChangeNotifier {
final List<int> _favoriteItems = [];
List<int> get items => _favoriteItems;
void add(int itemNo) {
_favoriteItems.add(itemNo);
notifyListeners();
}
void remove(int itemNo) {
_favoriteItems.remove(itemNo);
notifyListeners();
}
}
and in my favouritePage. I am getting everything perfect and also can remove favourited item but when I reopen my app I did not get any favourited item.
here is my page FavouritePage.
body: Consumer<Favorites>(
builder: (context, value, child) => ListView.builder(
itemCount: value.items.length,
padding: const EdgeInsets.symmetric(vertical: 16),
itemBuilder: (context, index) => FavoriteItemTile(value.items[index]),
),
),
FavouriteItemTile
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.primaries[itemNo % Colors.primaries.length],
),
title: Text(
'Item $itemNo',
key: Key('favorites_text_$itemNo'),
),
trailing: IconButton(
key: Key('remove_icon_$itemNo'),
icon: Icon(Icons.close),
onPressed: () {
Provider.of<Favorites>(context, listen: false).remove(itemNo);
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Removed from favorites.'),
duration: Duration(seconds: 1),
),
);
},
),
),
please provide the solution and can I use shared preferences with provider.
Yes. You should be using SharedPreferences. Add the preference library and these pieces of code
Object.dart
class Object1{
bool isLiked;
String name;
const Object1(this.name,this.isLiked);//Whatever fields you need
factory User.fromJson(Map<String, dynamic> parsedJson) {
return new Object1(
name: parsedJson['name'] ?? "",
isLiked: parsedJson['isLiked'] ?? "");
}
Map<String, dynamic> toJson() {
return {
"name": this.name,
"isLiked": this.isLiked
};
}
}
Main.dart
void main(){
setData();
runApp(MyApp);
}
void setData() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
List dataList = [Object1("Name",false).toJson()];//Your items in this format
if prefs.getStringList("lists") == null:
Map decode_options = jsonDecode(dataList);
prefs.setStringList(jsonEncode(Object1.fromJson(decode_options)));
}
Now instead of a custom class for favourites, we will get all the data where we can filter. To retrieve the data afterwards, use this code
SharedPreferences prefs = await SharedPreferences.getInstance();
Map objectMap = jsonDecode(await shared_User.getStringList('list'));
List itemList = [];
for (item in objectMap):
itemList.append(User.fromJson(item));
Now you can use this Item list with the properties and the isLiked feature which is a boolean to check whether it is showed or not.
This may seem complicated but is perfectly simple though your work would be much easier if you used a database like firebase and stored these as documents
One option can be that you can store according to index value in shared preference and query that index value in order to see whether it is added as favourite or not. However it won't be efficient as the number of favourites increases, though still an option.
If you want to store on device us File(pathToFile).write(dataAsString)
You might want to save the data as json using jsonEncode(listOfNumbers) and decode using jsonDecode()
Explanation:
To save data, convert it to json and save to File on device
// NOTE: dataAsMap should be instance of class Map which stores the data you want to save
Directory localDirectory = await getApplicationDocumentsDirectory();
File(localDirectory.path + “/“ + “fileName.json”).writeAsStringSync(jsonEncode(dataAsMap));
To get data:
Map jsonData = jsonDecode(File(localDirectory.path + “/“ + “fileName.json”).readAsStringSync())