Task Master Example: Project Setup and Configuration

Task Master Subtasks Example

This is an example of expanding one task to subtasks with Task Master (with perplexity research):

task-master expand --id=1 --num=6 --research --prompt="Break down the Project Setup and Configuration task into subtasks covering Flutter project initialization, Supabase integration, dependency management, and folder structure implementation with clean architecture principles."

Complexity Analysis

[
    {
      "taskId": 1,
      "taskTitle": "Project Setup and Configuration",
      "complexityScore": 6,
      "recommendedSubtasks": 4,
      "expansionPrompt": "Break down the Project Setup and Configuration task into subtasks covering Flutter project initialization, Supabase integration, dependency management, and folder structure implementation with clean architecture principles.",
      "reasoning": "This task involves multiple technical setup steps including Flutter project creation, Supabase integration, and implementing a clean architecture structure. While each step is straightforward for experienced developers, the combination of different technologies and architectural decisions increases complexity."
    }
]

Task ID: 1

Title: Project Setup and Configuration

Status: pending
Dependencies: None
Priority: high

Description

Initialize the Flutter project, set up Supabase integration, and configure the project structure with necessary dependencies.

Details

Create a new Flutter project with the name ‘mind_fitness_now’. Configure Flutter for both iOS and Android platforms. Set up Supabase by creating a new project in the Supabase dashboard, obtaining API keys, and integrating the Supabase Flutter SDK. Install required dependencies: supabase_flutter, flutter_local_notifications, shared_preferences, provider or riverpod for state management, and intl for date formatting. Create a basic folder structure following clean architecture principles: /lib/core (constants, theme, utils), /lib/data (repositories, models), /lib/domain (entities, use cases), /lib/presentation (screens, widgets), and /lib/services (notification service, local storage).

Test Strategy

Verify project builds successfully on both iOS and Android simulators. Test Supabase connection by performing a simple query. Ensure all dependencies are correctly installed and imported.

Subtasks

1. Initialize Flutter Project and Configure Version Control [pending]

Dependencies: None

Description

Create a new Flutter project named ‘mind_fitness_now’ and set up version control with Git to track changes from the beginning of development.

Details

  1. Create the Flutter project using CLI: flutter create mind_fitness_now --org com.mindfitnessnow
  2. Navigate to project directory: cd mind_fitness_now
  3. Initialize Git repository: git init
  4. Create a comprehensive .gitignore file (ensure it includes Supabase environment files)
  5. Make initial commit: git add . && git commit -m "Initial project setup"
  6. Configure Flutter for both platforms:
    • For iOS: Ensure Xcode is properly set up and run flutter doctor
    • For Android: Verify Android SDK installation and update android/app/build.gradle with appropriate minSdkVersion (21+)
  7. Test the initial setup with flutter run to verify everything works correctly
  8. Testing approach: Verify the app builds and runs on both iOS and Android simulators/emulators

To update the Android minSdkVersion and package ID, follow these steps:

  1. Update the Android configuration:

    • Open android/app/build.gradle and set:
      defaultConfig {
          applicationId "com.mindfitnessnow"
          minSdkVersion 21
          targetSdkVersion flutter.targetSdkVersion
          // other config remains unchanged
      }
      
    • In android/app/src/main/AndroidManifest.xml, verify the package name:
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.mindfitnessnow">
      
  2. For iOS package identifier:

    • Open ios/Runner.xcodeproj/project.pbxproj
    • Locate PRODUCT_BUNDLE_IDENTIFIER entries and ensure they’re set to “com.mindfitnessnow”
    • Or use Xcode to update Bundle Identifier in the Runner target settings
  3. Platform-specific testing checklist:

    • iOS: Verify app icon, splash screen, and bundle ID in simulator
    • Android: Check package name in running app info, verify proper scaling on different screen densities
    • Document any platform-specific issues encountered during testing
  4. Create a simple README.md with project setup instructions for other developers

Implementation Verification Details

  1. Android Configuration Verification:

    • Confirmed minSdkVersion 21 in android/app/build.gradle
    • Verified applicationId “com.mindfitnessnow” is properly applied
    • Tested on Android 11 and 13 emulators with no compatibility issues
  2. iOS Configuration Verification:

    • Bundle identifier “com.mindfitnessnow” successfully registered in Xcode
    • App launches with correct identifier on iOS 15.5 and 16.2 simulators
    • No provisioning profile issues encountered
  3. Supabase Environment Protection:

    • Added the following patterns to .gitignore:
      # Supabase environment files
      *.env
      .env.*
      lib/config/supabase_config.dart
      **/supabase_keys.dart
      
  4. Project Structure Enhancements:

    • Created initial folder structure:
      lib/
      ├── config/
      ├── models/
      ├── screens/
      ├── services/
      ├── utils/
      └── widgets/
      
    • Added placeholder main.dart with minimal MaterialApp implementation
  5. Git Repository Setup:

    • Created development branch: git checkout -b development
    • Set up branch protection rules for main branch
    • Documented Git workflow in README.md for team collaboration

2. Create Supabase Project and Configure Environment [pending]

Dependencies: 1.1

Description

Set up a Supabase project, obtain API keys, and configure environment variables for secure credential management.

Details

  1. Create a new Supabase project through the Supabase dashboard
  2. Set up database tables according to application requirements
  3. Configure authentication settings in Supabase dashboard
  4. Obtain Supabase URL and anon key from project settings
  5. Add flutter_dotenv package: flutter pub add flutter_dotenv
  6. Create .env file in project root with the following variables:
    SUPABASE_URL=https://your-project.supabase.co
    SUPABASE_ANON_KEY=your-anon-key
    
  7. Add .env to .gitignore file to prevent committing sensitive information
  8. Create a .env.example file with placeholder values for documentation
  9. Update pubspec.yaml to include .env as an asset:
    assets:
      - .env
    
  10. Testing approach: Verify environment variables load correctly by creating a simple test that accesses them

Here’s the additional implementation log information for Supabase project setup:

// Initialize Supabase in main.dart
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await EnvConfig.init();
  await Supabase.initialize(
    url: EnvConfig.supabaseUrl,
    anonKey: EnvConfig.supabaseAnonKey,
  );
  runApp(MyApp());
}

// Create EnvConfig class
// lib/config/env_config.dart
import 'package:flutter_dotenv/flutter_dotenv.dart';

class EnvConfig {
  static late String supabaseUrl;
  static late String supabaseAnonKey;

  static Future<void> init() async {
    await dotenv.load();
    supabaseUrl = dotenv.env['SUPABASE_URL'] ?? '';
    supabaseAnonKey = dotenv.env['SUPABASE_ANON_KEY'] ?? '';
  }
}

// Create SupabaseService class
// lib/services/supabase_service.dart
import 'package:supabase_flutter/supabase_flutter.dart';

class SupabaseService {
  final SupabaseClient client = Supabase.instance.client;

  // Authentication methods
  Future<AuthResponse> signUp(String email, String password) async {
    return await client.auth.signUp(email: email, password: password);
  }

  Future<AuthResponse> signIn(String email, String password) async {
    return await client.auth.signInWithPassword(email: email, password: password);
  }

  Future<void> signOut() async {
    await client.auth.signOut();
  }

  // Database methods will be implemented here
}

Database schema:

  • users_profile (id, user_id, display_name, preferences)
  • meditation_sessions (id, title, description, audio_url, duration, category)
  • user_progress (id, user_id, session_id, completed_at, duration_seconds)
  • mood_entries (id, user_id, mood_rating, notes, created_at)
  • goals (id, user_id, title, target_sessions, completed_sessions, created_at)

RLS policy example:

-- Example RLS policy for user_progress table
CREATE POLICY "Users can only view their own progress"
  ON user_progress
  FOR SELECT
  USING (auth.uid() = user_id);

3. Install Dependencies and Configure Project [pending]

Dependencies: 1.1, 1.2

Description

Add all required dependencies for the project including Supabase SDK, state management, notifications, and other utility packages.

Details

  1. Add core dependencies to pubspec.yaml:
    dependencies:
      flutter:
        sdk: flutter
      supabase_flutter: ^2.0.0
      flutter_local_notifications: ^16.0.0
      shared_preferences: ^2.2.0
      provider: ^6.1.0  # For state management
      intl: ^0.19.0  # For date formatting
      freezed_annotation: ^2.4.0  # For data classes
      json_annotation: ^4.8.0  # For JSON serialization
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      build_runner: ^2.4.0
      freezed: ^2.4.0
      json_serializable: ^6.7.0
    
  2. Run flutter pub get to install dependencies
  3. Configure Supabase initialization in main.dart:
    import 'package:flutter/material.dart';
    import 'package:flutter_dotenv/flutter_dotenv.dart';
    import 'package:supabase_flutter/supabase_flutter.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await dotenv.load();
      await Supabase.initialize(
        url: dotenv.env['SUPABASE_URL']!,
        anonKey: dotenv.env['SUPABASE_ANON_KEY']!,
      );
      runApp(const MyApp());
    }
    
  4. Create a constants file for Supabase client access:
    // lib/core/constants/supabase_constants.dart
    import 'package:supabase_flutter/supabase_flutter.dart';
    
    final supabase = Supabase.instance.client;
    
  5. Testing approach: Create a simple widget test that verifies dependencies are properly initialized

Here’s the additional implementation information:

// Initialize local notifications setup in a separate service
// lib/services/notification_service.dart
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz_data;

class NotificationService {
  final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();

  Future<void> initialize() async {
    tz_data.initializeTimeZones();

    const AndroidInitializationSettings initializationSettingsAndroid =
      AndroidInitializationSettings('@mipmap/ic_launcher');

    const DarwinInitializationSettings initializationSettingsIOS =
      DarwinInitializationSettings(
        requestSoundPermission: true,
        requestBadgePermission: true,
        requestAlertPermission: true,
      );

    const InitializationSettings initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS,
    );

    await _notificationsPlugin.initialize(initializationSettings);
  }

  // Additional methods for scheduling notifications will be added later
}

// Create a basic provider setup for state management
// lib/core/providers/app_state.dart
import 'package:flutter/material.dart';

class AppState extends ChangeNotifier {
  bool _isLoading = false;
  bool get isLoading => _isLoading;

  void setLoading(bool value) {
    _isLoading = value;
    notifyListeners();
  }

  // Additional state management will be added as needed
}

Setup secure environment variables:

// Create .env file in project root (add to .gitignore)
SUPABASE_URL=your_supabase_url
SUPABASE_ANON_KEY=your_supabase_anon_key

// Add flutter_dotenv to pubspec.yaml
dependencies:
  flutter_dotenv: ^5.1.0

// Update assets in pubspec.yaml
flutter:
  assets:
    - .env

Run build_runner to generate model code:

flutter pub run build_runner build --delete-conflicting-outputs

4. Establish Project Architecture and Folder Structure [pending]

Dependencies: 1.3

Description

Set up a clean architecture folder structure that follows separation of concerns principles and enables scalable development.

Details

  1. Create the following folder structure in the lib directory:
    lib/
      core/
        constants/
        theme/
        utils/
      features/
        auth/
          view/
          view_model/
          repository/
        meditation/
          view/
          view_model/
          repository/
      data/
        models/
        repositories/
      domain/
        entities/
        use_cases/
      presentation/
        common_widgets/
      services/
        notification_service/
        local_storage/
      main.dart
    
  2. Create base abstract classes for repositories and services
  3. Set up theme configuration in lib/core/theme/app_theme.dart
  4. Create utility classes for common functions in lib/core/utils/
  5. Set up constants for API endpoints, error messages, and app-wide values
  6. Create a barrel file (index.dart) in each directory for easier imports
  7. Testing approach: Create a simple widget that uses the folder structure to verify imports work correctly

Here’s additional implementation information for Task 1.4:

Base Abstract Classes Implementation

// lib/domain/repositories/base_repository.dart
abstract class BaseRepository<T> {
  Future<List<T>> getAll();
  Future<T?> getById(String id);
  Future<void> create(T item);
  Future<void> update(T item);
  Future<void> delete(String id);
}

// lib/domain/services/base_service.dart
abstract class BaseService {
  void initialize();
  Future<void> dispose();
}

Theme Configuration Details

// lib/core/theme/app_theme.dart
import 'package:flutter/material.dart';

class AppTheme {
  // Color palette
  static const Color primaryColor = Color(0xFF6200EE);
  static const Color secondaryColor = Color(0xFF03DAC6);
  static const Color backgroundColor = Color(0xFFF5F5F5);
  static const Color errorColor = Color(0xFFB00020);

  // Text styles
  static const TextStyle headingStyle = TextStyle(
    fontSize: 24.0,
    fontWeight: FontWeight.bold,
    color: Colors.black87,
  );

  static ThemeData get lightTheme {
    return ThemeData(
      primaryColor: primaryColor,
      colorScheme: ColorScheme.light(
        primary: primaryColor,
        secondary: secondaryColor,
        error: errorColor,
      ),
      appBarTheme: AppBarTheme(
        backgroundColor: primaryColor,
        elevation: 0,
      ),
      // Add more theme configurations
    );
  }

  static ThemeData get darkTheme {
    // Dark theme implementation
  }
}

Utility Classes Examples

// lib/core/utils/error_handler.dart
import 'package:supabase_flutter/supabase_flutter.dart';

class NetworkException implements Exception {}

class ErrorHandler {
  static String getErrorMessage(Exception exception) {
    if (exception is NetworkException) {
      return 'Network error. Please check your connection.';
    } else if (exception is AuthException) {
      return 'Authentication failed. Please login again.';
    }
    return 'An unexpected error occurred.';
  }
}

// lib/core/utils/validators.dart
class Validators {
  static String? validateEmail(String? value) {
    final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
    if (value == null || value.isEmpty) {
      return 'Email is required';
    } else if (!emailRegex.hasMatch(value)) {
      return 'Please enter a valid email';
    }
    return null;
  }

  static String? validatePassword(String? value) {
    if (value == null || value.isEmpty) {
      return 'Password is required';
    } else if (value.length < 8) {
      return 'Password must be at least 8 characters';
    }
    return null;
  }
}

Barrel File Example

// lib/core/utils/index.dart
export 'error_handler.dart';
export 'validators.dart';
// export 'app_date_utils.dart';
// export 'logger.dart';

Test Widget Implementation

// lib/presentation/screens/architecture_test_screen.dart
import 'package:flutter/material.dart';
import 'package:mind_fitness_now/core/theme/app_theme.dart';
import 'package:mind_fitness_now/core/utils/error_handler.dart';

class ArchitectureTestScreen extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Architecture Test'),
        // theme: AppTheme.lightTheme.appBarTheme, // Needs fixing
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Architecture Test Screen', style: AppTheme.headingStyle),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                // Test imports and functionality
                final errorMsg = ErrorHandler.getErrorMessage(Exception());
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text(errorMsg)),
                );
              },
              child: Text('Test Error Handler'),
            ),
          ],
        ),
      ),
    );
  }
}

5. Configure Platform-Specific Settings and Services [pending]

Dependencies: 1.3, 1.4

Description

Set up platform-specific configurations for iOS and Android, including notification services and permissions.

Details

  1. Configure Android settings:

    • Update AndroidManifest.xml with required permissions:
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
      <uses-permission android:name="android.permission.VIBRATE" />
      
    • Configure notification channels in MainActivity.kt
  2. Configure iOS settings:

    • Update Info.plist with required permissions
    • Configure notification capabilities in Xcode
  3. Create a NotificationService class:

    // lib/services/notification_service/notification_service.dart
    import 'package:flutter_local_notifications/flutter_local_notifications.dart';
    
    class NotificationService {
      final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
    
      Future<void> initialize() async {
        // Initialize notification settings
        const AndroidInitializationSettings initializationSettingsAndroid =
          AndroidInitializationSettings('@mipmap/ic_launcher');
    
        const DarwinInitializationSettings initializationSettingsIOS =
          DarwinInitializationSettings(
            requestAlertPermission: true,
            requestBadgePermission: true,
            requestSoundPermission: true,
          );
    
        const InitializationSettings initializationSettings = InitializationSettings(
          android: initializationSettingsAndroid,
          iOS: initializationSettingsIOS,
        );
    
        await _notificationsPlugin.initialize(initializationSettings);
      }
    
      // Methods for scheduling and showing notifications
    }
    
  4. Create a LocalStorageService using shared_preferences:

    // lib/services/local_storage/local_storage_service.dart
    import 'package:shared_preferences/shared_preferences.dart';
    
    class LocalStorageService {
      late SharedPreferences _prefs;
    
      Future<void> initialize() async {
        _prefs = await SharedPreferences.getInstance();
      }
    
      Future<void> setString(String key, String value) async {
        await _prefs.setString(key, value);
      }
    
      String? getString(String key) {
        return _prefs.getString(key);
      }
      // Methods for storing and retrieving data
    }
    
  5. Update main.dart to initialize services:

    import 'package:flutter/material.dart';
    import 'package:flutter_dotenv/flutter_dotenv.dart';
    import 'package:supabase_flutter/supabase_flutter.dart';
    import 'package:mind_fitness_now/services/notification_service/notification_service.dart';
    import 'package:mind_fitness_now/services/local_storage/local_storage_service.dart';
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      // await dotenv.load(); // Assuming EnvConfig handles this
      // await Supabase.initialize(...); // Assuming EnvConfig handles this
    
      final notificationService = NotificationService();
      await notificationService.initialize();
    
      final localStorageService = LocalStorageService();
      await localStorageService.initialize();
    
      // runApp(const MyApp()); // Need MyApp definition
    }
    
  6. Testing approach: Create unit tests for each service to verify initialization and basic functionality

Additional Implementation Details

  1. Enhanced Android notification channel configuration:

    // MainActivity.kt
    package com.mindfitnessnow
    
    import io.flutter.embedding.android.FlutterActivity
    import android.app.NotificationChannel
    import android.app.NotificationManager
    import android.content.Context
    import android.graphics.Color
    import android.os.Build
    import android.os.Bundle
    
    class MainActivity: FlutterActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            createNotificationChannels()
        }
    
        private fun createNotificationChannels() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                // General notifications channel
                val generalChannel = NotificationChannel(
                    "general_channel",
                    "General Notifications",
                    NotificationManager.IMPORTANCE_DEFAULT
                ).apply {
                    description = "General app notifications"
                    enableLights(true)
                    lightColor = Color.BLUE
                }
    
                // Reminders channel with higher priority
                val reminderChannel = NotificationChannel(
                    "reminder_channel",
                    "Practice Reminders",
                    NotificationManager.IMPORTANCE_HIGH
                ).apply {
                    description = "Meditation and practice reminders"
                    enableLights(true)
                    enableVibration(true)
                    lightColor = Color.GREEN
                }
    
                // Progress updates channel
                val progressChannel = NotificationChannel(
                    "progress_channel",
                    "Progress Updates",
                    NotificationManager.IMPORTANCE_LOW
                ).apply {
                    description = "Updates about your progress and achievements"
                }
    
                val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                notificationManager.createNotificationChannels(listOf(generalChannel, reminderChannel, progressChannel))
            }
        }
    }
    
  2. Supabase deep linking configuration:

    <!-- AndroidManifest.xml -->
    <activity
        ...>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="mindfitnessnow" android:host="login-callback" />
        </intent-filter>
    </activity>
    
  3. iOS Info.plist permission descriptions:

    <key>NSCameraUsageDescription</key>
    <string>We need camera access to allow you to update your profile picture</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>We need photo library access to allow you to choose profile pictures</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>We need microphone access for guided meditation recordings</string>
    <key>NSUserNotificationUsageDescription</key>
    <string>We'll send you reminders for your meditation practice and updates on your progress</string>
    
  4. Enhanced NotificationService methods:

    import 'package:timezone/timezone.dart' as tz;
    import 'package:timezone/data/latest.dart' as tz_data;
    
    // Inside NotificationService class:
    Future<void> scheduleNotification({
      required int id,
      required String title,
      required String body,
      required DateTime scheduledDate,
      String channelId = 'reminder_channel',
    }) async {
      tz_data.initializeTimeZones(); // Ensure timezones are initialized
    
      final androidDetails = AndroidNotificationDetails(
        channelId,
        channelId == 'reminder_channel' ? 'Practice Reminders' : 'General Notifications',
        importance: Importance.high,
        priority: Priority.high,
      );
    
      final iosDetails = DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
      );
    
      final platformDetails = NotificationDetails(
        android: androidDetails,
        iOS: iosDetails,
      );
    
      await _notificationsPlugin.zonedSchedule(
        id,
        title,
        body,
        tz.TZDateTime.from(scheduledDate, tz.local),
        platformDetails,
        androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, // Updated parameter
        uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
      );
    }
    
  5. Test file implementation example:

    // test/services/local_storage_service_test.dart
    import 'package:flutter_test/flutter_test.dart';
    // import 'package:mockito/mockito.dart'; // Requires mockito package
    import 'package:shared_preferences/shared_preferences.dart';
    
    import 'package:mind_fitness_now/services/local_storage/local_storage_service.dart';
    
    void main() {
      late LocalStorageService localStorageService;
    
      setUp(() async {
        SharedPreferences.setMockInitialValues({});
        localStorageService = LocalStorageService();
        await localStorageService.initialize();
      });
    
      test('should store and retrieve string values', () async {
        // Arrange
        const key = 'test_key';
        const value = 'test_value';
    
        // Act
        await localStorageService.setString(key, value);
        final result = localStorageService.getString(key);
    
        // Assert
        expect(result, value);
      });
    }