E-commerce Android app showcasing modern development practices
MVVM • Clean Architecture • Jetpack Compose • Hilt
ProductApp is a learning-focused Android application built with Kotlin and Jetpack Compose. This project demonstrates modern Android development best practices, including:
- 🎯 Clean Architecture principles
- 🔄 Reactive programming with StateFlow
- 💉 Dependency Injection with Hilt
- 🎨 Modern UI with Jetpack Compose
- 🌐 REST API consumption
📌 This is an educational project developed step-by-step with incremental commits and well-documented architectural decisions.
- ✅ Product Listing - Browse products from FakeStore API
- ✅ Image Loading - Efficient image caching with Coil
- ✅ Pull to Refresh - Update product list
- ✅ Error Handling - Graceful error states with retry mechanism
- ✅ Loading States - Smooth UX with loading indicators
- ✅ Material Design 3 - Modern, beautiful UI components
- ✅ Responsive Layout - Adapts to different screen sizes
- ✅ Dark Theme Support - System-based theme switching
- ✅ Smooth Animations - Native Compose animations
- ✅ State Management - Predictable UI states (Loading/Success/Error)
- ✅ Reactive UI - StateFlow-based reactive updates
- ✅ Type-Safe Navigation - Compose Navigation (planned)
- ✅ Offline Support - Room caching (planned)
The project follows a clean, scalable architecture pattern:
┌─────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ (Compose UI + ViewModels) │
├─────────────────────────────────────────┤
│ DOMAIN LAYER │
│ (Use Cases - Planned) │
├─────────────────────────────────────────┤
│ DATA LAYER │
│ (Repository + Data Sources) │
├─────────────────────────────────────────┤
│ EXTERNAL SERVICES │
│ (Retrofit + FakeStore API) │
└─────────────────────────────────────────┘
app/
├── ui/
│ ├── screen/
│ │ └── ProductListScreen.kt → Main product screen
│ ├── components/
│ │ ├── ProductCard.kt → Reusable product card
│ │ ├── LoadingIndicator.kt → Loading state UI
│ │ └── ErrorView.kt → Error state UI
│ ├── state/
│ │ └── UiState.kt → Sealed class for UI states
│ └── viewmodel/
│ └── ProductViewModel.kt → Business logic & state
│
├── data/
│ ├── model/
│ │ └── Product.kt → Data models
│ ├── repository/
│ │ └── ProductRepository.kt → Single source of truth
│ └── remote/
│ └── ApiService.kt → Retrofit API interface
│
└── di/
└── NetworkModule.kt → Hilt DI configuration
┌──────────┐ ┌──────────────┐ ┌────────────┐ ┌─────────┐
│ UI │ ───> │ ViewModel │ ───> │ Repository │ ───> │ API │
│ (Compose)│ <─── │ (StateFlow) │ <─── │ (Single │ <─── │(Retrofit)│
└──────────┘ └──────────────┘ │ Source) │ └─────────┘
└────────────┘
- UI observes ViewModel's StateFlow
- ViewModel requests data from Repository
- Repository fetches from API (or cache - future)
- Data flows back as UiState (Loading → Success/Error)
The app uses a sealed class to represent all possible UI states:
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String) : UiState<Nothing>()
}- ✅ Type-safe - Compiler guarantees all states are handled
- ✅ Predictable - UI always reflects current state
- ✅ Testable - Easy to unit test state transitions
- ✅ Maintainable - Clear separation of concerns
// ViewModel
private val _uiState = MutableStateFlow<UiState<List<Product>>>(UiState.Loading)
val uiState: StateFlow<UiState<List<Product>>> = _uiState.asStateFlow()
// UI (Compose)
val state by viewModel.uiState.collectAsState()
when (state) {
is UiState.Loading -> LoadingIndicator()
is UiState.Success -> ProductList(state.data)
is UiState.Error -> ErrorView(state.message, onRetry = { viewModel.retry() })
}Kotlin: 1.9.0+
├── Coroutines - Asynchronous programming
└── Flow/StateFlow - Reactive streams
Android
├── compileSdk: 34
├── minSdk: 24 (Android 7.0)
└── targetSdk: 34Jetpack Compose
├── Compose UI: 1.5.4
├── Compose Material3: 1.1.2
├── Compose Navigation: 2.7.5 (planned)
└── Compose Runtime
AndroidX
├── Core KTX: 1.12.0
├── Lifecycle ViewModel: 2.6.2
├── Activity Compose: 1.8.1
└── Lifecycle Runtime: 2.6.2Retrofit: 2.9.0
├── Converter Gson - JSON parsing
└── OkHttp - HTTP client
Image Loading
└── Coil Compose: 2.5.0Hilt: 2.48
├── Hilt Android
└── Hilt CompilerFakeStore API
└── https://fakestoreapi.com
├── GET /products - List all products
└── GET /products/{id} - Product details (planned)
# Required versions
- Android Studio: Hedgehog | 2023.1.1 or newer
- JDK: 17 or higher
- Kotlin: 1.9.0+
- Gradle: 8.0+- Clone the repository
git clone https://github.com/elvynedinson/ProductApp.git
cd ProductApp- Open in Android Studio
# File → Open → Select ProductApp folder- Sync Gradle
# Android Studio will automatically sync
# Or manually: File → Sync Project with Gradle Files- Run the app
# Select device/emulator
# Click Run button or Shift+F10# Debug build
./gradlew assembleDebug
# Release build (requires signing config)
./gradlew assembleRelease
# Install on device
./gradlew installDebug- ✅ Single Responsibility - Each class has one clear purpose
- ✅ Dependency Inversion - Depend on abstractions, not implementations
- ✅ Immutability - Data classes are immutable
- ✅ Type Safety - Leveraging Kotlin's type system
- ✅ Separation of Concerns - UI, Business Logic, Data are separated
- ✅ Unidirectional Data Flow - Data flows in one direction
- ✅ Single Source of Truth - Repository pattern
- ✅ Reactive UI - State-driven UI updates
- ✅ Incremental Development - Small, focused commits
- ✅ Descriptive Commits - Clear commit messages
- ✅ Code Comments - Explaining "why", not "what"
- ✅ Kotlin Conventions - Following official style guide
- ✅ Efficient Recomposition - Using
rememberandderivedStateOf - ✅ Image Caching - Coil handles caching automatically
- ✅ Coroutine Scoping - Proper lifecycle-aware coroutines
- ✅ State Hoisting - Reusable, stateless composables
The project is being built with testability in mind, though tests are not yet implemented.
This project was built following industry best practices and official documentation:
- Android Developer Guides
- Jetpack Compose Documentation
- Kotlin Coroutines Guide
- Clean Architecture by Uncle Bob
- Google's Architecture Samples
This is a personal learning project, but suggestions and feedback are welcome!
If you want to contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is open source and available under the MIT License.
Elvyn Edinson
Android Developer in training 🚀
- GitHub: @elvynedinson
- LinkedIn: Elvyn Edinson
- Email: elvyn.paucar.ponce@gmail.com
- ✅ Kotlin
- ✅ Jetpack Compose
- ✅ MVVM Architecture
- ✅ Clean Architecture Principles
- ✅ Dependency Injection (Hilt)
- ✅ Reactive Programming (Coroutines, Flow)
- ✅ REST API Integration
- ✅ Material Design 3
- FakeStore API for providing the free REST API
- Android Community for excellent documentation and support
- JetBrains for Kotlin and IntelliJ IDEA
- Google for Android and Jetpack libraries
Lines of Code: ~500 (and growing)
Commits: Incremental and descriptive
Architecture: MVVM + Clean Architecture
UI Framework: 100% Jetpack Compose