Build a social feed focused on user interactions by creating posts with like, comment, save, and report functionality. Implement double-tap to like with a heart animation overlay using GestureDetector, threaded comments with reply nesting in a BottomSheet, a bookmark system for saving posts to a personal collection, and a report flow with reason selection. Track engagement metrics in Firestore and update them with Cloud Functions for reliable counters.
Building a Social Feed with Rich User Interactions in FlutterFlow
A social feed is only as engaging as its interaction patterns. This tutorial focuses specifically on the interaction layer: double-tap likes with animations, threaded comments with replies, save/bookmark collections, and content reporting. These patterns apply to any feed-based app from social networks to content platforms.
Prerequisites
- A FlutterFlow project with Firestore and authentication configured
- A posts collection with basic post data (content, imageUrl, authorId, timestamp)
- Basic familiarity with subcollections and Action Flows in FlutterFlow
Step-by-step guide
Set up the Firestore data model for interactions
Set up the Firestore data model for interactions
Extend your posts collection with fields: likeCount (Integer), commentCount (Integer), shareCount (Integer), saveCount (Integer). Create subcollections under each post: likes (userId, timestamp), comments (userId, body, timestamp, parentCommentId nullable for threading, upvotes Integer), saves (userId, timestamp), and reports (userId, reason, timestamp). Create a user-level saved_posts subcollection under users/{uid} with fields: postId (String), savedAt (Timestamp) for a personal bookmark collection. Add Firestore indexes on comments for parentCommentId and timestamp ordering.
Expected result: Firestore has interaction subcollections under posts and a saved_posts subcollection under users for tracking all engagement types.
Implement double-tap to like with heart animation
Implement double-tap to like with heart animation
Wrap each post card in a GestureDetector Custom Widget configured for onDoubleTap. On double-tap, check if a like document already exists for the current user in the post's likes subcollection. If not, create the like document and increment likeCount using FieldValue.increment(1). Display a heart animation overlay: use a Stack containing the post content and an AnimatedOpacity heart Icon that appears on double-tap, scales up, then fades out over 800 milliseconds. Also add a heart IconButton below the post that toggles the like on single tap. The icon should be filled (red) if the user has liked the post and outlined if not, determined by querying the likes subcollection.
Expected result: Double-tapping a post shows an animated heart overlay and creates a like. The heart icon reflects the current user's like status.
Build threaded comments with reply nesting
Build threaded comments with reply nesting
Add a comment IconButton below each post. On tap, open a BottomSheet displaying the comments panel. The panel has a ListView at the top querying the post's comments subcollection where parentCommentId is null, ordered by timestamp descending. These are top-level comments. Under each top-level comment, add a nested ListView querying comments where parentCommentId equals the parent comment ID, ordered by timestamp ascending. Indent reply comments by adding 32 pixels of left padding. Each comment shows the user avatar, name, body text, timestamp, and a Reply button. Cap nesting at 2 levels: replies to replies show as flat within the second level with an '@username' prefix. Pin a TextField with a Send button at the bottom of the BottomSheet for composing new comments.
Expected result: A comments panel with top-level comments and indented replies, supporting two levels of threading with reply composition.
Add bookmark and report functionality
Add bookmark and report functionality
For bookmarks, add a bookmark IconButton on each post. On tap, check if a save document exists in the post's saves subcollection for the current user. If not, create it and also create a document in the user's saved_posts subcollection with the postId. Toggle the icon between filled bookmark (saved) and outlined bookmark (not saved). For a Saved Posts page, query the user's saved_posts subcollection and display the corresponding posts. For reporting, add a three-dot menu IconButton that opens a PopupMenuButton with a Report option. Tapping Report opens a BottomSheet with a reason selection: DropDown with options like Spam, Harassment, Inappropriate Content, Misinformation, and Other. Add a TextField for additional details. Submit creates a report document under the post and shows a SnackBar confirmation.
Expected result: Users can bookmark posts to a personal collection, unbookmark them, and report posts with a categorized reason.
Track engagement metrics with Cloud Functions
Track engagement metrics with Cloud Functions
Create Cloud Functions triggered by writes to the interaction subcollections. On new like: increment likeCount on the post document. On like delete: decrement likeCount. Apply the same pattern for commentCount and saveCount. A Cloud Function also calculates an engagement rate for each post: (likeCount + commentCount + shareCount) divided by impression count. Store this as an engagementRate field on the post document. Use this rate for ranking posts in the feed — query posts ordered by a composite of recency and engagementRate. Add a notification trigger: when a post receives a new like or comment, create a notification document for the post author.
Expected result: Interaction counts update reliably through Cloud Functions, engagement rates are calculated, and post authors receive notifications.
Assemble the feed with all interaction elements
Assemble the feed with all interaction elements
Create the FeedPage with a ListView querying posts ordered by timestamp descending with real-time listening enabled. Each post card is a Component containing: the author avatar and name row at the top, the post content and image in the middle, an interaction row at the bottom with like IconButton (with count), comment IconButton (with count), share IconButton, and bookmark IconButton. Wrap the entire post card in the GestureDetector for double-tap like. Add a Stack for the heart animation overlay. Below the interaction row, show a Text widget with 'Liked by {count} people' that taps to show a list of likers. Add pull-to-refresh on the ListView to reload the feed.
Expected result: A complete social feed with animated likes, comment counts, bookmarks, sharing, and real-time updates on all interaction counts.
Complete working example
1FIRESTORE DATA MODEL:2 posts/{postId}3 content: String4 imageUrl: String5 authorId: String6 likeCount: Integer7 commentCount: Integer8 shareCount: Integer9 saveCount: Integer10 engagementRate: Double11 timestamp: Timestamp12 └── likes/{likeId} (userId, timestamp)13 └── comments/{commentId}14 userId: String15 body: String16 parentCommentId: String (nullable)17 upvotes: Integer18 timestamp: Timestamp19 └── saves/{saveId} (userId, timestamp)20 └── reports/{reportId} (userId, reason, details, timestamp)2122 users/{uid}23 └── saved_posts/{saveId} (postId, savedAt)2425POST CARD COMPONENT:26 Stack27 ├── GestureDetector (onDoubleTap → like)28 │ └── Column29 │ ├── Row (avatar + authorName + 3-dot menu)30 │ ├── Image (post image)31 │ ├── Row (interaction buttons)32 │ │ ├── IconButton (heart: filled red / outlined)33 │ │ ├── Text (likeCount)34 │ │ ├── IconButton (comment → open BottomSheet)35 │ │ ├── Text (commentCount)36 │ │ ├── IconButton (share)37 │ │ └── IconButton (bookmark: filled / outlined)38 │ └── Text ("Liked by {count} people")39 └── AnimatedOpacity (heart overlay, fades after 800ms)4041COMMENTS BOTTOMSHEET:42 Column43 ├── ListView (top-level comments: parentCommentId == null)44 │ └── Column45 │ ├── Row (avatar + name + body + Reply button)46 │ └── Padding (left: 32)47 │ └── ListView (replies: parentCommentId == parentId)48 │ └── Row (avatar + name + body)49 └── Row (TextField + Send button, pinned bottom)5051REPORT BOTTOMSHEET:52 Column53 ├── Text ("Report this post")54 ├── DropDown (reason: Spam, Harassment, Inappropriate, etc.)55 ├── TextField (additional details)56 └── Button (Submit Report)Common mistakes when building a Custom Social Feed with User Interactions in FlutterFlow
Why it's a problem: Nesting comment replies beyond 2-3 levels of indentation
How to avoid: Cap reply nesting at 2-3 levels. For deeper conversations, show a 'View thread' link that opens the comment chain in a separate view without further indentation.
Why it's a problem: Incrementing like and comment counts with client-side read-then-write instead of atomic operations
How to avoid: Use FieldValue.increment(1) for incrementing and FieldValue.increment(-1) for decrementing. This performs atomic operations that handle concurrent writes correctly.
Why it's a problem: Not checking for existing likes before creating a duplicate
How to avoid: Before creating a like, query the likes subcollection for the current user. If a document already exists, skip the create. Alternatively, use the userId as the document ID to prevent duplicates.
Best practices
- Use FieldValue.increment for atomic counter updates on like, comment, and save counts
- Use the userId as the document ID in likes and saves subcollections to prevent duplicates
- Cap comment nesting at 2-3 levels for readability on mobile screens
- Show animated feedback (heart overlay) on double-tap for satisfying interaction UX
- Calculate engagement rates in Cloud Functions for reliable feed ranking
- Send notifications to post authors when their content receives interactions
- Add pull-to-refresh on the feed for manual content updates
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a social feed with rich interactions in FlutterFlow. Show me how to implement double-tap to like with animation, threaded comments with reply nesting, bookmark/save functionality, content reporting, and engagement tracking with Cloud Functions.
Create a social media post card component with an image at the top, a row of interaction buttons below (heart, comment, share, bookmark) with counts, and a 'Liked by X people' text at the bottom.
Frequently asked questions
How do I prevent spam comments on posts?
Add rate limiting by checking the timestamp of the user's last comment. If it was less than 10 seconds ago, block the new comment. For content filtering, use a Cloud Function that checks comment text against a profanity word list before creating the document.
Can I add emoji reactions beyond just likes?
Yes. Replace the single likes subcollection with a reactions subcollection that includes a reactionType field (like, love, laugh, angry, sad). Update the post document to store reaction counts as a map instead of a single likeCount.
How do I implement infinite scroll on the feed?
Set a page size on the Backend Query (e.g., 10 posts). Enable pagination on the ListView. FlutterFlow loads the next batch of posts as the user scrolls near the bottom of the current batch.
What happens when a user blocks another user?
Create a blocked_users subcollection under users. When rendering the feed, filter out posts where authorId is in the current user's blocked list. Also filter comments from blocked users in the comments panel.
How do I handle reported content as an admin?
Create an admin moderation page that queries all report documents across posts. Group reports by postId and show the count and reasons. Add buttons to dismiss reports, hide the post, or ban the author.
Can RapidDev help build a full social platform?
Yes. RapidDev can implement complete social platforms with feeds, stories, direct messaging, content moderation, push notifications, media processing, and recommendation algorithms.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation