Skip to content
This repository was archived by the owner on Jul 17, 2025. It is now read-only.
Open
Show file tree
Hide file tree
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
22 changes: 22 additions & 0 deletions lib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
![Image](https://github.com/user-attachments/assets/179ecb44-0167-4968-ab54-3198782716d0)

# Clean Architecture in OpenWardrobe

OpenWardrobe follows the principles of Clean Architecture, which emphasizes separation of concerns and independence of frameworks. Here's how the different components fit into this architecture:

## UI Layer
The UI layer is responsible for presenting data to the user and handling user interactions. In OpenWardrobe, this is represented by the various screens (e.g., `HomeScreen`, `WardrobeScreen`, `LookbookScreen`). These screens are built using Flutter widgets and are designed to be reactive, updating the UI based on the state of the application.

## Router Layer
The router layer manages navigation and routing within the application. It defines how users move between different screens and handles deep linking. In OpenWardrobe, the router is implemented using the `go_router` package, which provides a declarative way to manage routes and navigation.

## Repository Layer
The repository layer acts as an intermediary between the data sources (e.g., databases, APIs) and the rest of the application. It abstracts the data access logic and provides a clean API for the UI and business logic layers. In OpenWardrobe, the `AppRepository` is responsible for fetching user profiles and wardrobe items, ensuring that the UI does not need to know about the underlying data sources.

## Dependency Injection (DI) Layer
The DI layer is responsible for managing the dependencies of the application. It allows for easy swapping of implementations and promotes testability. OpenWardrobe uses the `get_it` package for dependency injection, which simplifies the process of providing instances of classes (like repositories and controllers) to the parts of the application that need them.

## Brick Layer
The brick layer contains the data models and business logic. It defines the structure of the data used throughout the application and encapsulates the business rules. In OpenWardrobe, this includes models like `UserProfile` and `WardrobeItem`, which represent the core entities of the application.

By organizing the application into these distinct layers, OpenWardrobe achieves a clean architecture that is maintainable, testable, and scalable. Each layer can evolve independently, and changes in one layer do not directly impact others, adhering to the principles of separation of concerns and dependency inversion.
6 changes: 4 additions & 2 deletions lib/di/service_locator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,7 @@ final getIt = GetIt.instance;
void setupLocator() {
// Register the AppRepository instance.
// Ensure that AppRepository.configure(...) is called before or within its constructor.
getIt.registerLazySingleton<AppRepository>(() => AppRepository());
}
getIt.registerSingleton<AppRepository>(AppRepository());
}

AppRepository get appRepository => getIt<AppRepository>();
40 changes: 40 additions & 0 deletions lib/domain/controllers/home_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:openwardrobe/brick/models/user_profile.model.dart';
import 'package:openwardrobe/repositories/app_repository.dart';

class HomeController extends ChangeNotifier {
final AppRepository _appRepository = GetIt.instance<AppRepository>();

UserProfile? _userProfile;
UserProfile? get userProfile => _userProfile;

Future<void> fetchUserProfile() async {
try {
final profiles = await _appRepository.get<UserProfile>();
if (profiles.isNotEmpty) {
_userProfile = profiles.first;
notifyListeners();
}
} catch (e) {
// Handle error
print('Error fetching user profile: $e');
}
}

void handleAddItems(BuildContext context) {
Navigator.pushNamed(context, '/camera');
}

void handleCreateOutfit() {
// Handle create outfit logic
}

void handleScheduleOutfit() {
// Handle schedule outfit logic
}

void handleViewStats() {
// Handle view stats logic
}
}
36 changes: 36 additions & 0 deletions lib/domain/controllers/lookbook_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:openwardrobe/domain/models/lookbook.model.dart';
import 'package:openwardrobe/data/repositories/app_repository.dart';

class LookbookController extends ChangeNotifier {
final AppRepository _appRepository = GetIt.instance<AppRepository>();

List<Lookbook>? _lookbooks;
List<Lookbook>? get lookbooks => _lookbooks;

Future<List<Lookbook>> fetchLookbooks() async {
try {
final lookbooks = await _appRepository.get<Lookbook>();
_lookbooks = lookbooks;
notifyListeners();
return lookbooks;
} catch (e) {
// Handle error
print('Error fetching lookbooks: $e');
return [];
}
}

void handleAddLookbook(BuildContext context) {
Navigator.pushNamed(context, '/lookbook/add');
}

void handleEditLookbook(Lookbook lookbook) {
// Handle edit lookbook logic
}

void handleDeleteLookbook(Lookbook lookbook) {
// Handle delete lookbook logic
}
}
54 changes: 54 additions & 0 deletions lib/domain/controllers/wardrobe_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:openwardrobe/domain/models/wardrobe_item.model.dart';
import 'package:openwardrobe/data/repositories/app_repository.dart';
import 'package:get_it/get_it.dart';

class WardrobeController extends ChangeNotifier {
final AppRepository _appRepository = GetIt.instance<AppRepository>();

List<WardrobeItem>? _wardrobeItems;
List<WardrobeItem>? get wardrobeItems => _wardrobeItems;

Future<List<WardrobeItem>> fetchWardrobeItems() async {
try {
final items = await _appRepository.get<WardrobeItem>();
_wardrobeItems = items;
notifyListeners();
return items;
} catch (e) {
// Handle error
print('Error fetching wardrobe items: $e');
return [];
}
}

Future<void> addItem(WardrobeItem item) async {
try {
await _appRepository.upsert<WardrobeItem>(item);
fetchWardrobeItems(); // Refresh the list after adding an item
} catch (e) {
// Handle error
print('Error adding wardrobe item: $e');
}
}

Future<void> deleteItem(WardrobeItem item) async {
try {
await _appRepository.delete<WardrobeItem>(item);
fetchWardrobeItems(); // Refresh the list after deleting an item
} catch (e) {
// Handle error
print('Error deleting wardrobe item: $e');
}
}

Future<void> updateItem(WardrobeItem item) async {
try {
await _appRepository.upsert<WardrobeItem>(item);
fetchWardrobeItems(); // Refresh the list after updating an item
} catch (e) {
// Handle error
print('Error updating wardrobe item: $e');
}
}
}
5 changes: 0 additions & 5 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ Future<void> main() async {

await AppRepository.configure(databaseFactory);




await AppRepository().initialize();

setupLocator();

runApp(const MyApp());
Expand Down
3 changes: 0 additions & 3 deletions lib/repositories/app_repository.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// 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';
Expand Down
1 change: 1 addition & 0 deletions lib/router/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../ui/screens/home/page.dart';
import '../ui/screens/wardrobe/page.dart';
import '../ui/screens/wardrobe/add/page.dart';
import '../ui/widgets/scaffold_with_navbar.dart';
import 'package:openwardrobe/di/service_locator.dart';

class AppRouter {
static final GlobalKey<NavigatorState> _rootNavigatorKey =
Expand Down
40 changes: 40 additions & 0 deletions lib/ui/screens/home/home_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:openwardrobe/brick/models/user_profile.model.dart';
import 'package:openwardrobe/repositories/app_repository.dart';

class HomeController extends ChangeNotifier {
final AppRepository _appRepository = GetIt.instance<AppRepository>();

UserProfile? _userProfile;
UserProfile? get userProfile => _userProfile;

Future<void> fetchUserProfile() async {
try {
final profiles = await _appRepository.get<UserProfile>();
if (profiles.isNotEmpty) {
_userProfile = profiles.first;
notifyListeners();
}
} catch (e) {
// Handle error
print('Error fetching user profile: $e');
}
}

void handleAddItems(BuildContext context) {
Navigator.pushNamed(context, '/camera');
}

void handleCreateOutfit() {
// Handle create outfit logic
}

void handleScheduleOutfit() {
// Handle schedule outfit logic
}

void handleViewStats() {
// Handle view stats logic
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import 'package:openwardrobe/repositories/app_repository.dart';
import 'package:openwardrobe/ui/widgets/dashboard/link.dart';
import 'package:openwardrobe/ui/widgets/user_profile/user_profile_component.dart';

class HomeScreen extends StatelessWidget {
HomeScreen({super.key});
class HomeView extends StatelessWidget {
HomeView({super.key});

final appRepo = GetIt.instance<AppRepository>();

Expand Down
36 changes: 36 additions & 0 deletions lib/ui/screens/lookbook/lookbook_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:openwardrobe/brick/models/lookbook.model.dart';
import 'package:openwardrobe/repositories/app_repository.dart';

class LookbookController extends ChangeNotifier {
final AppRepository _appRepository = GetIt.instance<AppRepository>();

List<Lookbook>? _lookbooks;
List<Lookbook>? get lookbooks => _lookbooks;

Future<List<Lookbook>> fetchLookbooks() async {
try {
final lookbooks = await _appRepository.get<Lookbook>();
_lookbooks = lookbooks;
notifyListeners();
return lookbooks;
} catch (e) {
// Handle error
print('Error fetching lookbooks: $e');
return [];
}
}

void handleAddLookbook(BuildContext context) {
Navigator.pushNamed(context, '/lookbook/add');
}

void handleEditLookbook(Lookbook lookbook) {
// Handle edit lookbook logic
}

void handleDeleteLookbook(Lookbook lookbook) {
// Handle delete lookbook logic
}
}
Loading
Loading