diff --git a/lib/controllers/camera_controller.dart b/lib/controllers/camera_controller.dart index 83c34f2..de2f55f 100644 --- a/lib/controllers/camera_controller.dart +++ b/lib/controllers/camera_controller.dart @@ -2,11 +2,13 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart' show Uint8List, kIsWeb; import 'package:image_picker/image_picker.dart'; -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; class CameraController { - final AppRepository _appRepository = GetIt.instance(); + final AppRepository _appRepository; + + CameraController(this._appRepository); Future> pickImages({bool fromGallery = false}) async { if (kIsWeb) { diff --git a/lib/controllers/home_controller.dart b/lib/controllers/home_controller.dart index 6f4589d..9f745ef 100644 --- a/lib/controllers/home_controller.dart +++ b/lib/controllers/home_controller.dart @@ -1,19 +1,15 @@ -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; import 'package:openwardrobe/brick/models/user_profile.model.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; - class HomeController { - final AppRepository _appRepository = GetIt.instance(); - + final AppRepository _appRepository; + HomeController(this._appRepository); Stream> fetchUserProfile() { - final usersStream = _appRepository.subscribe(); - - return usersStream; - + final usersStream = _appRepository.subscribe(); + return usersStream; } - } diff --git a/lib/controllers/lookbook_controller.dart b/lib/controllers/lookbook_controller.dart index cd85761..6f90cdb 100644 --- a/lib/controllers/lookbook_controller.dart +++ b/lib/controllers/lookbook_controller.dart @@ -1,9 +1,11 @@ -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; import 'package:openwardrobe/brick/models/lookbook.model.dart'; class LookbookController { - final AppRepository _appRepository = GetIt.instance(); + final AppRepository _appRepository; + + LookbookController(this._appRepository); Future> fetchLookbookItems() async { try { diff --git a/lib/controllers/settings_account_controller.dart b/lib/controllers/settings_account_controller.dart index a63544f..38e5e62 100644 --- a/lib/controllers/settings_account_controller.dart +++ b/lib/controllers/settings_account_controller.dart @@ -1,13 +1,15 @@ import 'dart:io'; import 'package:flutter/foundation.dart' show Uint8List, kIsWeb; -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:image_picker/image_picker.dart'; import 'package:openwardrobe/brick/models/user_profile.model.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; class SettingsAccountController { - final AppRepository _appRepository = GetIt.instance(); + final AppRepository _appRepository; + + SettingsAccountController(this._appRepository); Future fetchUserProfile() async { try { diff --git a/lib/controllers/settings_controller.dart b/lib/controllers/settings_controller.dart index acd4551..a3dea92 100644 --- a/lib/controllers/settings_controller.dart +++ b/lib/controllers/settings_controller.dart @@ -1,8 +1,10 @@ -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; class SettingsController { - final AppRepository _appRepository = GetIt.instance(); + final AppRepository _appRepository; + + SettingsController(this._appRepository); Future> fetchSettings() async { try { diff --git a/lib/controllers/wardrobe_controller.dart b/lib/controllers/wardrobe_controller.dart index 6993035..795d095 100644 --- a/lib/controllers/wardrobe_controller.dart +++ b/lib/controllers/wardrobe_controller.dart @@ -1,11 +1,13 @@ import 'package:brick_core/core.dart'; -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/brick/models/outfit.model.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; import 'package:openwardrobe/brick/models/wardrobe_item.model.dart'; class WardrobeController { - final AppRepository _appRepository = GetIt.instance(); + final AppRepository _appRepository; + + WardrobeController(this._appRepository); Future> fetchWardrobeItems() async { try { @@ -19,7 +21,6 @@ class WardrobeController { Future> fetchOutfits() async { try { return await _appRepository.get(); - } catch (e) { // Handle error throw Exception('Failed to fetch outfits: $e'); diff --git a/lib/di/service_locator.dart b/lib/di/service_locator.dart deleted file mode 100644 index 1521d1c..0000000 --- a/lib/di/service_locator.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:get_it/get_it.dart'; - -import '../repositories/app_repository.dart'; -import '../controllers/camera_controller.dart'; -import '../controllers/home_controller.dart'; -import '../controllers/wardrobe_controller.dart'; -import '../controllers/lookbook_controller.dart'; -import '../controllers/settings_account_controller.dart'; // Import the new controller - -final getIt = GetIt.instance; - -void setupLocator() { - // Register the AppRepository instance. - getIt.registerLazySingleton(() => AppRepository()); - - // Register controllers - getIt.registerLazySingleton(() => CameraController()); - getIt.registerLazySingleton(() => HomeController()); - getIt.registerLazySingleton(() => WardrobeController()); - getIt.registerLazySingleton(() => LookbookController()); - getIt.registerLazySingleton(() => SettingsAccountController()); // Register the new controller -} diff --git a/lib/main.dart b/lib/main.dart index 004d83c..7a43ab6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,31 +2,21 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:openwardrobe/repositories/app_repository.dart'; import 'router/app_router.dart'; - -import 'package:openwardrobe/di/service_locator.dart'; - -// sqflite_common_ffi_web +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart'; - import 'package:flutter/foundation.dart' show kIsWeb; import 'package:sqflite/sqflite.dart' show databaseFactory; - Future main() async { WidgetsFlutterBinding.ensureInitialized(); - if (kIsWeb) { - databaseFactory = databaseFactoryFfiWeb; - } - - await AppRepository.configure(databaseFactory); - - - + if (kIsWeb) { + databaseFactory = databaseFactoryFfiWeb; + } - await AppRepository().initialize(); + await AppRepository.configure(databaseFactory); - setupLocator(); + await AppRepository().initialize(); runApp(const MyApp()); } @@ -36,16 +26,23 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp.router( - title: 'OpenWardrobe', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: const Color.fromARGB(255, 33, 243, 163)), - useMaterial3: true, // Enable Material 3 (modern UI) + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => AppRepository(), + ), + // Add other BlocProviders here as needed + ], + child: MaterialApp.router( + title: 'OpenWardrobe', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: const Color.fromARGB(255, 33, 243, 163)), + useMaterial3: true, // Enable Material 3 (modern UI) + ), + darkTheme: ThemeData.dark(), // Support dark mode + themeMode: ThemeMode.system, // Automatically switch theme + routerConfig: AppRouter.router, ), - darkTheme: ThemeData.dark(), // Support dark mode - themeMode: ThemeMode.system, // Automatically switch theme - - routerConfig: AppRouter.router, ); } } diff --git a/lib/repositories/app_repository.dart b/lib/repositories/app_repository.dart index ffde7d6..383ab4a 100644 --- a/lib/repositories/app_repository.dart +++ b/lib/repositories/app_repository.dart @@ -1,13 +1,13 @@ -// Saved in my_app/lib/src/brick/repository.dart import 'package:brick_offline_first_with_supabase/brick_offline_first_with_supabase.dart'; import 'package:brick_sqlite/brick_sqlite.dart'; import 'package:brick_sqlite/memory_cache_provider.dart'; -// This hide is for Brick's @Supabase annotation; in most cases, -// supabase_flutter **will not** be imported in application code. import 'package:brick_supabase/brick_supabase.dart' hide Supabase; import 'package:openwardrobe/brick/db/schema.g.dart'; import 'package:sqflite_common/sqlite_api.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; +import 'package:hydrated_bloc/hydrated_bloc.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:path/path.dart' as p; import 'package:openwardrobe/brick/brick.g.dart'; @@ -40,6 +40,13 @@ class AppRepository extends OfflineFirstWithSupabaseRepository { modelDictionary: supabaseModelDictionary, ); + final storageDirectory = await getApplicationDocumentsDirectory(); + final storagePath = p.join(storageDirectory.path, 'hydrated_bloc'); + + HydratedBloc.storage = await HydratedStorage.build( + storageDirectory: Directory(storagePath), + ); + _instance = AppRepository._( supabaseProvider: provider, sqliteProvider: SqliteProvider( diff --git a/lib/router/app_router.dart b/lib/router/app_router.dart index dc80c55..b982e84 100644 --- a/lib/router/app_router.dart +++ b/lib/router/app_router.dart @@ -5,6 +5,7 @@ import 'package:openwardrobe/ui/screens/camera/page.dart'; import 'package:openwardrobe/ui/screens/lookbook/page.dart'; import 'package:openwardrobe/ui/screens/wardrobe/settings/page.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import '../ui/screens/auth/page.dart'; import '../ui/screens/home/page.dart'; @@ -59,7 +60,10 @@ class AppRouter { GoRoute( path: '/', name: 'Home', - builder: (context, state) => HomeScreen(), + builder: (context, state) => BlocProvider( + create: (context) => HomeController(context.read()), + child: HomeScreen(), + ), ), ], ), @@ -69,7 +73,10 @@ class AppRouter { GoRoute( path: '/wardrobe', name: 'Wardrobe', - builder: (context, state) => WardrobeScreen(), + builder: (context, state) => BlocProvider( + create: (context) => WardrobeController(context.read()), + child: WardrobeScreen(), + ), ), ], ), @@ -79,7 +86,10 @@ class AppRouter { GoRoute( path: '/lookbook', name: 'Lookbook', - builder: (context, state) => LookbookScreen(), + builder: (context, state) => BlocProvider( + create: (context) => LookbookController(context.read()), + child: LookbookScreen(), + ), ), ], ), diff --git a/lib/ui/screens/home/page.dart b/lib/ui/screens/home/page.dart index ef10757..724c4f0 100644 --- a/lib/ui/screens/home/page.dart +++ b/lib/ui/screens/home/page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:openwardrobe/brick/models/user_profile.model.dart'; import 'package:openwardrobe/controllers/home_controller.dart'; @@ -9,8 +9,6 @@ import 'package:openwardrobe/ui/widgets/user_profile/user_profile_component.dart class HomeScreen extends StatelessWidget { HomeScreen({super.key}); - final HomeController homeController = GetIt.instance(); - @override Widget build(BuildContext context) { return Scaffold( @@ -26,22 +24,15 @@ class HomeScreen extends StatelessWidget { const SizedBox(height: 20), ConstrainedBox( constraints: const BoxConstraints(maxWidth: 500), - child: StreamBuilder>( - stream: homeController.fetchUserProfile(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { + child: BlocBuilder>( + builder: (context, userProfile) { + if (userProfile.isEmpty) { return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center(child: Text('No profile found')); - } - - final userProfile = snapshot.data!.first; // Assuming there is always one user profile - - return UserProfileComponent(item: userProfile); - }, -), + } else { + return UserProfileComponent(item: userProfile.first); + } + }, + ), ), const SizedBox(height: 20), ConstrainedBox( diff --git a/lib/ui/screens/lookbook/page.dart b/lib/ui/screens/lookbook/page.dart index bc3085e..0f528ff 100644 --- a/lib/ui/screens/lookbook/page.dart +++ b/lib/ui/screens/lookbook/page.dart @@ -1,14 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/brick/models/lookbook.model.dart'; -import 'package:get_it/get_it.dart'; import 'package:openwardrobe/ui/widgets/lookbook/lookbook_component.dart'; import 'package:openwardrobe/controllers/lookbook_controller.dart'; class LookbookScreen extends StatelessWidget { LookbookScreen({super.key}); - final LookbookController lookbookController = GetIt.instance(); - @override Widget build(BuildContext context) { return Scaffold( @@ -25,25 +23,17 @@ class LookbookScreen extends StatelessWidget { Expanded( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 500), - child: FutureBuilder>( - future: lookbookController.fetchLookbookItems(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { + child: BlocBuilder>( + builder: (context, lookbookItems) { + if (lookbookItems.isEmpty) { return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center( - child: Text('Error: ${snapshot.error}'), - ); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center(child: Text('No items found')); } else { - final items = snapshot.data!; return SingleChildScrollView( child: Wrap( spacing: 8.0, runSpacing: 8.0, alignment: WrapAlignment.start, - children: items.map((item) => + children: lookbookItems.map((item) => Container( width: 150, child: LookbookComponent(item: item), diff --git a/lib/ui/screens/wardrobe/page.dart b/lib/ui/screens/wardrobe/page.dart index e1f5637..ace2ea7 100644 --- a/lib/ui/screens/wardrobe/page.dart +++ b/lib/ui/screens/wardrobe/page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:get_it/get_it.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:openwardrobe/brick/models/outfit.model.dart'; import 'package:openwardrobe/brick/models/wardrobe_item.model.dart'; import 'package:openwardrobe/controllers/wardrobe_controller.dart'; @@ -14,25 +14,23 @@ class WardrobeScreen extends StatefulWidget { } class _WardrobeScreenState extends State { - final WardrobeController wardrobeController = - GetIt.instance(); + late WardrobeController wardrobeController; + + @override + void initState() { + super.initState(); + wardrobeController = context.read(); + } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Wardrobe')), - body: FutureBuilder>( - future: wardrobeController.fetchWardrobeItems(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { + body: BlocBuilder>( + builder: (context, wardrobeItems) { + if (wardrobeItems.isEmpty) { return const Center(child: CircularProgressIndicator()); - } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); - } else if (!snapshot.hasData || snapshot.data!.isEmpty) { - return const Center(child: Text('No items found')); } else { - final items = snapshot.data!; - return SingleChildScrollView( child: Align( alignment: Alignment.topCenter, @@ -41,24 +39,16 @@ class _WardrobeScreenState extends State { Wrap( spacing: 10, runSpacing: 10, - children: items + children: wardrobeItems .map((item) => WardrobeItemComponent(item: item)) .toList(), ), const SizedBox(height: 20), - FutureBuilder>( - future: wardrobeController.fetchOutfits(), - builder: (context, outfitSnapshot) { - if (outfitSnapshot.connectionState == ConnectionState.waiting) { - // Show wardrobe items while outfits are still loading + BlocBuilder>( + builder: (context, outfits) { + if (outfits.isEmpty) { return const Center(child: CircularProgressIndicator()); - } else if (outfitSnapshot.hasError) { - return Center(child: Text('Error: ${outfitSnapshot.error}')); - } else if (!outfitSnapshot.hasData || outfitSnapshot.data!.isEmpty) { - return const Center(child: Text('No outfits found')); } else { - final outfits = outfitSnapshot.data!; - return Wrap( spacing: 10, runSpacing: 10,