Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Create a Custom Social Media Profile in FlutterFlow

Build a social profile page with a Stack widget for the cover photo and avatar overlap, a Row for follower/following/post count stats, an expandable bio using conditional maxLines, and a TabBar for Posts, Likes, and About tabs. Implement follow/unfollow using Firestore batch writes to update both users' follower and following subcollections atomically. Store follower counts as FieldValue.increment in the user document, not as manually maintained separate fields.

What you'll learn

  • How to build a cover photo and avatar overlap using Stack widget
  • How to display follower, following, and post count stats
  • How to implement follow and unfollow with Firestore batch writes
  • How to add a content TabBar for Posts, Likes, and About sections
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read45-60 minFlutterFlow Standard+ (Custom Actions for batch writes)March 2026RapidDev Engineering Team
TL;DR

Build a social profile page with a Stack widget for the cover photo and avatar overlap, a Row for follower/following/post count stats, an expandable bio using conditional maxLines, and a TabBar for Posts, Likes, and About tabs. Implement follow/unfollow using Firestore batch writes to update both users' follower and following subcollections atomically. Store follower counts as FieldValue.increment in the user document, not as manually maintained separate fields.

Building the Standard Social Profile Pattern

The social media profile page is one of the most recognized UI patterns in mobile apps — Instagram, TikTok, Twitter, and LinkedIn all use variations of the same layout. FlutterFlow's widget set is well-suited for building this pattern: Stack handles the cover-photo-with-overlapping-avatar design, Row distributes the stat counts, TabBar switches between content views, and Firestore handles the follow relationship data. The trickiest part is the follow/unfollow logic, which needs to update both users' documents atomically to prevent count inconsistencies.

Prerequisites

  • A FlutterFlow project with Firebase Auth and Firestore connected
  • A 'users' Firestore collection with profile fields (displayName, bio, photoUrl, coverPhotoUrl)
  • FlutterFlow Standard plan or higher for Custom Actions
  • Basic familiarity with the FlutterFlow widget tree and action builder

Step-by-step guide

1

Build the Cover Photo and Avatar Overlap with Stack

Create the profile header using a Stack widget. Set the Stack's height to 200px. Add a first child: an Image widget spanning the full Stack width — this is the cover photo. Bind it to the user's coverPhotoUrl field; set a gradient color fallback if the field is empty. Add a second child: a Column positioned at the bottom of the Stack using the Stack's alignment property. Inside the Column, add a CircleImage widget for the avatar (80px diameter, white border 3px, shadow). Bind it to the user's photoUrl field. Position the CircleImage so it overlaps the cover photo bottom edge by adding a negative top margin of -40px on the Column's container. Place the Stack inside a Column — below it add the display name, username, and stats row.

Expected result: Profile header shows a full-width cover photo with the user's circular avatar overlapping the bottom edge, visually anchoring the profile identity.

2

Add the Follower, Following, and Post Count Stats Row

Below the Stack header, add a Row widget with MainAxisAlignment set to spaceEvenly. Inside the Row, create three identical stat Column widgets: each Column has a Text widget for the count (bold, 20px font) and a Text widget for the label ('Posts', 'Followers', 'Following', 14px grey). Bind the Followers count to the user document's followerCount field (Integer), Following count to followingCount, and Posts to postCount. Add an InkWell to each stat Column so tapping 'Followers' navigates to a Followers list page and tapping 'Following' navigates to a Following list page — this is expected behavior users will look for.

Expected result: Three stat columns display correct counts and tapping Followers and Following navigates to the appropriate list pages.

3

Implement Follow and Unfollow with Firestore Batch Writes

Create a Custom Action called 'toggleFollow' that takes the target user's ID as a parameter. The action checks if the current user already follows the target, then either follows or unfollows atomically using a Firestore batch write. The batch updates: (1) the current user's followingCount +1/-1 and adds/removes the target user from a 'following' subcollection, (2) the target user's followerCount +1/-1 and adds/removes the current user from a 'followers' subcollection. A batch write ensures both updates succeed or both fail — you never have a state where user A follows user B but B's followerCount wasn't incremented.

toggle_follow_action.dart
1import 'package:cloud_firestore/cloud_firestore.dart';
2
3Future<bool> toggleFollow(
4 String currentUserId,
5 String targetUserId,
6) async {
7 final firestore = FirebaseFirestore.instance;
8 final followingRef = firestore
9 .collection('users')
10 .doc(currentUserId)
11 .collection('following')
12 .doc(targetUserId);
13
14 final followDoc = await followingRef.get();
15 final isCurrentlyFollowing = followDoc.exists;
16 final batch = firestore.batch();
17
18 final currentUserRef = firestore.collection('users').doc(currentUserId);
19 final targetUserRef = firestore.collection('users').doc(targetUserId);
20 final followerRef = firestore
21 .collection('users')
22 .doc(targetUserId)
23 .collection('followers')
24 .doc(currentUserId);
25
26 if (isCurrentlyFollowing) {
27 // Unfollow
28 batch.delete(followingRef);
29 batch.delete(followerRef);
30 batch.update(currentUserRef,
31 {'followingCount': FieldValue.increment(-1)});
32 batch.update(targetUserRef,
33 {'followerCount': FieldValue.increment(-1)});
34 } else {
35 // Follow
36 batch.set(followingRef, {'followedAt': FieldValue.serverTimestamp()});
37 batch.set(followerRef, {'followedAt': FieldValue.serverTimestamp()});
38 batch.update(currentUserRef,
39 {'followingCount': FieldValue.increment(1)});
40 batch.update(targetUserRef,
41 {'followerCount': FieldValue.increment(1)});
42 }
43
44 await batch.commit();
45 // Returns true if now following, false if now unfollowed
46 return !isCurrentlyFollowing;
47}

Expected result: Tapping Follow increments both users' counts atomically. Tapping again (Unfollow) decrements both counts. Button label changes immediately on tap.

4

Add the Follow Button with Dynamic State

Add a Follow/Unfollow button below the bio text. To check follow state, create a Firestore query checking if a document exists at users/{currentUserId}/following/{profileUserId}. Use this as a condition to show either a filled 'Following' button or an outlined 'Follow' button. Use a Page State boolean variable 'isFollowing' initialized by the Firestore existence check. When the toggleFollow action completes, update the page state variable with the returned boolean. This gives instant UI feedback without waiting for Firestore to re-query. Hide the Follow button entirely when viewing your own profile (Conditional: currentUserUID != profileUserId).

Expected result: Viewing another user's profile shows a Follow button. Tapping it changes to Following immediately. Tapping Following shows a confirmation dialog then unfollows.

5

Build a TabBar for Posts, Likes, and About Sections

Below the follow button and bio, add a TabBar widget with three tabs: Posts, Likes, About. For the Posts tab: a GridView with 3 columns showing post thumbnail images, queried from a 'posts' collection filtered by userId == profileUserId, ordered by createdAt descending. For the Likes tab: a similar GridView filtered to posts where userId matches posts the user has liked (store liked post IDs in a likedPosts subcollection). For the About tab: a Column showing additional profile info — location, website link (launches browser), joined date, and interests. Use ListView instead of GridView for About to handle variable content length.

Expected result: Three tabs display correctly. Posts tab shows the user's post grid. Likes tab shows liked posts. About tab shows detailed profile information.

Complete working example

check_is_following.dart
1import 'package:cloud_firestore/cloud_firestore.dart';
2
3/// Custom Action: checkIsFollowing
4/// Returns true if currentUserId follows targetUserId
5Future<bool> checkIsFollowing(
6 String currentUserId,
7 String targetUserId,
8) async {
9 if (currentUserId.isEmpty || targetUserId.isEmpty) return false;
10 if (currentUserId == targetUserId) return false;
11
12 try {
13 final doc = await FirebaseFirestore.instance
14 .collection('users')
15 .doc(currentUserId)
16 .collection('following')
17 .doc(targetUserId)
18 .get();
19 return doc.exists;
20 } catch (e) {
21 debugPrint('checkIsFollowing error: $e');
22 return false;
23 }
24}
25
26/// Custom Function: formatFollowerCount
27/// Returns '12.3K' for 12300, '1.2M' for 1200000
28String formatFollowerCount(int count) {
29 if (count >= 1000000) {
30 final millions = count / 1000000;
31 return '${millions.toStringAsFixed(millions >= 10 ? 0 : 1)}M';
32 } else if (count >= 1000) {
33 final thousands = count / 1000;
34 return '${thousands.toStringAsFixed(thousands >= 10 ? 0 : 1)}K';
35 }
36 return count.toString();
37}
38
39/// Custom Function: truncateBio
40/// Returns bio text, truncated to maxChars with ellipsis
41String truncateBio(String bio, int maxChars) {
42 if (bio.length <= maxChars) return bio;
43 return '${bio.substring(0, maxChars).trimRight()}...';
44}

Common mistakes when creating a Custom Social Media Profile in FlutterFlow

Why it's a problem: Storing follower counts as separate fields incremented manually in two separate Firestore writes

How to avoid: Use a Firestore batch write to update all related documents atomically. A batch either fully succeeds or fully fails, so counts and subcollection membership are always in sync.

Why it's a problem: Loading all three tab contents on page load

How to avoid: Initialize only the Posts tab (the default visible tab) on page load. Use the TabBar's onTap callback to trigger queries for Likes and About the first time each tab is selected.

Why it's a problem: Calculating follower count by querying the followers subcollection on every profile view

How to avoid: Store a pre-computed followerCount integer on the user document. Update it with FieldValue.increment(1) and FieldValue.increment(-1) in your follow/unfollow batch write. Reading the count costs one document read, not N.

Best practices

  • Use Firestore batch writes for any action that updates multiple documents (follow, unfollow, post creation)
  • Pre-compute and cache counts (followerCount, postCount) on the user document rather than counting subcollection documents
  • Store the follow state as a Page State variable for instant UI updates — don't wait for Firestore to re-query after follow/unfollow
  • Hide the Follow button when viewing your own profile using a conditional check against currentUserUID
  • Format large follower counts (12.3K, 1.2M) using a Custom Function — raw numbers look unprofessional
  • Use lazy loading for tab content — only query when a tab is first selected
  • Add a confirmation bottom sheet before unfollowing to prevent accidental unfollows

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I'm building a social media app in FlutterFlow. I need to implement a follow/unfollow system where: (1) a user can follow another user, (2) both users' follower and following counts update correctly, (3) the follow state is reflected immediately in the UI. Design the Firestore data model and explain the batch write logic I need.

FlutterFlow Prompt

In my FlutterFlow project, I want to create a profile page TabBar with Posts and Likes tabs. The Posts tab shows a GridView of the user's posts from Firestore. Write the Firestore query configuration I need and the action to load the Likes tab data only when that tab is first tapped.

Frequently asked questions

How do I make the avatar overlap the cover photo in FlutterFlow?

Use a Stack widget containing: (1) the cover photo Image as the first child, (2) a Positioned widget at the bottom of the Stack containing your CircleImage avatar. Set the Positioned widget's 'bottom' property to -40 (negative value) to make the avatar extend below the Stack boundary, creating the overlap effect. The parent Column needs 'clipBehavior: Clip.none' to allow the overflow.

Should I use a subcollection or an array to store followers?

Use a subcollection (users/{userId}/followers/{followerId}). Arrays of user IDs in Firestore documents hit a size limit at around 30KB (roughly 1,000 user IDs). Subcollections scale to millions of followers. Subcollections also allow querying, pagination, and metadata storage (like the followedAt timestamp).

How do I prevent a user from following themselves?

Add a conditional check in your toggleFollow Custom Action: if (currentUserId == targetUserId) return false early. Also hide the Follow button in your FlutterFlow UI when the profileUserId page parameter matches currentUserUID using a conditional visibility rule.

How do I show posts in a 3-column grid on the profile page?

Use a GridView widget with crossAxisCount set to 3 and a Firestore query filtering posts by userId == profileUserId, ordered by createdAt descending. Set each grid item to display only the thumbnail image with an aspect ratio of 1:1. Tapping an item navigates to the full post detail page.

How do I handle the edit profile functionality?

Add an Edit Profile button visible only when viewing your own profile (conditional: currentUserUID == profileUserId). Tapping it navigates to an Edit Profile page with form fields pre-filled from the current user document. On save, use FlutterFlow's Update Document action to write the changes to the users collection. For photo updates, use FlutterFlow's Upload Photo action connected to Firebase Storage.

Can I show a profile page for users who are not logged in?

Yes, but with limitations. Firestore security rules must allow reading the users collection for unauthenticated users (or anonymous auth users) to display the profile. The Follow button should be hidden for logged-out viewers. Consider showing a 'Sign up to follow' prompt instead to drive conversions.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.