Build a mental health app with daily mood check-ins using five emoji faces on a 1-5 scale, optional activity tags via ChoiceChips, and free-text journal entries. Store mood_entries and journal_entries in Firestore with strict per-user security rules. Display mood trends over 7, 30, or 90 days using an fl_chart LineChart Custom Widget. Include a resource library with articles, exercises, and hotline information. Add a prominent Crisis Helpline button that launches tel:988 for immediate emergency access.
Building a Mental Health Tracking and Support App in FlutterFlow
Mental health apps help users track their emotional state, identify patterns, and access support resources. This tutorial creates a complete mood tracking app with daily check-ins, journaling, trend visualization, and a resource library. Privacy is prioritized throughout with strict Firestore security rules ensuring users can only access their own data.
Prerequisites
- A FlutterFlow project with Firestore and Firebase Authentication configured
- The fl_chart package added as a Custom Widget dependency
- Basic familiarity with FlutterFlow Action Flows and Backend Queries
- Understanding of Firestore Security Rules for data privacy
Step-by-step guide
Set up the Firestore data model for mood and journal entries
Set up the Firestore data model for mood and journal entries
Create a mood_entries collection with fields: userId (String), mood (Integer, 1-5 scale), note (String, optional), activities (String Array), timestamp (Timestamp). Create a journal_entries collection with: userId (String), title (String), body (String), mood (Integer, optional), isPrivate (Boolean, default true), timestamp (Timestamp). Create a resources collection with: title (String), type (String: 'article', 'exercise', 'hotline'), content (String), category (String), order (Integer). Add strict Firestore Security Rules: users can only create, read, update, and delete their own entries where resource.data.userId == request.auth.uid.
Expected result: Firestore has mood_entries, journal_entries, and resources collections with per-user security rules.
Build the daily mood check-in screen
Build the daily mood check-in screen
Create a CheckInPage with a Column layout. Add a greeting Text ('How are you feeling today?', headlineMedium). Below, add a Row of five IconButtons representing mood levels: 1 (very sad face), 2 (sad face), 3 (neutral face), 4 (happy face), 5 (very happy face). Use Page State selectedMood to track the selection and highlight the selected emoji with a colored circular Container background. Below the emojis, add ChoiceChips for activities: Exercise, Sleep, Social, Work, Nature, Reading, Meditation. Add a TextField (multiline, 3 lines, hint: 'Add a note about your day...') for optional context. Finally, a Save Button that creates a mood_entries document with all values and navigates to the dashboard.
Expected result: A clean check-in screen lets users select their mood, tag activities, and save a daily entry.
Create the mood trend chart with fl_chart LineChart
Create the mood trend chart with fl_chart LineChart
Create a MoodChart Custom Widget using the fl_chart package. Accept parameters: moodData (List of JSON with mood and date). Render a LineChart with dates on the x-axis and mood (1-5) on the y-axis. Color the line with a gradient from red (1) through yellow (3) to green (5). Add dot indicators at each data point. On the dashboard page, query mood_entries for the current user, ordered by timestamp descending, limited by the selected time range (7, 30, or 90 days via ChoiceChips). Pass the query results to the MoodChart widget. Display the average mood as a Text below the chart.
1// Custom Widget: MoodChart2import 'package:fl_chart/fl_chart.dart';34class MoodChart extends StatelessWidget {5 final List<Map<String, dynamic>> moodData;6 const MoodChart({required this.moodData});78 @override9 Widget build(BuildContext context) {10 final spots = moodData.asMap().entries.map((e) =>11 FlSpot(e.key.toDouble(), e.value['mood'].toDouble())12 ).toList();1314 return LineChart(LineChartData(15 minY: 1, maxY: 5,16 gridData: FlGridData(show: true),17 titlesData: FlTitlesData(18 leftTitles: AxisTitles(sideTitles: SideTitles(19 showTitles: true, reservedSize: 30,20 getTitlesWidget: (v, _) => Text(21 ['', '😢', '😟', '😐', '😊', '😄'][v.toInt()],22 style: TextStyle(fontSize: 14)),23 )),24 ),25 lineBarsData: [LineChartBarData(26 spots: spots, isCurved: true,27 gradient: LinearGradient(28 colors: [Colors.red, Colors.amber, Colors.green]),29 dotData: FlDotData(show: true),30 belowBarData: BarAreaData(31 show: true,32 gradient: LinearGradient(33 colors: [Colors.green.withOpacity(0.1),34 Colors.red.withOpacity(0.1)],35 begin: Alignment.topCenter,36 end: Alignment.bottomCenter)),37 )],38 ));39 }40}Expected result: A colored line chart displays mood trends over the selected time period with emoji labels on the y-axis.
Build the journaling feature with entry list and editor
Build the journaling feature with entry list and editor
Create a JournalPage with a Column: a header Row with Text ('Journal') and an Add IconButton. Below, a ListView with Backend Query on journal_entries filtered by userId == currentUser.uid, ordered by timestamp descending. Each row shows a Container with: title Text, first 100 characters of body as preview Text, mood emoji (if set), and timestamp. Tap a row to navigate to JournalDetailPage. Create a JournalEditorPage with: title TextField, body TextField (multiline, minLines: 10), optional mood selector (same 5 emoji Row), and Save Button. Save creates or updates a journal_entries document. Add a Delete action on the detail page with a confirmation dialog.
Expected result: Users can create, view, edit, and delete private journal entries with optional mood tagging.
Add the resource library and crisis helpline button
Add the resource library and crisis helpline button
Create a ResourcesPage with a Column. At the very top, add a prominent Container with red background, white Text ('Crisis Helpline: 988'), and a phone Icon. On tap, use Launch URL action with 'tel:988' to dial the Suicide and Crisis Lifeline. Below, add ChoiceChips for resource categories: Articles, Exercises, Hotlines, All. Add a ListView with Backend Query on resources filtered by the selected category, ordered by the order field. Each resource row shows: type icon (article, exercise, phone), title Text, and a preview of content. Tap to open a ResourceDetailPage showing the full content.
Expected result: A resource library with categorized content and a prominent crisis helpline button accessible from the main navigation.
Build the dashboard tying everything together
Build the dashboard tying everything together
Create a DashboardPage as the app home. Use a SingleChildScrollView with a Column. Add: a greeting Text with the user name and today date, the daily check-in prompt Container (tap navigates to CheckInPage, with Conditional Visibility hidden if already checked in today), the MoodChart Custom Widget with time range ChoiceChips (7/30/90 days), a Row of stat Containers (average mood this week, current streak of check-ins, total journal entries), and a 'Recent Journal Entries' horizontal ListView showing the last 3 entries. Add bottom navigation tabs: Dashboard, Check-In, Journal, Resources.
Expected result: A comprehensive dashboard showing mood trends, stats, and quick access to all features.
Complete working example
1FIRESTORE DATA MODEL:2 mood_entries/{docId}3 userId: String4 mood: Integer (1-5)5 note: String (optional)6 activities: [String]7 timestamp: Timestamp89 journal_entries/{docId}10 userId: String11 title: String12 body: String13 mood: Integer (optional)14 isPrivate: Boolean (default true)15 timestamp: Timestamp1617 resources/{docId}18 title: String19 type: "article" | "exercise" | "hotline"20 content: String21 category: String22 order: Integer2324FIRESTORE SECURITY RULES:25 match /mood_entries/{doc} {26 allow read, write: if request.auth.uid == resource.data.userId;27 allow create: if request.auth.uid == request.resource.data.userId;28 }29 match /journal_entries/{doc} {30 allow read, write: if request.auth.uid == resource.data.userId;31 allow create: if request.auth.uid == request.resource.data.userId;32 }3334DASHBOARD PAGE:35 SingleChildScrollView > Column36 ├── Text (greeting + date)37 ├── Container (daily check-in prompt, if not done)38 ├── ChoiceChips (7 / 30 / 90 days)39 ├── MoodChart Custom Widget (fl_chart LineChart)40 ├── Row (stat cards: avg mood, streak, journal count)41 └── ListView.horizontal (recent journal entries)4243CHECK-IN PAGE:44 Column45 ├── Text ("How are you feeling today?")46 ├── Row (5 emoji IconButtons, mood 1-5)47 ├── ChoiceChips (activities: Exercise, Sleep, Social...)48 ├── TextField (optional note)49 └── Button ("Save Check-In")5051CRISIS HELPLINE:52 Container (red bg, always visible)53 Row: Icon(phone) + Text("Crisis Helpline: 988")54 On Tap: Launch URL → tel:988Common mistakes when creating a Mental Health Tracking and Support App in FlutterFlow
Why it's a problem: Storing mood and journal data without strict per-user Firestore Security Rules
How to avoid: Add Firestore Security Rules requiring request.auth.uid == resource.data.userId for all read and write operations on mood_entries and journal_entries.
Why it's a problem: Burying the crisis helpline button in a submenu or settings page
How to avoid: Place the crisis helpline button prominently at the top of the Resources page and consider adding it to the main dashboard as well.
Why it's a problem: Querying all mood entries without a date range limit for the chart
How to avoid: Always filter mood_entries by timestamp within the selected range (7, 30, or 90 days) and limit the query accordingly.
Why it's a problem: Using local caching for mood data without encryption
How to avoid: Enable Secure Persisted Fields in FlutterFlow for any locally cached sensitive data. Avoid storing full journal text in App State.
Best practices
- Enforce per-user data isolation with Firestore Security Rules on all sensitive collections
- Make the crisis helpline button prominent and accessible from every main screen
- Limit chart data queries to the visible date range for performance
- Use Secure Persisted Fields for any locally cached mental health data
- Include both structured mood ratings and free-text journaling for comprehensive tracking
- Show activity correlation (which activities correlate with better moods) to provide actionable insights
- Add a daily check-in reminder via push notifications at a user-selected time
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a mental health tracking app in FlutterFlow. Show me the Firestore data model for mood entries and journal entries with strict security rules, a daily check-in screen with emoji mood selection and activity tagging, a mood trend LineChart using fl_chart, a journal editor, a resource library, and a crisis helpline button.
Create a check-in page with a heading question, a row of five large emoji buttons for mood selection, a set of activity tag chips below, a text field for notes, and a save button at the bottom.
Frequently asked questions
Can I add guided meditation or breathing exercises to the app?
Yes. Add a 'guided_exercises' collection with audio URLs and step-by-step instructions. Use a custom audio player component or the built-in FlutterFlow audio player to play guided content. Timer-based breathing exercises can use a circular animation with countdown.
How do I show users which activities correlate with better moods?
Query mood_entries with high mood scores (4-5) and aggregate the activities array. Compare against entries with low scores (1-2). Display the most common positive and negative activity correlations in a simple bar chart or ranked list on the dashboard.
Is the mood data shared with anyone else?
No. Firestore Security Rules ensure each user can only access their own mood_entries and journal_entries. The rules check request.auth.uid against the document userId field for every read and write operation.
Can I add therapist or counselor connectivity?
Yes. Add a 'providers' collection with therapist profiles and a messaging system. Users can share specific mood summaries (aggregated, not raw entries) with their therapist via a dedicated sharing action that creates a read-only copy.
How do I handle the crisis helpline for international users?
Create a crisis_numbers collection with country and phone_number fields. Detect the user's locale or let them set their country in settings. Display the appropriate local crisis number. The International Association for Suicide Prevention maintains a directory of global helplines.
Can RapidDev help build a complete mental health platform?
Yes. RapidDev can implement a full mental health platform including mood tracking, journaling, guided exercises, therapist connectivity, crisis resources, push notification reminders, and HIPAA-compliant data handling.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation