Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/firebase/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Firebase 如何映射用户特定的喜好/收藏夹?类似于大多数社交应用上的“喜欢”功能_Firebase_Flutter_Dart_Google Cloud Firestore - Fatal编程技术网

Firebase 如何映射用户特定的喜好/收藏夹?类似于大多数社交应用上的“喜欢”功能

Firebase 如何映射用户特定的喜好/收藏夹?类似于大多数社交应用上的“喜欢”功能,firebase,flutter,dart,google-cloud-firestore,Firebase,Flutter,Dart,Google Cloud Firestore,你好,我是一个自学成才的颤振开发人员,编写了我的第一个应用程序。我正在Flitter中构建一个基本的quotes应用程序,它使用Firebase for auth和FireStore作为数据库。以下是FireStore noSQL模式: 有一个故事集,它是只读数据,提供如下所示的图像链接和文本。注意:文档ID在Stories集合中自动生成: 然后是一个用户集合,它存储用户数据,如用户名、电子邮件、帐户创建日期时间和likes数组[可能存储喜欢的故事]。在此集合中,文档的ID是经过身份验证(登录

你好,我是一个自学成才的颤振开发人员,编写了我的第一个应用程序。我正在Flitter中构建一个基本的quotes应用程序,它使用Firebase for auth和FireStore作为数据库。以下是FireStore noSQL模式:

有一个故事集,它是只读数据,提供如下所示的图像链接和文本。注意:文档ID在Stories集合中自动生成:

然后是一个用户集合,它存储用户数据,如用户名、电子邮件、帐户创建日期时间和likes数组[可能存储喜欢的故事]。在此集合中,文档的ID是经过身份验证(登录)的用户的UID(唯一ID)

下面是我所追求的用户故事和我的方法:

每当用户点击下面的收藏夹图标时,like必须保存到users集合中,其中user's uid是文档ID,并将like故事存储在likes数组中

然后利用一条if语句,说明如果经过身份验证的用户在likes数组中有故事,请将勾勒出的白色心脏变成如下所示的红色心脏:

void _persistPreference() async {
    var preferences = await SharedPreferences.getInstance();
    preferences.setBool(likedKey, !liked);
}

然而,我的代码中有一个bug,每当用户点击最喜欢的图标时,它会立刻将所有故事的心变成红色。有人能帮忙吗?下面是一段代码:

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();
                  })
            ])
          ],
        ));
  }
}


类FirestoreSlideshowState扩展状态{
静态常量likedKey='liked_key';
布尔喜欢;
final PageController ctrl=PageController(viewportFraction:0.8);
最终Firestore db=Firestore.instance;
溪流滑坡;
字符串activeTag='favs';
//跟踪当前页面以避免不必要的渲染
int currentPage=0;
@凌驾
void initState(){
super.initState();
_恢复PersistedReference();
_queryDb();
//设置页面更改时的状态
ctrl.addListener((){
int next=ctrl.page.round();
如果(当前页!=下一页){
设置状态(){
当前页面=下一页;
});
}
});
}
void\u restorePersistedReference()异步{
var preferences=await SharedPreferences.getInstance();
var liked=preferences.getBool(likedKey)?false;
设置状态(){
喜欢;喜欢;
});
}
void\u persistPreference()异步{
设置状态(){
喜欢=!喜欢;
});
var preferences=await SharedPreferences.getInstance();
setBool(likedKey,liked);
}
@凌驾
小部件构建(构建上下文){
返回流生成器(
流:幻灯片,
初始数据:[],
生成器:(上下文,异步快照快照){
List slideList=snap.data.toList();
返回PageView.builder(
控制器:ctrl,
itemCount:slideList.length,
itemBuilder:(上下文,int currentIdx){
如果(slideList.length>=currentIdx){
//活动页
bool active=currentIdx==currentPage;
返回_buildStoryPage(slideList[currentIdx],活动);
}
});
});
}
流_queryDb({String tag='favs'}){
//询问
Query Query=db.collection('Stories')。其中('tags',arrayContains:tag);
//将文档映射到数据有效负载
幻灯片=
query.snapshots().map((list)=>list.documents.map((doc)=>doc.data));
//更新活动标记
设置状态(){
activeTag=tag;
});
}
_buildStoryPage(地图数据,布尔激活){
final _width=MediaQuery.of(context).size.width;
final _height=MediaQuery.of(context).size.height;
//动画特性
最终双模糊=激活?20:0;
最终双偏移=激活?20:0;
最终双层顶部=有效?75:150;
返回动画容器(
持续时间:持续时间(毫秒:500),
曲线:Curves.easeOutQuint,
宽度:_宽度/2,
高度:_高度,
边距:仅限边集(顶部:顶部,底部:20,右侧:20),
装饰:盒子装饰(
边界半径:边界半径。圆形(40),
图像:装饰图像(
fit:BoxFit.fill,
图像:网络图像(数据['img']),
),
boxShadow:[
箱形阴影(
颜色:颜色。黑色87,
模糊半径:模糊,
偏移:偏移(偏移,偏移))
]),
子:列(
mainAxisAlignment:mainAxisAlignment.center,
crossAxisAlignment:crossAxisAlignment.end,
儿童:[
填充物(
填充:常数边集全部(8.0),
儿童:中心(
子项:文本(数据['quote'],
样式:TextStyle(
尺寸:20,
颜色:颜色,白色,
fontFamily(“打字机”),
),
),
尺寸箱(高度:20),
填充物(
填充:常数边集全部(8.0),
子:文本(数据['author'],
样式:TextStyle(
尺寸:20,
颜色:颜色,白色,
fontFamily(“打字机”),
),
尺寸箱(高度:20),
行(mainAxisAlignment:mainAxisAlignment.end,子项:[
图标按钮(
图标:图标(喜欢?图标。收藏:图标。收藏\u边框,
颜色:喜欢?颜色。红色:颜色。灰色[50]),
已按下:(){
_持久偏好();
} ),
图标按钮(
图标:图标(
Icons.share,
颜色:颜色,白色,
),
已按下:(){
分享();
})
])
],
));
}
}

在这行代码中:
=
//user is a preloaded variable with the current user data
IconButton(icon: Icon(user.data['likes'].contains(data['uid']) ? Icons.favorite ...))
{
    'userId': string
    'quoteId': string
}
_db.collection(likes).where('userId', isEqualTo: currentUserId).get()
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)
}