我正在为预订服务创建一个flutter应用程序,但我在检索评论时遇到问题,当用户添加评论时,它显示对所有用户个人资料的评论,而不是仅对他的评论(我不知道问题是否始于添加评论功能的方式是显示和获取它们或两者兼而有之)。我将放一段视频来解释这个问题https://imgur.com/a/Kd0ZMSo
void viewReview(var ratings, String name, String review, String avatarUrl) {
showGeneralDialog(
context: context,
barrierDismissible: false,
transitionDuration: const Duration(
milliseconds: 400,
), // how long it takes to popup dialog after button click
pageBuilder: (_, __, ___) {
// your widget implementation
return Container(
color: Colors.black.withOpacity(0.4),
child: ListView(
children: [
Container(
margin: const EdgeInsets.only(right: 12, top: 8, bottom: 0),
padding: const EdgeInsets.all(11),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 2,
spreadRadius: 1)
],
borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Row(
children: [
CircleAvatar(
backgroundImage: NetworkImage(avatarUrl),
),
const SizedBox(width: 4),
Text(name,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12.0,
color: Colors.black)),
const SizedBox(width: 45),
_buildRatingStars(ratings)
],
),
Container(
margin: const EdgeInsets.only(top: 8),
width: MediaQuery.of(context).size.width - 140,
child: Text(
review,
textScaleFactor: 1.1,
style: const TextStyle(
fontSize: 12.0, color: Colors.black),
),
),
TextButton(
style: TextButton.styleFrom(
shape: const StadiumBorder(),
backgroundColor: Colors.blue,
),
onPressed: () {
Navigator.pop(context);
},
child: const Text(
'Close',
style: TextStyle(
color: Colors.white,
),
),
),
],
),
),
],
),
);
},
);
}
Widget _buildReviewItem(DocumentSnapshot review) {
var data = review.data() as Map<String, dynamic>?;
String reviewerId = data?['reviewerId'] ?? '';
String reviewerName = data?['reviewerName'] ?? 'Anonymous';
return Container(
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey[300]!),
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
reviewerName,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text(
data?['timestamp']?.toDate().toString() ?? 'No date',
style: TextStyle(color: Colors.grey, fontSize: 12),
),
],
),
),
Row(
children: [
Icon(Icons.star, color: Colors.amber, size: 18),
SizedBox(width: 4),
Text(
'${data?['rating']?.toStringAsFixed(1) ?? 'N/A'}',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
if (FirebaseAuth.instance.currentUser?.uid == reviewerId)
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _deleteReview(review.id),
),
],
),
SizedBox(height: 8),
Text(data?['comment'] ?? 'No comment'),
],
),
),
);
}
void _deleteReview(String reviewId) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Delete Review"),
content: Text("Are you sure you want to delete this review?"),
actions: [
TextButton(
child: Text("Cancel"),
onPressed: () => Navigator.of(context).pop(),
),
TextButton(
child: Text("Delete"),
onPressed: () async {
Navigator.of(context).pop();
try {
DocumentSnapshot reviewDoc = await FirebaseFirestore.instance
.collection("Users")
.doc(widget.serviceProviderId)
.collection("BusinessAccount")
.doc("detail")
.collection("reviews")
.doc(reviewId)
.get();
if (reviewDoc.exists) {
Map<String, dynamic> reviewData = reviewDoc.data() as Map<String, dynamic>;
String currentUserId = FirebaseAuth.instance.currentUser?.uid ?? '';
if (currentUserId == reviewData['reviewerId']) {
await reviewDoc.reference.delete();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Review deleted successfully")),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("You don't have permission to delete this review")),
);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Review not found")),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Failed to delete review: $e")),
);
}
},
),
],
);
},
);
}
Container(
color: Colors.white,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Reviews",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18),
),
TextButton(
onPressed: () {
_addReviewBottomSheet(context);
},
child: Text("Add Review"),
)
],
),
),
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("Users")
.doc(widget.serviceProviderId)
.collection("BusinessAccount")
.doc("detail")
.collection("reviews")
.orderBy("timestamp", descending: true)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
print("Error fetching reviews: ${snapshot.error}");
return Center(child: Text('Error: ${snapshot.error}'));
}
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return Center(child: Text('No reviews yet'));
}
print("Number of reviews: ${snapshot.data!.docs.length}");
return ListView.builder(
padding: EdgeInsets.zero,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return _buildReviewItem(snapshot.data!.docs[index]);
},
);
},
),
),
],
),
),
],
),
),
],
),
),
],
),
);
}
void _addReviewBottomSheet(BuildContext context) {
String reviewerName = FirebaseAuth.instance.currentUser?.displayName ?? 'Anonymous';
String? reviewerAvatarUrl = FirebaseAuth.instance.currentUser?.photoURL;
TextEditingController reviewController = TextEditingController();
int rating = 1;
showModalBottomSheet(
context: context,
builder: (BuildContext bc) {
return StatefulBuilder(
builder: (context, setState) => Container(
height: MediaQuery.of(context).size.height * .60,
child: Padding(
padding: const EdgeInsets.only(left: 15.0, top: 10.0),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
const Text("Add review"),
const Spacer(),
IconButton(
icon: const Icon(Icons.cancel),
color: Colors.orange,
iconSize: 25,
onPressed: () {
Navigator.of(context).pop();
},
)
],
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 15.0),
child: TextField(
controller: reviewController,
decoration: const InputDecoration(
helperText: "Review",
),
),
),
),
],
),
Row(
children: [
const Expanded(flex: 4, child: Text("Stars given: ")),
Expanded(
flex: 6,
child: DropdownButton<int>(
value: rating,
items: List<DropdownMenuItem<int>>.generate(
5,
(index) => DropdownMenuItem<int>(
value: index + 1,
child: _buildRatingStars(index + 1),
),
),
onChanged: (value) {
setState(() => rating = value!);
},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
onPressed: () async {
print('Adding review for user: ${widget.serviceProviderId}');
await FirebaseFirestore.instance
.collection("Users")
.doc(widget.serviceProviderId)
.collection("BusinessAccount")
.doc("detail")
.collection("reviews")
.add({
"reviewerName": reviewerName,
"reviewerId": FirebaseAuth.instance.currentUser?.uid,
"rating": rating,
"comment": reviewController.text,
"timestamp": FieldValue.serverTimestamp(),
"avatarUrl": reviewerAvatarUrl,
});
print('Review added successfully');
Navigator.of(context).pop();
},
child: const Text('Save'),
)
],
),
],
),
),
),
);
},
);
}
我没有看到任何将评论与地点联系起来的逻辑。在提供的视频中,我看到两个不同的地方得到了相同的评论。根据视频,您应该将 place_id 添加到评论中,您可以从所有用户的评论中查询,以仅显示与 place_id 特定地点相关的评论。
以下是我实现此目标的建议步骤:
viewReview
方法的 Statefull 小部件。collection("reviews").where("placeId", isEqualTo: widget.placeId)
通过这种方法,您不会在多个地方获得相同的评论。