Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 94 additions & 56 deletions patient/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,101 @@ import 'package:patient/presentation/widgets/snackbar_service.dart';
import 'package:patient/provider/appointments_provider.dart';
import 'package:patient/provider/assessment_provider.dart';
import 'package:patient/provider/auth_provider.dart';
import 'package:patient/repository/supabase_auth_repository.dart';

import 'package:patient/provider/reports_provider.dart';
import 'package:patient/provider/task_provider.dart';
import 'package:patient/provider/therapy_goals_provider.dart';
import 'package:patient/repository/supabase_auth_repository.dart';
import 'package:patient/repository/supabase_patient_repository.dart';

import 'package:provider/provider.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();

import 'provider/task_provider.dart';
import 'provider/therapy_goals_provider.dart';
try {
// Load environment variables
await dotenv.load(fileName: ".env");

final supabaseUrl = dotenv.env['SUPABASE_URL'];
final supabaseKey = dotenv.env['SUPABASE_ANON_KEY'];
final geminiKey = dotenv.env['GEMINI_API_KEY'];

// Validate env variables
if (supabaseUrl == null || supabaseKey == null || geminiKey == null) {
throw Exception("Missing environment variables in .env file");
}

Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: ".env");
await Supabase.initialize(
url: dotenv.env['SUPABASE_URL']!,
anonKey: dotenv.env['SUPABASE_ANON_KEY']!,
);
Gemini.init(apiKey: dotenv.env['GEMINI_API_KEY']!); // Add your Gemini API key here
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.white,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
),
);

setupDependencyInjection();

runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AssessmentProvider()),
ChangeNotifierProvider(
create: (_) => AuthProvider(
authRepository: SupabaseAuthRepository(
supabaseClient: Supabase.instance.client,
// Initialize Supabase
await Supabase.initialize(
url: supabaseUrl,
anonKey: supabaseKey,
);

// Initialize Gemini
Gemini.init(apiKey: geminiKey);

// System UI styling
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.white,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Colors.white,
systemNavigationBarIconBrightness: Brightness.dark,
),
);

// Setup dependency injection
setupDependencyInjection();

// Create shared repositories
final supabaseClient = Supabase.instance.client;
final patientRepository =
SupabasePatientRepository(supabaseClient: supabaseClient);
final authRepository =
SupabaseAuthRepository(supabaseClient: supabaseClient);
Comment on lines +55 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if getIt is used elsewhere in the codebase to retrieve repositories
rg -n "getIt<" --type=dart -g '!**/dependency_injector.dart'

Repository: AOSSIE-Org/NeuroTrack

Length of output: 340


🏁 Script executed:

# Find the dependency injection setup file
fd -e dart | xargs grep -l "setupDependencyInjection"

Repository: AOSSIE-Org/NeuroTrack

Length of output: 208


🏁 Script executed:

# Look at main.dart more broadly to understand the context
find . -name "main.dart" -path "*/patient/*" -type f

Repository: AOSSIE-Org/NeuroTrack

Length of output: 88


🏁 Script executed:

# Check the provider definitions to see how repositories are passed
rg -n "PatientRepository\|AuthRepository" --type=dart -A 3 patient/lib/main.dart

Repository: AOSSIE-Org/NeuroTrack

Length of output: 47


🏁 Script executed:

# Read the dependency injector to see what gets registered
cat -n patient/lib/core/utils/dependency_injector.dart

Repository: AOSSIE-Org/NeuroTrack

Length of output: 597


🏁 Script executed:

# Read main.dart to see the full context around lines 55-63 and how repositories are used
cat -n patient/lib/main.dart | head -100

Repository: AOSSIE-Org/NeuroTrack

Length of output: 4065


Redundant repository instantiation creates inconsistent dependency management across the codebase.

setupDependencyInjection() registers SupabasePatientRepository as a singleton in GetIt, but lines 59-63 create new instances that are passed to providers. This results in:

  • Two separate SupabasePatientRepository instances in memory
  • Providers use the manually created instance
  • chat_manager.dart uses getIt<PatientRepository>(), retrieving a different instance
  • Inconsistent state management if changes occur in one instance

Update setupDependencyInjection() to also register AuthRepository, then retrieve both repositories from GetIt instead of creating them manually:

    // Setup dependency injection
    setupDependencyInjection();

-    // Create shared repositories
-    final supabaseClient = Supabase.instance.client;
-    final patientRepository =
-        SupabasePatientRepository(supabaseClient: supabaseClient);
-    final authRepository =
-        SupabaseAuthRepository(supabaseClient: supabaseClient);
+    // Retrieve shared repositories from GetIt
+    final patientRepository = getIt<PatientRepository>();
+    final authRepository = getIt<AuthRepository>();

This requires updating setupDependencyInjection() in dependency_injector.dart to register AuthRepository.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@patient/lib/main.dart` around lines 55 - 63, setupDependencyInjection
currently registers SupabasePatientRepository but not AuthRepository, causing
manual instantiation in main.dart to produce duplicate instances; update
dependency_injector.dart to register AuthRepository (mapping AuthRepository to
SupabaseAuthRepository) and then in main.dart stop creating new
SupabasePatientRepository/SupabaseAuthRepository instances and instead resolve
both repositories from getIt (e.g., getIt<PatientRepository>() and
getIt<AuthRepository>()) so providers receive the singleton instances and
chat_manager.dart's getIt<PatientRepository>() returns the same object.


runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => AssessmentProvider()),

ChangeNotifierProvider(
create: (_) => AuthProvider(
authRepository: authRepository,
),
),
),
ChangeNotifierProvider(create: (_) => ReportsProvider(
patientRepository: SupabasePatientRepository(supabaseClient: Supabase.instance.client),
)),
ChangeNotifierProvider(create: (_) => TaskProvider(
patientRepository: SupabasePatientRepository(supabaseClient: Supabase.instance.client),
)),
ChangeNotifierProvider(create: (_) => TherapyGoalsProvider(
patientRepository: SupabasePatientRepository(supabaseClient: Supabase.instance.client),
)),
ChangeNotifierProvider(create: (_) => AppointmentsProvider(
authRepository: SupabaseAuthRepository(supabaseClient: Supabase.instance.client),
patientRepository: SupabasePatientRepository(supabaseClient: Supabase.instance.client)
))
],
child: const MyApp(),
),
);

ChangeNotifierProvider(
create: (_) => ReportsProvider(
patientRepository: patientRepository,
),
),

ChangeNotifierProvider(
create: (_) => TaskProvider(
patientRepository: patientRepository,
),
),

ChangeNotifierProvider(
create: (_) => TherapyGoalsProvider(
patientRepository: patientRepository,
),
),

ChangeNotifierProvider(
create: (_) => AppointmentsProvider(
authRepository: authRepository,
patientRepository: patientRepository,
),
),
],
child: const MyApp(),
),
);
} catch (e) {
debugPrint("App initialization failed: $e");
}
Comment on lines +104 to +106

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

App fails silently if initialization errors occur - no UI is shown.

If any initialization step throws (e.g., missing .env file, Supabase connection failure), the app only logs to debug console and exits the main() function without calling runApp(). The user sees a blank/frozen screen with no feedback.

The PR objective was to "prevent crashes caused by missing .env variables," but this implementation still leaves the app in an unusable state.

🐛 Proposed fix: Show an error screen to the user
   } catch (e) {
     debugPrint("App initialization failed: $e");
+    runApp(
+      MaterialApp(
+        home: Scaffold(
+          body: Center(
+            child: Padding(
+              padding: const EdgeInsets.all(24.0),
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: [
+                  const Icon(Icons.error_outline, size: 64, color: Colors.red),
+                  const SizedBox(height: 16),
+                  const Text(
+                    'Initialization Failed',
+                    style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+                  ),
+                  const SizedBox(height: 8),
+                  Text(
+                    e.toString(),
+                    textAlign: TextAlign.center,
+                    style: const TextStyle(color: Colors.grey),
+                  ),
+                ],
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (e) {
debugPrint("App initialization failed: $e");
}
} catch (e) {
debugPrint("App initialization failed: $e");
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error_outline, size: 64, color: Colors.red),
const SizedBox(height: 16),
const Text(
'Initialization Failed',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Text(
e.toString(),
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.grey),
),
],
),
),
),
),
),
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@patient/lib/main.dart` around lines 104 - 106, The catch block in main()
currently only logs initialization errors and returns, leaving the UI blank;
update the catch to call runApp() with a simple error UI so users see feedback
instead of a frozen screen. Create or use an InitializationErrorScreen (or
similar) widget that accepts the error/stack message, and in the catch (e, s)
replace debugPrint(...) with runApp(InitializationErrorScreen(error:
e.toString(), stack: s.toString())) (or wrap it in a small MaterialApp) so the
app always renders a visible error page when initialization (e.g., in main(),
Supabase setup, or .env loading) fails. Ensure you capture both exception and
stack (catch (e, s)) and keep the debugPrint but still call runApp to display
the error widget.

}

class MyApp extends StatelessWidget {
Expand All @@ -78,10 +112,14 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: SnackbarService.scaffoldMessengerKey,
debugShowCheckedModeBanner: false,
title: 'Patient App',
theme: AppTheme.lightTheme(),
home: const SplashScreen());
scaffoldMessengerKey: SnackbarService.scaffoldMessengerKey,
debugShowCheckedModeBanner: false,
title: 'NeuroTrack Patient',
theme: AppTheme.lightTheme(),
initialRoute: '/',
routes: {
'/': (context) => const SplashScreen(),
},
);
}
}
}