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

How to Build a Custom User Profile Page in FlutterFlow

Build a user profile page with a CircleImage avatar that opens ImagePicker on tap for uploading to Firebase Storage, a cover photo Container, display name and bio Text fields that swap to TextFields in edit mode via a Page State isEditing toggle, a stats row showing posts, followers, and following counts denormalized on the user document, an activity feed ListView of recent posts, and a follow/unfollow button for viewing other users' profiles.

What you'll learn

  • How to build an avatar and cover photo with tap-to-upload using FlutterFlowUploadButton
  • How to implement edit mode with a Page State toggle that swaps display widgets for input fields
  • How to display follower, following, and post counts with denormalized Firestore fields
  • How to implement follow and unfollow actions with atomic counter updates
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read20-30 minFlutterFlow Free+ (FlutterFlowUploadButton for avatar/cover)March 2026RapidDev Engineering Team
TL;DR

Build a user profile page with a CircleImage avatar that opens ImagePicker on tap for uploading to Firebase Storage, a cover photo Container, display name and bio Text fields that swap to TextFields in edit mode via a Page State isEditing toggle, a stats row showing posts, followers, and following counts denormalized on the user document, an activity feed ListView of recent posts, and a follow/unfollow button for viewing other users' profiles.

User profile with avatar upload, edit mode, stats, and follow system

A user profile page is the public-facing identity of each user in your app. This tutorial builds a complete profile: a cover photo and circular avatar with tap-to-change uploads, display name and bio that switch between display mode and edit mode, a stats row showing posts, followers, and following counts, a tabbed activity feed showing recent posts, and a follow/unfollow system for viewing other users' profiles. This is distinct from a settings page (app preferences) or a signup form (initial account creation).

Prerequisites

  • A FlutterFlow project with Firebase/Firestore connected
  • Firebase Authentication enabled with user accounts
  • Firebase Storage enabled for avatar and cover photo uploads
  • Users collection in Firestore with basic user data fields

Step-by-step guide

1

Set up the user document structure with profile and stats fields

Ensure your Firestore users collection has the following fields: displayName (String), bio (String, default empty), avatarUrl (String — Firebase Storage URL), coverPhotoUrl (String — Firebase Storage URL), location (String), postsCount (Integer, default 0), followerCount (Integer, default 0), followingCount (Integer, default 0), and createdAt (Timestamp). Create a subcollection users/{uid}/following where each document ID is the UID of a followed user (with a timestamp field for when the follow happened). The stats counts are denormalized — updated atomically whenever a follow/unfollow or post action occurs. Fill in test data for at least two users.

Expected result: User documents have all profile fields plus denormalized counter fields for posts, followers, and following.

2

Build the profile header with avatar and cover photo upload

Create a ProfilePage that receives an optional userId parameter (empty means current user). At the top, add a Stack for the cover photo and avatar. Bottom layer: a Container (height: 200, full width) with a DecorationImage bound to the user's coverPhotoUrl field (use a default gradient if empty). Top layer: a Positioned CircleImage (diameter: 100, border: 3px white) centered at the bottom edge of the cover photo (bottom: -50, centerHorizontal). For the current user's own profile, overlay a small camera IconButton on the avatar's bottom-right corner. On Tap of the camera icon, trigger FlutterFlowUploadButton configured for Firebase Storage path users/{uid}/avatar.jpg. On upload complete, update the user document's avatarUrl field. Similarly, add a camera icon on the cover photo that uploads to users/{uid}/cover.jpg and updates coverPhotoUrl.

Expected result: Profile displays the cover photo and circular avatar. Tapping the camera icons opens the image picker and uploads to Storage.

3

Implement edit mode toggle for display name, bio, and location

Below the avatar, add a Column for user info. Create a Page State variable isEditing (Boolean, default false). For each field (displayName, bio, location), use Conditional Visibility to switch between display and edit widgets: when isEditing is false, show a Text widget with the field value; when isEditing is true, show a TextField pre-filled with the current value bound to Page State edit variables (editDisplayName, editBio, editLocation). Add an Edit Profile button (visible when isEditing is false and the profile is the current user's) that sets isEditing to true. Add Save and Cancel buttons (visible when isEditing is true): Save triggers an Update Document action on the user document with the edited values and sets isEditing back to false; Cancel discards changes and sets isEditing to false. For other users' profiles, hide the edit button entirely.

Expected result: Tapping Edit Profile swaps static text for editable TextFields. Save updates Firestore and returns to display mode.

4

Display the stats row with posts, followers, and following counts

Below the user info, add a Row with three equally spaced Column widgets for the stats. Each Column contains: a Text with the count value (headlineSmall, bold) and a Text label below it (bodySmall, secondary color). Column 1: postsCount + 'Posts'. Column 2: followerCount + 'Followers'. Column 3: followingCount + 'Following'. Tap on Followers navigates to a FollowersListPage that queries users whose following subcollection contains this user's UID. Tap on Following navigates to a FollowingListPage that queries the user's following subcollection and resolves each UID to a user document. These counts are denormalized on the user document for instant display — no subcollection count queries needed.

Expected result: A stats row shows posts, followers, and following counts. Tapping followers or following navigates to the respective lists.

5

Build the follow/unfollow button and activity feed tab

For other users' profiles (userId != currentUser.uid), add a Follow/Unfollow button below the stats row. Check if the current user follows this profile by querying users/{currentUser.uid}/following/{profileUserId}. If the document exists, show an Unfollow button (outlined style); if not, show a Follow button (filled primary style). Follow action: create a document at users/{currentUser.uid}/following/{profileUserId} with timestamp, then atomically increment the current user's followingCount and the profile user's followerCount using FieldValue.increment(1). Unfollow reverses this: delete the following document and decrement both counts. Below the button, add a TabBar with 'Posts' and 'About' tabs. Posts tab: ListView of recent posts where authorId == profileUserId ordered by timestamp desc. About tab: joined date, location, and bio in a simple Column layout.

Expected result: Visiting another user's profile shows a Follow/Unfollow button that updates counts atomically. The activity feed shows their recent posts.

Complete working example

User Profile Page Architecture
1Firestore Data Model:
2 users/{uid}
3 displayName: String ("Jane Smith")
4 bio: String ("Fitness enthusiast & designer")
5 avatarUrl: String (Storage URL)
6 coverPhotoUrl: String (Storage URL)
7 location: String ("San Francisco, CA")
8 postsCount: Integer (42) [denormalized]
9 followerCount: Integer (128) [denormalized]
10 followingCount: Integer (95) [denormalized]
11 createdAt: Timestamp
12 users/{uid}/following/{followedUid}
13 timestamp: Timestamp
14
15ProfilePage (receives optional userId param):
16 Stack (cover photo + avatar)
17 Container (h: 200, DecorationImage: coverPhotoUrl)
18 Camera IconButton overlay [if own profile]
19 Positioned (bottom: -50, center)
20 Stack
21 CircleImage (avatarUrl, 100x100, white border)
22 Positioned (bottom-right)
23 Camera IconButton [if own profile]
24 SizedBox (h: 60) space for avatar overflow
25 Column (user info)
26 IF !isEditing: Text(displayName, headlineSmall, bold)
27 IF isEditing: TextField(editDisplayName)
28 IF !isEditing: Text(bio, bodyMedium, secondary)
29 IF isEditing: TextField(editBio, multiline)
30 IF !isEditing: Row: Icon(location_on) + Text(location)
31 IF isEditing: TextField(editLocation)
32 IF own profile:
33 Button: "Edit Profile" [if !isEditing]
34 Row: Button("Save") + Button("Cancel") [if isEditing]
35 Row (stats)
36 Column: Text(postsCount, bold) + Text("Posts")
37 Column: Text(followerCount, bold) + Text("Followers")
38 On Tap FollowersListPage
39 Column: Text(followingCount, bold) + Text("Following")
40 On Tap FollowingListPage
41 Button: Follow/Unfollow [if other user's profile]
42 Follow Create following doc + increment both counts
43 Unfollow Delete following doc + decrement both counts
44 TabBar
45 Posts Tab: ListView (posts where authorId==userId)
46 About Tab: joined date + location + bio

Common mistakes when building a Custom User Profile Page in FlutterFlow

Why it's a problem: Fetching follower and following counts by querying the entire subcollection each time

How to avoid: Denormalize: store followerCount and followingCount as Integer fields directly on the user document. Increment and decrement these atomically using FieldValue.increment(1) and FieldValue.increment(-1) on follow/unfollow actions. The profile page reads one document for all counts.

Why it's a problem: Using Page State for avatar URL after upload without updating Firestore

How to avoid: After FlutterFlowUploadButton completes, immediately update the user document's avatarUrl field in Firestore with the new Storage URL. The profile page always reads from Firestore, ensuring the latest image persists.

Why it's a problem: Not checking whether the profile belongs to the current user before showing edit controls

How to avoid: Add Conditional Visibility on all edit controls: only show when the userId parameter equals currentUser.uid (or when userId is empty, meaning the current user's own profile). This provides a clean read-only experience for visitors.

Best practices

  • Denormalize followerCount and followingCount on the user document — never count subcollection docs on every profile load
  • Use FieldValue.increment and FieldValue.increment(-1) for atomic count updates on follow/unfollow
  • Resize uploaded images (512x512 for avatars, 1200x400 for covers) to keep Storage costs and load times low
  • Use Conditional Visibility to cleanly separate edit mode and display mode without duplicate widgets where possible
  • Always update Firestore after image upload — do not rely on Page State for persistent avatar URLs
  • Add a settings gear icon in the AppBar that navigates to the settings page for account management
  • Test the follow/unfollow flow between two accounts to verify counts increment and decrement correctly

Still stuck?

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

ChatGPT Prompt

Design a Firestore data model for a user profile system with display name, bio, avatar, cover photo, and denormalized counts for posts, followers, and following. Include a following subcollection and write the logic for atomic follow/unfollow operations that increment and decrement the count fields on both user documents.

FlutterFlow Prompt

Create a user profile page with a cover photo at the top, a circular avatar overlapping the bottom of the cover, display name and bio below, a row showing posts count, followers count, and following count, and a Follow button. Add an edit mode where tapping Edit Profile turns the text fields into editable inputs.

Frequently asked questions

How do I handle the avatar overlapping the cover photo?

Use a Stack widget. Place the cover photo Container as the bottom layer. Add the CircleImage as a Positioned widget with bottom: -50 (half the avatar height) to create the overlap effect. Add a SizedBox with height 60 below the Stack to create space for the overflowing avatar in the page layout.

Can I add a profile completion percentage indicator?

Yes. Create a Custom Function that checks which fields are filled (avatar, cover, bio, location) and calculates a percentage. Display it as a LinearPercentIndicator on the profile page with a prompt like 'Complete your profile: add a bio' for missing fields.

How do I prevent users from following themselves?

The Follow button should only appear when the profile userId does not equal currentUser.uid. Add this as a Conditional Visibility check. As a safety net, add a Firestore Security Rule on the following subcollection that rejects writes where the document ID equals the parent user's UID.

How do I show a user's posts on their profile?

Add a ListView in the Posts tab bound to a Backend Query on your posts collection where authorId equals the profile user's UID, ordered by timestamp descending. Reuse the same PostCard component from your main feed for consistency.

What image dimensions should I use for avatars and cover photos?

For avatars, resize to 512x512 pixels — they display at 100x100 or smaller, so higher resolution is wasted storage and bandwidth. For cover photos, resize to 1200x400 pixels to match the wide aspect ratio. Use JPEG compression at 80% quality.

Can RapidDev help build a social profile system with verification and privacy settings?

Yes. A production profile system with identity verification badges, granular privacy settings (who can see activity, follow requests), profile analytics, and content recommendation requires Cloud Functions and custom logic. RapidDev can build the complete social profile platform.

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.