Build a Facebook-style friend connection system where users send, accept, or reject friend requests. Friendships are bidirectional — on acceptance, create friend documents in both users' subcollections using a batch write. Add a People You May Know feature using mutual friend queries and a search page for finding users by name. Display friend lists in a GridView with avatars.
Building Bidirectional Friend Connections in FlutterFlow
Unlike follow models where relationships are one-way, friend connections are bidirectional — both users must agree. This tutorial builds the full friend system: sending requests, accepting or rejecting, displaying friend lists, discovering new people, and handling unfriending. All friendships are stored symmetrically in both users' subcollections.
Prerequisites
- A FlutterFlow project with Firebase authentication enabled
- Firestore database with a users collection containing displayName and photoUrl fields
- Familiarity with FlutterFlow Backend Queries and Action Flows
- Understanding of Firestore subcollections
Step-by-step guide
Create the Firestore schema for friend requests and friendships
Create the Firestore schema for friend requests and friendships
Create a top-level `friend_requests` collection with fields: fromUserId (String), toUserId (String), status (String: pending/accepted/rejected), timestamp (Timestamp). Then, under the existing users collection, add a `friends` subcollection for each user with fields: friendUserId (String), displayName (String), photoUrl (String), connectedAt (Timestamp). Also add a friendCount integer field on the user document for quick display without querying the subcollection. Register these collections in FlutterFlow's Data section.
Expected result: The friend_requests collection and users/{uid}/friends subcollection are created and visible in FlutterFlow's Data panel.
Build the user search and Add Friend functionality
Build the user search and Add Friend functionality
Create a FindFriends page with a TextField at the top for search input. Below it, add a ListView bound to a Backend Query on the users collection. Use a Search filter on the displayName field with prefix matching (Firestore: where displayName >= searchTerm and displayName < searchTerm + 'z'). Each result shows a CircleImage avatar, displayName Text, and a conditional button: if a pending friend_request exists from the current user to this user, show 'Request Sent' (disabled). If they are already friends, show 'Friends' (disabled). Otherwise, show an 'Add Friend' button. On Add Friend tap, create a friend_requests document with fromUserId as the current user, toUserId as the selected user, status 'pending', and the current timestamp.
Expected result: Users can search by name and send friend requests. The button state reflects whether a request is pending or the user is already a friend.
Create the friend request inbox with accept and reject actions
Create the friend request inbox with accept and reject actions
Create a FriendRequests page or tab showing incoming requests. Add a ListView bound to a Backend Query on the friend_requests collection filtered by toUserId == currentUser.uid AND status == 'pending', ordered by timestamp descending. Each item shows the sender's CircleImage avatar, displayName, and a Row with Accept (green) and Reject (red) buttons. On Accept tap, execute a sequence of actions: (1) Update the friend_request status to 'accepted'. (2) Create a document in users/{currentUser.uid}/friends with friendUserId = fromUserId, the sender's name and photo, and connectedAt = now. (3) Create a matching document in users/{fromUserId}/friends with friendUserId = currentUser.uid. (4) Increment friendCount on both user documents using FieldValue.increment(1). On Reject tap, simply update the friend_request status to 'rejected'.
Expected result: Incoming friend requests appear in a list. Accepting creates friend documents in both users' subcollections. Rejecting updates the request status.
Display the friend list in a GridView with unfriend option
Display the friend list in a GridView with unfriend option
On the user's profile page or a dedicated Friends tab, add a GridView bound to a Backend Query on users/{currentUser.uid}/friends ordered by connectedAt descending. Each grid item shows a CircleImage avatar and displayName Text. Wrap each item in a GestureDetector: on tap, navigate to that friend's profile page passing their userId. Add a long-press action that shows a BottomSheet with an Unfriend button. On Unfriend tap, show a confirmation dialog, then delete the friend document from both users' subcollections and decrement friendCount on both user documents. Add a Text above the GridView showing the total friend count from the user document.
Expected result: Friends appear in a grid with avatars and names. Tapping navigates to their profile. Long-pressing offers the unfriend option.
Build the People You May Know feature with mutual friend logic
Build the People You May Know feature with mutual friend logic
On the FindFriends page, add a section above the search results titled 'People You May Know'. To populate this, create a Cloud Function that: (1) reads the current user's friend list, (2) for each friend, reads their friend list, (3) collects users who appear in friends-of-friends but are NOT already the current user's friend and have no pending request, (4) ranks by number of mutual friends, (5) writes the top 10 suggestions to a `friend_suggestions` subcollection on the current user's document. In FlutterFlow, add a horizontal ListView bound to this subcollection. Each item shows the user's avatar, name, a mutual friends count text, and an Add Friend button.
Expected result: A horizontal row of suggested friends appears based on mutual connections, each showing the number of shared friends and an Add Friend button.
Complete working example
1// Firestore Schema2// Collection: friend_requests3// - fromUserId: String4// - toUserId: String5// - status: String (pending | accepted | rejected)6// - timestamp: Timestamp78// Subcollection: users/{uid}/friends9// - friendUserId: String10// - displayName: String (denormalized)11// - photoUrl: String (denormalized)12// - connectedAt: Timestamp1314// User doc additions:15// - friendCount: Integer (denormalized)1617// Action Flow: Send Friend Request18// 1. Check: no existing request between these two users19// 2. Create Document → friend_requests:20// fromUserId: currentUser.uid21// toUserId: selectedUser.uid22// status: 'pending'23// timestamp: now24// 3. Optional: send notification to toUserId2526// Action Flow: Accept Friend Request27// (Use Batch Write for atomicity)28// 1. Update friend_requests/{id}: status → 'accepted'29// 2. Create users/{currentUser.uid}/friends/{docId}:30// friendUserId: fromUserId31// displayName: sender.displayName32// photoUrl: sender.photoUrl33// connectedAt: now34// 3. Create users/{fromUserId}/friends/{docId}:35// friendUserId: currentUser.uid36// displayName: currentUser.displayName37// photoUrl: currentUser.photoUrl38// connectedAt: now39// 4. Update users/{currentUser.uid}:40// friendCount: FieldValue.increment(1)41// 5. Update users/{fromUserId}:42// friendCount: FieldValue.increment(1)4344// Action Flow: Reject Friend Request45// 1. Update friend_requests/{id}: status → 'rejected'4647// Action Flow: Unfriend48// (Use Batch Write for atomicity)49// 1. Delete users/{currentUser.uid}/friends50// where friendUserId == targetUserId51// 2. Delete users/{targetUserId}/friends52// where friendUserId == currentUser.uid53// 3. Update users/{currentUser.uid}:54// friendCount: FieldValue.increment(-1)55// 4. Update users/{targetUserId}:56// friendCount: FieldValue.increment(-1)5758// Page: FindFriends59// Column:60// → Text "People You May Know"61// → ListView.horizontal (friend_suggestions)62// → Container: CircleImage + name + mutual count + Add btn63// → TextField (search by name)64// → ListView (search results)65// → Container: CircleImage + name + conditional button6667// Page: FriendRequests68// ListView (friend_requests where toUserId==me, status==pending)69// → Container: CircleImage + name + Accept btn + Reject btnCommon mistakes when creating a Social Network with Friend Connections in FlutterFlow
Why it's a problem: Creating a friend document in only one user's subcollection instead of both
How to avoid: On accept, use a Firestore batch write to create friend documents in BOTH users/{fromUserId}/friends AND users/{toUserId}/friends atomically.
Why it's a problem: Querying the friends subcollection to display friend count on profiles
How to avoid: Denormalize a friendCount integer on the user document. Increment on accept, decrement on unfriend. Display this single field instead of counting subcollection docs.
Why it's a problem: Not checking for existing friend requests before sending a new one
How to avoid: Before creating a friend_request, query for existing documents where fromUserId and toUserId match (in either direction). If one exists with status pending, show 'Request already sent'.
Best practices
- Use batch writes when creating or deleting friend documents to ensure both users' data stays in sync
- Denormalize friend displayName and photoUrl on the friend subcollection document to avoid extra reads
- Store friendCount on the user document for quick display without subcollection queries
- Check for existing requests in both directions before allowing a new friend request
- Show different button states based on friendship status: Add Friend, Request Sent, Friends, or Accept/Reject
- Use a Cloud Function to generate friend suggestions based on mutual connections
- Add a notification when a friend request is received so users do not miss it
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I'm building a friend connection system in FlutterFlow with Firestore. I need bidirectional friend requests with accept/reject, friend lists stored in both users' subcollections via batch writes, a search page for finding users, and a People You May Know feature based on mutual friends. Show me the Firestore schema and FlutterFlow Action Flow logic.
Create a friend requests page with a ListView of incoming requests showing sender avatar, name, and Accept/Reject buttons. When Accept is tapped, create friend documents in both users' friends subcollections.
Frequently asked questions
What is the difference between a friend model and a follow model?
A friend model is bidirectional — both users must agree (like Facebook). A follow model is unidirectional — user A can follow user B without B's approval (like Twitter). This tutorial implements the friend model.
How do I handle the case where user A sends a request to user B, and user B also sends one to user A?
Before creating a new request, check for an existing pending request in the opposite direction. If one exists, automatically accept both by creating the friendship directly instead of creating a duplicate request.
Can I limit how many friends a user can have?
Yes. Check the friendCount on the user document before allowing new friend requests. If it exceeds your limit, show a message that the user's friend list is full.
How do I show mutual friends on a user's profile?
Query the visited user's friends subcollection and the current user's friends subcollection, then find the intersection. Display matching users as 'X mutual friends' with their avatars.
Should I delete rejected friend requests from Firestore?
Keep them with status 'rejected' for a cooldown period (e.g., 30 days) to prevent the sender from immediately resending. After the cooldown, a scheduled Cloud Function can clean up old rejected requests.
Can RapidDev help build a complete social networking platform?
Yes. RapidDev can implement friend connections, news feeds, messaging, groups, events, notifications, and content moderation into a full-featured social networking app.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation