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
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
- Create the Flutter project using CLI:
flutter create mind_fitness_now --org com.mindfitnessnow
- Navigate to project directory:
cd mind_fitness_now
- Initialize Git repository:
git init
- Create a comprehensive .gitignore file (ensure it includes Supabase environment files)
- Make initial commit:
git add . && git commit -m "Initial project setup"
- 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+)
- Test the initial setup with
flutter run
to verify everything works correctly
- 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:
-
Update the Android configuration:
- Open
android/app/build.gradle
and set:
defaultConfig {
applicationId "com.mindfitnessnow"
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
}
- In
android/app/src/main/AndroidManifest.xml
, verify the package name:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mindfitnessnow">
-
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
-
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
-
Create a simple README.md with project setup instructions for other developers
Implementation Verification Details
-
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
-
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
-
Supabase Environment Protection:
-
Project Structure Enhancements:
-
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
Dependencies: 1.1
Description
Set up a Supabase project, obtain API keys, and configure environment variables for secure credential management.
Details
- Create a new Supabase project through the Supabase dashboard
- Set up database tables according to application requirements
- Configure authentication settings in Supabase dashboard
- Obtain Supabase URL and anon key from project settings
- Add flutter_dotenv package:
flutter pub add flutter_dotenv
- Create .env file in project root with the following variables:
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
- Add .env to .gitignore file to prevent committing sensitive information
- Create a .env.example file with placeholder values for documentation
- Update pubspec.yaml to include .env as an asset:
assets:
- .env
- 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:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await EnvConfig.init();
await Supabase.initialize(
url: EnvConfig.supabaseUrl,
anonKey: EnvConfig.supabaseAnonKey,
);
runApp(MyApp());
}
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'] ?? '';
}
}
import 'package:supabase_flutter/supabase_flutter.dart';
class SupabaseService {
final SupabaseClient client = Supabase.instance.client;
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 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:
CREATE POLICY "Users can only view their own progress"
ON user_progress
FOR SELECT
USING (auth.uid() = user_id);
Dependencies: 1.1, 1.2
Description
Add all required dependencies for the project including Supabase SDK, state management, notifications, and other utility packages.
Details
- 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
intl: ^0.19.0
freezed_annotation: ^2.4.0
json_annotation: ^4.8.0
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^2.4.0
freezed: ^2.4.0
json_serializable: ^6.7.0
- Run
flutter pub get
to install dependencies
- 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());
}
- Create a constants file for Supabase client access:
import 'package:supabase_flutter/supabase_flutter.dart';
final supabase = Supabase.instance.client;
- Testing approach: Create a simple widget test that verifies dependencies are properly initialized
Here’s the additional implementation information:
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);
}
}
import 'package:flutter/material.dart';
class AppState extends ChangeNotifier {
bool _isLoading = false;
bool get isLoading => _isLoading;
void setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
}
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
- 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
- Create base abstract classes for repositories and services
- Set up theme configuration in lib/core/theme/app_theme.dart
- Create utility classes for common functions in lib/core/utils/
- Set up constants for API endpoints, error messages, and app-wide values
- Create a barrel file (index.dart) in each directory for easier imports
- 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
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);
}
abstract class BaseService {
void initialize();
Future<void> dispose();
}
Theme Configuration Details
import 'package:flutter/material.dart';
class AppTheme {
static const Color primaryColor = Color(0xFF6200EE);
static const Color secondaryColor = Color(0xFF03DAC6);
static const Color backgroundColor = Color(0xFFF5F5F5);
static const Color errorColor = Color(0xFFB00020);
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,
),
);
}
static ThemeData get darkTheme {
}
}
Utility Classes Examples
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.';
}
}
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
export 'error_handler.dart';
export 'validators.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 {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Architecture Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Architecture Test Screen', style: AppTheme.headingStyle),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
final errorMsg = ErrorHandler.getErrorMessage(Exception());
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMsg)),
);
},
child: Text('Test Error Handler'),
),
],
),
),
);
}
}
Dependencies: 1.3, 1.4
Description
Set up platform-specific configurations for iOS and Android, including notification services and permissions.
Details
-
Configure Android settings:
-
Configure iOS settings:
- Update Info.plist with required permissions
- Configure notification capabilities in Xcode
-
Create a NotificationService class:
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationService {
final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
Future<void> initialize() async {
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);
}
}
-
Create a LocalStorageService using shared_preferences:
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);
}
}
-
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();
final notificationService = NotificationService();
await notificationService.initialize();
final localStorageService = LocalStorageService();
await localStorageService.initialize();
}
-
Testing approach: Create unit tests for each service to verify initialization and basic functionality
Additional Implementation Details
-
Enhanced Android notification channel configuration:
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) {
val generalChannel = NotificationChannel(
"general_channel",
"General Notifications",
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = "General app notifications"
enableLights(true)
lightColor = Color.BLUE
}
val reminderChannel = NotificationChannel(
"reminder_channel",
"Practice Reminders",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Meditation and practice reminders"
enableLights(true)
enableVibration(true)
lightColor = Color.GREEN
}
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))
}
}
}
-
Supabase deep linking configuration:
<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>
-
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>
-
Enhanced NotificationService methods:
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz_data;
Future<void> scheduleNotification({
required int id,
required String title,
required String body,
required DateTime scheduledDate,
String channelId = 'reminder_channel',
}) async {
tz_data.initializeTimeZones();
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,
uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime,
);
}
-
Test file implementation example:
import 'package:flutter_test/flutter_test.dart';
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 {
const key = 'test_key';
const value = 'test_value';
await localStorageService.setString(key, value);
final result = localStorageService.getString(key);
expect(result, value);
});
}