Listox is an open-source mobile application built with Flutter. It helps you manage your groceries with smart predictions based on your shopping habits, supporting quick item tracking, automatic restock suggestions, and a scalable, modular architecture inspired by SOLID principles.
| Layer | Technologies | Description |
|---|---|---|
| Frontend (Open Source) | Flutter, Dart, Local Database, Localizations | Client app responsible for UI, grocery tracking, predictions display, and user interactions |
| Backend / Services | Firebase Analytics, Crashlytics, RevenueCat | Handles app analytics, crash reporting, and subscription management |
| Infrastructure | Apple In-App Purchases, Firebase Services | Manages payments, subscriptions, analytics, and app stability |
| Package | Purpose |
|---|---|
| flutter_bloc | State management |
| get_it | Dependency injection |
| app_links | Deep links |
| drift | Local database |
| dio | Network requests |
| easy_localization | Multi-language support |
| animate_do | Animations |
This section describes the architectural structure of the application and its internal dependency model. The design follows principles inspired by Clean Architecture, with emphasis on separation of concerns, modularity, and long-term maintainability.
The project is organized using a feature-based structure with explicit separation between domain, data, and presentation layers. The following diagram illustrates the base structure of the project:
flowchart TB
subgraph Presentation[" "]
direction TB
page
bloc
widget
shared
end
subgraph Feature[" "]
direction TB
data --> model_m[model]
data --> repo_m[repo]
data --> source_m[source]
domain --> entity_d[entity]
domain --> repo_d[repo]
domain --> source_d[source]
domain --> use_case_d[use_case]
end
lib --> core
core --> feature
feature --> Feature
lib --> presentation
presentation --> Presentation
The
presentation/folder is kept outside of thecore/directory to enforce strict separation between application logic and UI components. Below is what each layer is responsible for using the Profile feature as an example for typical files ⬇️
| Folder | Responsibility | Typical Files |
|---|---|---|
entity/ |
Business models | profile.dartprofile_failure.dart |
use_case/ |
Business operations | get_profile_use_case.dart |
repo/ |
Repository contracts | profile_repo.dart |
source/ |
Data source contracts | profile_local_source.dartprofile_remote_source.dart |
The domain layer contains pure business logic and application rules for implementing feature behavior in the application. It defines core use cases and entities and remains independent of external frameworks and data sources.
| Folder | Responsibility | Typical Files |
|---|---|---|
model/ |
Data transfer objects (DTOs) | profile_dto.dartprofile_response_dto.dart |
repo/ |
Repository implementations | profile_repo_impl.dart |
source/ |
Local / Remote data source implementations | profile_local_source_impl.dartprofile_remote_source_impl.dart |
The data layer is used for retrieving, storing, and transforming external data for application features. It implements domain contracts and performs DTO-to-entity mapping.
pages/contains all application screensbloc/contains Cubits and Blocs for state managementwidget/contains reusable UI componentsshared/contains routing, theming, constants, utilities, etc...
The presentation layer is separated from core feature modules as a deliberate design choice. This structure reflects a personal preference for improved screen discoverability and centralized UI infrastructure.
The application uses annotation-based dependency injection with injectable and get_it to connect layers and enable controlled execution flows.
Dependencies are registered automatically through code generation.
Example objects injection:
@LazySingleton()
class ProfileCubit extends Cubit<ProfileState> {
final GetProfileUseCase _getProfileUseCase;
ProfileCubit(this._getProfileUseCase) : super(ProfileState.initial());
}
@LazySingleton()
class GetProfileUseCase {
final ProfileRepo _profileRepo;
const GetProfileUseCase(this._profileRepo);
}Example resolution hierarchy:
ProfilePage
→ ProfileCubit
→ GetProfileUseCase
→ ProfileRepository
→ ProfileRemoteSource / ProfileLocalSource
→ API
- Static icons from Iconsax
- Animated emojis from Noto Animated Emojis — licensed under CC BY 4.0
- Fonts: Quicksand (Primary), Manrope (Secondary)
To run this application, you'll need Flutter of version 3.38.1 or higher:
# Get all packages
flutter pub get
# Generate localization files
flutter pub run easy_localization:generate -S "assets/translations" -O "lib/presentation/shared/localization"
# Generate localization keys
flutter pub run easy_localization:generate -S "assets/translations" -O "lib/presentation/shared/localization" -o "locale_keys.g.dart" -f keys
# Build runner
dart run build_runner build --delete-conflicting-outputs
# Run dev environment
flutter run --flavor dev -t lib/main_dev.dart
# Run prod environment
flutter run --flavor prod -t lib/main_prod.dartDenwee projects are crafted by a solo enthusiastic developer across Mobile, Web, and Backend technologies. Your contributions, no matter how big or small, are always welcome! Here’s how you can help:
- Open PR's – fix bugs, add features, or improve existing code.
- Submit Issues – report bugs, request features, or suggest improvements.
Explore the Denwee App, built with Flutter. The app has scalable architecture which you can follow in your projects 🔥 Check it out
Some design elements and animations were inspired by Reflectly App, adapted and implemented originally for Denwee. Definitely check out their awesome product!
If this project helped you, a quick review on the App Store or Google Play would really mean a lot! For any questions or support, please reach out to support@denwee.com 🫶