Muhammad Luqman
 · FlutterFlow Expert | Flutter | MVP and SaaS Expert | Software Engineer

Mood Tracker Widget

📝 Overview:

I wanted to add this mood-tracking UI to my app, but FlutterFlow doesn't have a built-in widget that can do this. I tried using a slider inside a rotated widget and turned it to 270 degrees, but that didn't work.

So, I built my custom widget and added it to my app.

✅ What This Mood Tracker Does

This UI shows how you're feeling using vertical sliders and emoji faces. Each slider stands for a mood—Happy, Calm, Anxious, Sad, and Angry. You can move the sliders up and down to show how strong your feelings are.

The slider will also change the emoji and color based on how you're feeling. It's simple and fun to use and works well in apps for:

  • Relaxation 🧘

  • Mental health 🧠

  • Mood journals 📔

Your mood will be saved in the App State so the app remembers how you felt, even when you open it later. You can also change or adjust the design to match your app.

🛠️How to Set It Up

Here are the easy steps:

  1. Make 5 App State variables

    • Go to App State

    • Add variables like HappyEmotion, CalmEmotion, AnxiousEmotion, SadEmotion and AngryEmotion

    • Set the type to Double and make them Persistent (so they’re saved)

  2. Create a Custom Widget

    • Name it MoodTrackerScreen

    • Paste the code inside the widget

Here is the code:

// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

import 'index.dart'; // Imports other custom widgets

import 'index.dart'; // Imports other custom widgets

class MoodTrackerScreen extends StatefulWidget {
  const MoodTrackerScreen({Key? key, this.width, this.height})
      : super(key: key);
  final double? width;
  final double? height;

  @override
  State<MoodTrackerScreen> createState() => _MoodTrackerScreenState();
}

class _MoodTrackerScreenState extends State<MoodTrackerScreen> {
  final Map<String, double> moodLevels = {
    'Happy': FFAppState().HappyEmotion,
    'Calm': FFAppState().CalmEmotion,
    'Anxious': FFAppState().AnxiousEmotion,
    'Sad': FFAppState().SadEmotion,
    'Angry': FFAppState().AngryEmotion,
  };

  final Map<String, Color> moodColors = {
    'Happy': Colors.teal,
    'Calm': Colors.amber,
    'Anxious': Colors.purple,
    'Sad': Colors.deepPurple,
    'Angry': Colors.redAccent,
  };

  final Map<String, IconData> moodIcons = {
    'Happy': Icons.sentiment_satisfied,
    'Calm': Icons.sentiment_neutral,
    'Anxious': Icons.sentiment_dissatisfied,
    'Sad': Icons.sentiment_very_dissatisfied,
    'Angry': Icons.sentiment_very_dissatisfied_rounded,
  };

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: moodLevels.keys.map((mood) {
        return Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            VerticalMoodSlider(
              value: moodLevels[mood]!,
              onChanged: (newValue) {
                setState(() {
                  moodLevels[mood] = newValue;
                });
                switch (mood) {
                  case 'Happy':
                    FFAppState().HappyEmotion = newValue;
                    break;
                  case 'Calm':
                    FFAppState().CalmEmotion = newValue;
                    break;
                  case 'Anxious':
                    FFAppState().AnxiousEmotion = newValue;
                    break;
                  case 'Sad':
                    FFAppState().SadEmotion = newValue;
                    break;
                  case 'Angry':
                    FFAppState().AngryEmotion = newValue;
                    break;
                }
              },
              color: moodColors[mood]!,
            ),
            const SizedBox(height: 16),
            CircleAvatar(
              radius: 22,
              backgroundColor: moodColors[mood],
              child: Icon(moodIcons[mood], color: Colors.white),
            ),
            const SizedBox(height: 8),
            Text(
              mood,
              style: const TextStyle(color: Colors.white),
            ),
          ],
        );
      }).toList(),
    );
  }
}

class VerticalMoodSlider extends StatelessWidget {
  final double value;
  final ValueChanged<double> onChanged;
  final Color color;

  const VerticalMoodSlider({
    Key? key,
    required this.value,
    required this.onChanged,
    required this.color,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final double sliderHeight = 150;
    final double thumbSize = 20;
    final double trackWidth = 15;

    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onVerticalDragUpdate: (details) {
        final box = context.findRenderObject() as RenderBox;
        final localOffset = box.globalToLocal(details.globalPosition);
        double newValue = 1.0 - (localOffset.dy / box.size.height);
        newValue = newValue.clamp(0.0, 1.0);
        onChanged(newValue);
      },
      onTapDown: (details) {
        final box = context.findRenderObject() as RenderBox;
        final localOffset = box.globalToLocal(details.globalPosition);
        double newValue = 1.0 - (localOffset.dy / box.size.height);
        newValue = newValue.clamp(0.0, 1.0);
        onChanged(newValue);
      },
      child: Container(
        width: 40,
        height: sliderHeight,
        padding: const EdgeInsets.symmetric(horizontal: 12.5),
        child: Stack(
          children: [
            // Background track (static)
            Container(
              width: trackWidth,
              height: sliderHeight,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                color: Color.fromARGB(112, 157, 151, 151),
              ),
            ),

            // Animated active portion
            AnimatedPositioned(
              duration: const Duration(milliseconds: 200),
              curve: Curves.easeOut,
              bottom: 0,
              height: (sliderHeight - thumbSize / 2) * value + thumbSize / 2,
              width: trackWidth,
              child: Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.vertical(
                    bottom: Radius.circular(10),
                    top: Radius.circular(value == 1.0 ? 10 : 0),
                  ),
                  color: color.withOpacity(0.5),
                ),
              ),
            ),

            // Animated thumb
            AnimatedAlign(
              duration: const Duration(milliseconds: 200),
              curve: Curves.easeOut,
              alignment: Alignment(0, 1.0 - 2 * value),
              child: Container(
                width: trackWidth,
                height: thumbSize,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: color,
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withOpacity(0.2),
                      blurRadius: 4,
                      offset: Offset(0, 2),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

If you found this helpful, please like, share, and comment 💬✨. If you need any help, feel free to DM me anytime! 📩😊

#Slider #MoodTracker #CustomCode #CustomUI

8
2 replies