A modern, scalable Android project template built with Jetpack Compose, ViewModel, Retrofit, and SharedPreferences authentication.
This template is designed to be the starting point for full-scale apps with clean architecture, reusable components, and dependency separation.
- ✅ Jetpack Compose UI
- ✅ Shared ViewModel state (loading, error)
- ✅ Token-based authentication
- ✅ Persistent login via
SharedPreferences - ✅ Retrofit with automatic token injection
- ✅ Modular navigation system
- ✅ Manual dependency injection (no Hilt)
- ✅ Ready to scale with more APIs and ViewModels
- Jetpack Compose – UI Toolkit
- ViewModel & State – State management
- Retrofit – API client
- SharedPreferences – Token persistence
- Navigation Compose – App routing
- Material 3 – Modern design
androidtemplate/
│
├── data/
│ ├── dtos/
│ │ └── User.kt # Data class representing a user
│ ├── requests/
│ │ └── RegisterRequest.kt # Request payload for user registration
│ └── responses/
│ └── TokenResponse.kt # Response payload for JWT token
│
├── navigation/
│ ├── AppNavigation.kt # Main NavHost and navigation graph
│ └── Screen.kt # Sealed class for all navigation routes
│
├── network/
│ ├── AuthApiService.kt # Retrofit interface for auth-related API calls
│ ├── RetrofitHelper.kt # Singleton for Retrofit instance setup
│ └── TokenInterceptor.kt # Interceptor to add JWT token to requests
│
├── ui/
│ ├── composables/
│ │ └── LoadingIndicator.kt # Reusable loading indicator composable
│ ├── screens/
│ │ ├── HomeScreen.kt # Home screen shown after login
│ │ └── LoginScreen.kt # Login form screen
│ └── theme/
│ ├── Color.kt # Color definitions
│ ├── Theme.kt # AppTheme wrapper
│ └── Type.kt # Typography settings
│
├── utils/
│ ├── AppInitializer.kt # Provides token manager and services manually
│ └── TokenManager.kt # Manages saving/retrieving JWT tokens
│
├── viewmodels/
│ ├── AuthViewModel.kt # ViewModel handling login and session state
│ └── BaseViewModel.kt # Base class for shared loading/error state
│
└── MainActivity.kt # Entry point for Compose app
Go to the GitHub repository and click Fork.
git clone https://github.com/YOUR_USERNAME/YourAppName.git
cd YourAppNameUse the provided script to rename the project
chmod +x rename-template.sh
./rename-template.sh yourprojectname com.yourcompany.yourprojectnameExample:
./rename-template.sh capstone com.yourcompany.capstoneThis will automatically replace:
androidtemplate → yourprojectname
AndroidTemplate → YourProjectName
com.example.androidtemplate → com.yourcompany.yourappIt will work even if it displays red words or unresolved references.
After running the script, make sure to sync Gradle and then invalidate cache for it to fully apply.
Open in Android Studio
Select your emulator or device
Press
This project includes:
TokenManagerfor saving/retrieving JWTsAuthViewModelthat handles login/logout- Automatic token injection via
TokenInterceptor
On successful login:
- Token is saved via
SharedPreferences - User is auto-logged in on next app launch
⚠️ Backend Requirements: Your backend must include the following endpoints for this template to work:
// GET /users/me
@GetMapping("/users/me")
@PreAuthorize("isAuthenticated()")
fun getCurrentUser(): ResponseEntity<User> {
val email = SecurityContextHolder.getContext().authentication.name
val user = userRepository.findByEmail(email)
?: throw UsernameNotFoundException("User not found with email: $email")
return ResponseEntity.ok(user)
}
// POST /login
@PostMapping("/login")
fun login(@RequestBody request: AuthRequest): ResponseEntity<AuthResponse> {
val authToken = UsernamePasswordAuthenticationToken(request.email, request.password)
val authentication = authenticationManager.authenticate(authToken)
if (authentication.isAuthenticated) {
val user = userRepository.findByEmail(request.email)
?: throw UsernameNotFoundException("User not found")
val token = jwtService.generateToken(request.email, user.role.name) // remove role if not needed in your controller and jwtservice
return ResponseEntity.ok(AuthResponse(token = token, user = user))
} else {
throw UsernameNotFoundException("Invalid credentials")
}
}
// DTOs
data class AuthRequest(val email: String, val password: String)
data class AuthResponse(val token: String, val user: User)To add a new screen: Create ScreenNameScreen.kt under ui/screens
-
Add it to Screen.kt class
-
Add a composable() entry in AppNavigation.kt
To add a new API:
-
Create YourApiService.kt in network/
-
Add it in AppInitializer.kt
-
Inject it into your ViewModel
Contributions are welcome! Feel free to open issues or submit PRs.
Humoud – @HAlGhanim