firebase - 如何映射用户特定的喜欢/收藏夹?类似于大多数社交应用上的点赞功能
问题描述
您好,我是一名自学 Flutter 开发人员,正在编写我的第一个应用程序。我正在 Flutter 中构建一个基本的报价应用程序,它利用 Firebase 进行身份验证,并将 FireStore 作为数据库。这是 FireStore noSQL 架构:
有一个 Stories 集合,它是提供图像链接和文本的只读数据,如下所示。请注意,文档 ID 是在 Stories 集合中自动生成的:
然后是一个 Users 集合,用于存储用户数据,例如用户名、电子邮件、帐户创建日期时间和一个喜欢数组 [可能存储喜欢的故事]。在此集合中,文档的 ID 是经过身份验证(登录)的用户的 UID(唯一 ID)。
所以这是我正在追逐的用户故事和我的方法:
每当用户点击下面最喜欢的图标时,必须将喜欢的内容保存到用户集合中,其中用户的 uid 是文档 ID,并将喜欢的故事存储在 likes 数组中。
然后使用 if 语句说,如果经过身份验证的用户在 likes 数组中有故事,则将勾勒出的白色心脏变成红色心脏,如下所示:
但是,我的代码中有一个错误,当用户点击最喜欢的图标时,它会立即将所有故事的心变成红色。有人可以帮忙吗?这是代码片段:
class FirestoreSlideshowState extends State<FirestoreSlideshow> {
static const likedKey = 'liked_key';
bool liked;
final PageController ctrl = PageController(viewportFraction: 0.8);
final Firestore db = Firestore.instance;
Stream slides;
String activeTag = 'favs';
// Keep track of current page to avoid unnecessary renders
int currentPage = 0;
@override
void initState() {
super.initState();
_restorePersistedPreference();
_queryDb();
// Set state when page changes
ctrl.addListener(() {
int next = ctrl.page.round();
if (currentPage != next) {
setState(() {
currentPage = next;
});
}
});
}
void _restorePersistedPreference() async {
var preferences = await SharedPreferences.getInstance();
var liked = preferences.getBool(likedKey) ?? false;
setState(() {
this.liked = liked;
});
}
void _persistPreference() async {
setState(() {
liked = !liked;
});
var preferences = await SharedPreferences.getInstance();
preferences.setBool(likedKey, liked);
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: slides,
initialData: [],
builder: (context, AsyncSnapshot snap) {
List slideList = snap.data.toList();
return PageView.builder(
controller: ctrl,
itemCount: slideList.length,
itemBuilder: (context, int currentIdx) {
if (slideList.length >= currentIdx) {
// Active page
bool active = currentIdx == currentPage;
return _buildStoryPage(slideList[currentIdx], active);
}
});
});
}
Stream _queryDb({String tag = 'favs'}) {
// Make a Query
Query query = db.collection('Stories').where('tags', arrayContains: tag);
// Map the documents to the data payload
slides =
query.snapshots().map((list) => list.documents.map((doc) => doc.data));
// Update the active tag
setState(() {
activeTag = tag;
});
}
_buildStoryPage(Map data, bool active) {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
// Animated Properties
final double blur = active ? 20 : 0;
final double offset = active ? 20 : 0;
final double top = active ? 75 : 150;
return AnimatedContainer(
duration: Duration(milliseconds: 500),
curve: Curves.easeOutQuint,
width: _width / 2,
height: _height,
margin: EdgeInsets.only(top: top, bottom: 20, right: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(data['img']),
),
boxShadow: [
BoxShadow(
color: Colors.black87,
blurRadius: blur,
offset: Offset(offset, offset))
]),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Text(data['quote'],
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontFamily: "Typewriter")),
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(data['author'],
style: TextStyle(
fontSize: 20,
color: Colors.white,
fontFamily: "Typewriter")),
),
SizedBox(height: 20),
Row(mainAxisAlignment: MainAxisAlignment.end, children: [
IconButton(
icon: Icon(liked ? Icons.favorite : Icons.favorite_border,
color: liked ? Colors.red : Colors.grey[50]),
onPressed: (){
_persistPreference();
} ),
IconButton(
icon: Icon(
Icons.share,
color: Colors.white,
),
onPressed: () {
share();
})
])
],
));
}
}
解决方案
我会建议为喜欢制作另一个收藏。如果此应用程序要扩展,由于 Firestore 文档的大小限制为 1 MB,您将限制用户的报价数量。所以这将是一个建议的解决方案:
文档结构/likes/{likeId}
{
'userId': string
'quoteId': string
}
然后,如果您想加载喜欢的报价列表,您可以执行如下查询:
_db.collection(likes).where('userId', isEqualTo: currentUserId).get()
然后,您可以从那里根据上面的结构从包含在 likes 对象中的报价 ID 中加载报价。为了避免无缘无故加载大量文档,您还可以在查询中实现分页。
因此,现在当有人想要报价时,您可以在 likes 集合中创建一个新的like 对象,其中包含报价 ID 和用户 ID,如上所示。
对于删除/取消喜欢,您需要找到一个与 userId 和 quoteId 都匹配的文档才能删除它
var snap = _db.collection(likes).where('userId', isEqualTo: currentUserId).where('quoteId', isEqualTo: quoteId).get();
if(snap.docs != null && snap.docs.isNotEmpty){
//the record exists
snap.docs.first.ref.delete() // this will delete the first record in the database that matches this like. Ideally there will only ever be one (you should check to make sure a document doesn't already exist before creating a new one)
}
在颤振方面,您可能会有一个 ListView 显示这些列表。我建议制作一个组件,该组件将使用未来的构建器从 id 加载报价。
如果您需要更多 Flutter 端代码示例,请告诉我。祝你好运!
推荐阅读
- javascript - 如何在useEffect()中刷新signalR的连接ID?
- bash - 从执行 ssh 两级的 shell 脚本获取输出
- google-cloud-platform - GCP 负载均衡器 URL 映射将所有请求路由到默认后端服务,配置根据文档显示正确
- macos - 如何使用 AppleScript 及其脚本标签替换 Pages 中的单个占位符文本?
- reactjs - 仅在 React 中使用 ContextAPI 加载查找值一次
- oracle - 如何在使用 azure 数据工厂的查询中使用变量?
- javascript - Firebase:尝试删除文档并返回上一个屏幕时出现问题
- python - 是否可以向 webdriver 添加噪音警报?
- php - 为什么 SQL 会将表格中的日期更新为 0000-00-00 00:00:00?
- flutter - NNBD - 为什么静态分析器无法检查最终类字段?