■ AthleteTracker Pro
Android Application — Full Source Code Guide
Tech Stack Kotlin + Jetpack Compose
Backend / DB Firebase Firestore + Room DB
AI Feature Gemini API (voice → text reviews)
Media Coil Library (async image loading)
Target Android 8.0+ (API 26+)
Auth Firebase Authentication
This document contains complete Kotlin source code, UI layout guidance, screen-by-screen implementation
details, and project setup instructions for the AthleteTracker Pro Android application.
Table of Contents
• 1. Project Setup & Dependencies ([Link])
• 2. Firebase Configuration
• 3. Data Models (Kotlin data classes)
• 4. Splash Screen
• 5. Login Screen — UI + ViewModel
• 6. Register / Sign-Up Screen
• 7. Home / Dashboard Screen
• 8. Athlete Profile Card
• 9. Trial Logger Screen
• 10. Milestone Badges Screen
• 11. Leaderboard Screen
• 12. Navigation Graph
• 13. Firebase Auth Repository
• 14. Room DB Setup
• 15. UI Color Theme & Typography
• 16. Drawable Resources Guide
• 17. [Link]
1. Project Setup & Dependencies
[Link] (app level)
plugins { id '[Link]' id '[Link]' id
'[Link]-services' id '[Link]' } android { namespace
'[Link]' compileSdk 34 defaultConfig { applicationId "[Link]" minSdk 26
targetSdk 34 versionCode 1 versionName "1.0" } buildFeatures { compose true } composeOptions {
kotlinCompilerExtensionVersion '1.5.3' } } dependencies { // Compose BOM implementation
platform('[Link]:compose-bom:2024.02.00') implementation '[Link]:ui' implementation
'[Link].material3:material3' implementation '[Link]:ui-tooling-preview'
implementation '[Link]:activity-compose:1.8.2' // Navigation implementation
'[Link]:navigation-compose:2.7.7' // Firebase implementation
platform('[Link]:firebase-bom:32.7.2') implementation '[Link]:firebase-auth-ktx'
implementation '[Link]:firebase-firestore-ktx' // Room DB implementation
'[Link]:room-runtime:2.6.1' implementation '[Link]:room-ktx:2.6.1' ksp
'[Link]:room-compiler:2.6.1' // ViewModel + LiveData implementation
'[Link]:lifecycle-viewmodel-compose:2.7.0' implementation
'[Link]:lifecycle-runtime-ktx:2.7.0' // Coil (image loading) implementation
'[Link]-kt:coil-compose:2.5.0' // Gemini API implementation
'[Link]:generativeai:0.2.2' // Coroutines implementation
'[Link]:kotlinx-coroutines-android:1.7.3' }
2. Firebase Configuration
Step-by-step Firebase setup:
1. Go to [Link] and create a new project named 'AthleteTracker'.
2. Add an Android app with package name: [Link]
3. Download [Link] and place it in the /app directory.
4. Enable Email/Password Authentication in Firebase Console → Authentication → Sign-in method.
5. Create a Firestore database in test mode (you can add security rules later).
6. Add the google-services plugin in project-level [Link]:
// Project-level [Link] buildscript { dependencies { classpath
'[Link]:google-services:4.4.1' } }
3. Data Models
[Link]
package [Link] import [Link] import [Link]
@Entity(tableName = "athletes") data class Athlete( @PrimaryKey val id: String = "", val name: String = "",
val age: Int = 0, val sport: String = "", // e.g. "Kabaddi", "Athletics" val school: String = "", val
photoUrl: String = "", val totalPoints: Int = 0, val rank: Int = 0 ) data class Trial( val id: String = "",
val athleteId: String = "", val eventType: String = "", // "Sprint 100m", "Long Jump", etc. val value:
Double = 0.0, // seconds or metres val unit: String = "", // "sec" or "m" val timestamp: Long =
[Link]() ) data class Badge( val id: String = "", val title: String = "", // "District
Level Ready" val description: String = "", val iconRes: Int = 0, val earnedAt: Long = 0L, val isEarned:
Boolean = false ) data class LeaderboardEntry( val rank: Int = 0, val athleteName: String = "", val sport:
String = "", val points: Int = 0, val photoUrl: String = "" )
4. Splash Screen
[Link]
package [Link] import [Link].* import
[Link] import [Link].* import
[Link] import [Link].* import [Link]
import [Link] import [Link] import
[Link] import [Link] import
[Link] import [Link] import [Link]
@Composable fun SplashScreen(navController: NavController) { val scale = remember { Animatable(0f) }
LaunchedEffect(Unit) { [Link](1f, animationSpec = tween(800, easing = FastOutSlowInEasing))
delay(1500) [Link]("login") { popUpTo("splash") { inclusive = true } } } Box( modifier =
Modifier .fillMaxSize() .background(Color(0xFF1565C0)), contentAlignment = [Link] ) {
Column(horizontalAlignment = [Link], modifier = [Link]([Link])) {
Text("■", fontSize = [Link]) Spacer(modifier = [Link]([Link])) Text("AthleteTracker", fontSize =
[Link], fontWeight = [Link], color = [Link]) Text("Pro", fontSize = [Link], color =
Color(0xFFFFCC02)) } } }
5. Login Screen
Login Screen — UI Design Description
The Login screen uses a blue-to-dark gradient background (#1565C0 → #0D47A1). At the top center: app logo emoji
(■) + 'AthleteTracker Pro' title in white. Below: a white rounded Card with Email TextField, Password TextField (with
eye toggle), a blue 'Login' button, a 'Forgot Password?' text link, and a 'Don't have an account? Sign Up' link.
[Link]
package [Link] import [Link] import
[Link] import [Link] import
[Link] import [Link] import
[Link] import [Link] sealed class AuthState { object Idle :
AuthState() object Loading : AuthState() object Success : AuthState() data class Error(val message: String)
: AuthState() } class AuthViewModel : ViewModel() { private val auth = [Link]() private
val _authState = MutableStateFlow([Link]) val authState: StateFlow = _authState fun login(email:
String, password: String) { if ([Link]() || [Link]()) { _authState.value =
[Link]("Please fill all fields") return } [Link] { _authState.value =
[Link] try { [Link](email, password).await() _authState.value =
[Link] } catch (e: Exception) { _authState.value = [Link]([Link] ?: "Login failed")
} } } fun register(name: String, email: String, password: String, sport: String) { if ([Link]() ||
[Link]() || [Link]()) { _authState.value = [Link]("Please fill all fields")
return } [Link] { _authState.value = [Link] try { val result =
[Link](email, password).await() // Save athlete profile to Firestore val uid
= [Link]?.uid ?: return@launch val db = [Link]()
[Link]("athletes").document(uid).set( mapOf("name" to name, "email" to email, "sport" to sport,
"uid" to uid) ).await() _authState.value = [Link] } catch (e: Exception) { _authState.value =
[Link]([Link] ?: "Registration failed") } } } fun resetPassword(email: String) {
[Link] { try { [Link](email).await() _authState.value =
[Link]("Reset email sent!") } catch (e: Exception) { _authState.value = [Link]([Link]
?: "Error") } } } }
[Link]
package [Link] import [Link] import
[Link].* import [Link] import
[Link] import [Link] import
[Link].* import [Link].material3.* import
[Link].* import [Link] import [Link] import
[Link] import [Link] import
[Link] import [Link].* import
[Link].* import [Link] import
[Link] @Composable fun LoginScreen(navController: NavController, vm:
AuthViewModel = viewModel()) { var email by remember { mutableStateOf("") } var password by remember {
mutableStateOf("") } var showPass by remember { mutableStateOf(false) } val state by
[Link]() LaunchedEffect(state) { if (state is [Link]) {
[Link]("home") { popUpTo("login") { inclusive = true } } } } Box( modifier = Modifier
.fillMaxSize() .background( [Link]( colors = listOf(Color(0xFF1565C0), Color(0xFF0D47A1))
) ), contentAlignment = [Link] ) { Column(horizontalAlignment = [Link],
modifier = [Link]([Link])) { // App Logo & Title Text("■", fontSize = [Link]) Text("AthleteTracker
Pro", fontSize = [Link], fontWeight = [Link], color = [Link]) Text("Track. Train. Triumph.",
fontSize = [Link], color = Color(0xFFBBDEFB)) Spacer(modifier = [Link]([Link])) // White Card Card(
shape = RoundedCornerShape([Link]), colors = [Link](containerColor = [Link]),
modifier = [Link]() ) { Column(modifier = [Link]([Link]), verticalArrangement =
[Link]([Link])) { Text("Welcome Back", fontSize = [Link], fontWeight = [Link], color =
Color(0xFF1565C0)) // Email OutlinedTextField( value = email, onValueChange = { email = it }, label = {
Text("Email") }, leadingIcon = { Icon([Link], null) }, keyboardOptions = KeyboardOptions(
keyboardType = [Link]), modifier = [Link](), shape = RoundedCornerShape([Link])
) // Password OutlinedTextField( value = password, onValueChange = { password = it }, label = {
Text("Password") }, leadingIcon = { Icon([Link], null) }, trailingIcon = { IconButton(onClick =
{ showPass = !showPass }) { Icon(if (showPass) [Link] else [Link],
null) } }, visualTransformation = if (showPass) [Link] else
PasswordVisualTransformation(), keyboardOptions = KeyboardOptions( keyboardType = [Link]),
modifier = [Link](), shape = RoundedCornerShape([Link]) ) // Error if (state is
[Link]) { Text((state as [Link]).message, color = [Link],
fontSize = [Link]) } // Login Button Button( onClick = { [Link](email, password) }, modifier =
[Link]().height([Link]), shape = RoundedCornerShape([Link]), enabled = state !is
[Link], colors = [Link]( containerColor = Color(0xFF1565C0)) ) { if (state
is [Link]) CircularProgressIndicator(color = [Link], modifier = [Link]([Link])) else
Text("Login", fontSize = [Link], fontWeight = [Link]) } // Forgot Password TextButton(onClick = {
[Link](email) }, modifier = [Link]([Link])) { Text("Forgot Password?", color =
Color(0xFF1565C0), fontSize = [Link]) } HorizontalDivider() // Sign Up link Row(modifier =
[Link](), horizontalArrangement = [Link]) { Text("Don't have an account? ",
color = [Link], fontSize = [Link]) TextButton(onClick = { [Link]("register") }) {
Text("Sign Up", color = Color(0xFFE65100), fontWeight = [Link], fontSize = [Link]) } } } } } } }
6. Register / Sign-Up Screen
[Link]
package [Link] import [Link] import
[Link].* import [Link] import
[Link] import
[Link] import [Link] import
[Link] import [Link].* import
[Link].material3.* import [Link].* import [Link] import
[Link] import [Link] import
[Link] import [Link] import
[Link].* import [Link].* import
[Link] import [Link]
@OptIn(ExperimentalMaterial3Api::class) @Composable fun RegisterScreen(navController: NavController, vm:
AuthViewModel = viewModel()) { var name by remember { mutableStateOf("") } var email by remember {
mutableStateOf("") } var password by remember { mutableStateOf("") } var confirmPass by remember {
mutableStateOf("") } var sport by remember { mutableStateOf("") } var age by remember { mutableStateOf("")
} var school by remember { mutableStateOf("") } var showPass by remember { mutableStateOf(false) } var
expanded by remember { mutableStateOf(false) } val sports =
listOf("Kabaddi","Athletics","Football","Basketball","Cricket","Volleyball") val state by
[Link]() LaunchedEffect(state) { if (state is [Link]) {
[Link]("home") { popUpTo("register") { inclusive = true } } } } Box(modifier =
[Link]() .background([Link]( listOf(Color(0xFF1565C0), Color(0xFF0D47A1)))))
{ Column(modifier = [Link]() .verticalScroll(rememberScrollState()).padding([Link]),
horizontalAlignment = [Link]) { Spacer([Link]([Link])) Text("■", fontSize =
[Link]) Text("Create Account", fontSize = [Link], fontWeight = [Link], color = [Link])
Spacer([Link]([Link])) Card(shape = RoundedCornerShape([Link]), colors =
[Link]([Link]), modifier = [Link]()) { Column(modifier =
[Link]([Link]), verticalArrangement = [Link]([Link])) { Text("Athlete Registration",
fontSize = [Link], fontWeight = [Link], color = Color(0xFF1565C0)) OutlinedTextField(value = name,
onValueChange = { name = it }, label = { Text("Full Name") }, leadingIcon = { Icon([Link],
null) }, modifier = [Link](), shape = RoundedCornerShape([Link])) OutlinedTextField(value =
age, onValueChange = { age = it }, label = { Text("Age") }, leadingIcon = { Icon([Link], null)
}, keyboardOptions = KeyboardOptions( keyboardType = [Link]), modifier =
[Link](), shape = RoundedCornerShape([Link])) OutlinedTextField(value = school,
onValueChange = { school = it }, label = { Text("School Name") }, leadingIcon = {
Icon([Link], null) }, modifier = [Link](), shape = RoundedCornerShape([Link]))
// Sport Dropdown ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = !expanded })
{ OutlinedTextField(value = sport, onValueChange = {}, readOnly = true, label = { Text("Primary Sport") },
leadingIcon = { Icon([Link], null) }, trailingIcon = {
[Link](expanded) }, modifier = [Link]().fillMaxWidth(),
shape = RoundedCornerShape([Link])) ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded
= false }) { [Link] { s -> DropdownMenuItem(text = { Text(s) }, onClick = { sport = s; expanded =
false }) } } } OutlinedTextField(value = email, onValueChange = { email = it }, label = { Text("Email") },
leadingIcon = { Icon([Link], null) }, keyboardOptions = KeyboardOptions(keyboardType =
[Link]), modifier = [Link](), shape = RoundedCornerShape([Link]))
OutlinedTextField(value = password, onValueChange = { password = it }, label = { Text("Password") },
leadingIcon = { Icon([Link], null) }, trailingIcon = { IconButton(onClick = { showPass =
!showPass }) { Icon(if (showPass) [Link] else [Link], null) } },
visualTransformation = if (showPass) [Link] else PasswordVisualTransformation(),
modifier = [Link](), shape = RoundedCornerShape([Link])) OutlinedTextField(value =
confirmPass, onValueChange = { confirmPass = it }, label = { Text("Confirm Password") }, leadingIcon = {
Icon([Link], null) }, visualTransformation = PasswordVisualTransformation(), modifier =
[Link](), shape = RoundedCornerShape([Link]), isError = [Link]() &&
confirmPass != password) if (state is [Link]) Text((state as [Link]).message, color =
[Link], fontSize = [Link]) Button(onClick = { if (password == confirmPass)
[Link](name, email, password, sport) }, modifier = [Link]().height([Link]), shape =
RoundedCornerShape([Link]), enabled = state !is [Link], colors = [Link](
containerColor = Color(0xFFE65100))) { if (state is [Link]) CircularProgressIndicator(color =
[Link], modifier = [Link]([Link])) else Text("Create Account", fontSize = [Link], fontWeight =
[Link]) } Row([Link](), horizontalArrangement = [Link]) {
Text("Already have an account? ", color = [Link], fontSize = [Link]) TextButton(onClick = {
[Link]() }) { Text("Login", color = Color(0xFF1565C0), fontWeight = [Link],
fontSize = [Link]) } } } } Spacer([Link]([Link])) } } }
8. Athlete Profile Card
[Link]
package [Link] import [Link] import
[Link].* import [Link].* import
[Link] import [Link].* import
[Link].material3.* import [Link].* import [Link].* import
[Link] import [Link] import
[Link] import [Link] import
[Link] import [Link].* import [Link]
import [Link] @Composable fun AthleteProfileCard(athlete: Athlete) {
Card( modifier = [Link]().padding([Link]), shape = RoundedCornerShape([Link]), elevation =
[Link]([Link]) ) { Box { // Gradient header background Box(modifier =
[Link]().height([Link]) .background([Link]( listOf(Color(0xFF1565C0),
Color(0xFF42A5F5))))) Column(modifier = [Link]([Link])) { Row(verticalAlignment =
[Link]) { // Profile Photo (circular) if ([Link]()) {
AsyncImage(model = [Link], contentDescription = "Profile", modifier = [Link]([Link])
.clip(CircleShape), contentScale = [Link]) } else { Box(modifier = [Link]([Link])
.clip(CircleShape) .background(Color(0xFFE65100)), contentAlignment = [Link]) {
Text([Link]()?.toString() ?: "A", fontSize = [Link], color = [Link], fontWeight =
[Link]) } } Spacer([Link]([Link])) Column { Text([Link], fontSize = [Link],
fontWeight = [Link], color = [Link]) Text("Age: ${[Link]}", color = Color(0xFFBBDEFB),
fontSize = [Link]) AssistChip(onClick = {}, label = { Text([Link], fontSize = [Link]) }, leadingIcon =
{ Icon([Link], null, [Link]([Link])) }) } } Spacer([Link]([Link]))
// Stats Row Row(modifier = [Link](), horizontalArrangement = [Link]) {
StatItem("■", "${[Link]}", "Points") StatItem("■", "#${[Link]}", "Rank") StatItem("■",
[Link](12), "School") } } } } } @Composable fun StatItem(emoji: String, value: String, label:
String) { Column(horizontalAlignment = [Link]) { Text(emoji, fontSize = [Link])
Text(value, fontWeight = [Link], fontSize = [Link], color = Color(0xFF1565C0)) Text(label, fontSize
= [Link], color = [Link]) } }
9. Trial Logger Screen
[Link]
package [Link] import [Link].* import
[Link] import [Link] import
[Link] import [Link] import
[Link].* import [Link].material3.* import
[Link].* import [Link].* import [Link] import
[Link] import [Link].* // Simple stopwatch state data
class StopwatchState(val isRunning: Boolean = false, val elapsedMs: Long = 0L) @Composable fun
TrialLoggerScreen() { var selectedEvent by remember { mutableStateOf("Sprint 100m") } var stopwatch by
remember { mutableStateOf(StopwatchState()) } var distance by remember { mutableStateOf("") } var trialLog
by remember { mutableStateOf(listOf()) } val events = listOf("Sprint 100m","Sprint 200m","Long Jump","High
Jump","Shot Put") var expanded by remember { mutableStateOf(false) } LaunchedEffect([Link]) {
if ([Link]) { val start = [Link]() - [Link] while
([Link]) { [Link](10) stopwatch = [Link]( elapsedMs =
[Link]() - start) } } } Column(modifier = [Link]().padding([Link])) {
Text("Trial Logger", fontSize = [Link], fontWeight = [Link], color = Color(0xFF1565C0))
Spacer([Link]([Link])) // Event selector ExposedDropdownMenuBox(expanded = expanded,
onExpandedChange = { expanded = !expanded }) { OutlinedTextField(value = selectedEvent, onValueChange = {},
readOnly = true, label = { Text("Event Type") }, trailingIcon = {
[Link](expanded) }, modifier = [Link]().fillMaxWidth(),
shape = RoundedCornerShape([Link])) ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded
= false }) { [Link] { e -> DropdownMenuItem(text = { Text(e) }, onClick = { selectedEvent = e;
expanded = false }) } } } Spacer([Link]([Link])) // Stopwatch display Card(modifier =
[Link](), shape = RoundedCornerShape([Link]), colors =
[Link](Color(0xFF1565C0))) { Column(modifier = [Link]([Link]),
horizontalAlignment = [Link]) { Text("■ Stopwatch", color = [Link], fontSize =
[Link]) val ms = [Link] Text("%02d:%02d.%02d".format(ms/60000,(ms%60000)/1000,(ms%1000)/10),
fontSize = [Link], fontWeight = [Link], color = [Link]) Row(horizontalArrangement =
[Link]([Link])) { Button(onClick = { stopwatch = [Link](isRunning =
![Link]) }, colors = [Link]( containerColor = if ([Link])
Color(0xFFE53935) else Color(0xFF43A047))) { Text(if ([Link]) "Stop" else "Start") }
OutlinedButton(onClick = { stopwatch = StopwatchState() val time = "%02d:%02d.%02d".format(ms/60000,
(ms%60000)/1000,(ms%1000)/10) trialLog = trialLog + "$selectedEvent — $time sec" }, colors =
[Link]( contentColor = [Link])) { Text("Log") } } } }
Spacer([Link]([Link])) // Distance Logger OutlinedTextField(value = distance, onValueChange = {
distance = it }, label = { Text("Distance / Score (metres)") }, leadingIcon = {
Icon([Link], null) }, trailingIcon = { IconButton(onClick = { if ([Link]())
{ trialLog = trialLog + "$selectedEvent — ${distance}m" distance = "" } }) { Icon([Link], null)
} }, modifier = [Link](), shape = RoundedCornerShape([Link])) Spacer([Link]([Link]))
Text("Trial Log", fontWeight = [Link], fontSize = [Link]) LazyColumn(verticalArrangement =
[Link]([Link])) { items([Link]()) { entry -> Card(modifier =
[Link](), shape = RoundedCornerShape([Link])) { Text("• $entry", modifier =
[Link]([Link]), fontSize = [Link]) } } } } }
10. Milestone Badges Screen
[Link]
package [Link] import [Link].* import
[Link].* import [Link] import
[Link].material3.* import [Link].* import [Link].* import
[Link] import [Link] import
[Link] import [Link].* data class BadgeItem(val emoji:
String, val title: String, val desc: String, val isEarned: Boolean) @Composable fun BadgesScreen() { val
badges = listOf( BadgeItem("■","District Level Ready", "Sprint < 12s or Jump > 5m", true),
BadgeItem("■","Speed Demon", "100m in under 11 seconds", false), BadgeItem("■","High Flyer", "High Jump
above 1.5m", true), BadgeItem("■","Iron Will", "Log 30 consecutive trial days", false),
BadgeItem("■","School Champion", "Rank #1 on leaderboard", false), BadgeItem("■","All-Rounder", "Compete
in 3+ different events", true), ) Column(modifier = [Link]().padding([Link])) {
Text("Milestone Badges", fontSize = [Link], fontWeight = [Link], color = Color(0xFF1565C0))
Text("Earn badges by hitting performance benchmarks", fontSize = [Link], color = [Link])
Spacer([Link]([Link])) LazyVerticalGrid(columns = [Link](2), horizontalArrangement =
[Link]([Link]), verticalArrangement = [Link]([Link])) { items(badges) { badge ->
BadgeCard(badge) } } } } @Composable fun BadgeCard(badge: BadgeItem) { Card(modifier =
[Link]().aspectRatio(1f), shape = RoundedCornerShape([Link]), colors =
[Link]( containerColor = if ([Link]) Color(0xFFFFF8E1) else Color(0xFFF5F5F5))) {
Column(modifier = [Link]().padding([Link]), horizontalAlignment =
[Link], verticalArrangement = [Link]) { Text([Link], fontSize =
[Link]) Spacer([Link]([Link])) Text([Link], fontWeight = [Link], fontSize = [Link],
textAlign = [Link], color = if ([Link]) Color(0xFFE65100) else [Link])
Text([Link], fontSize = [Link], textAlign = [Link], color = [Link]) if ([Link]) {
Spacer([Link]([Link])) Surface(shape = RoundedCornerShape(50), color = Color(0xFF43A047)) { Text("✓
Earned", modifier = [Link]([Link], [Link]), fontSize = [Link], color = [Link]) } } } } }
11. Leaderboard Screen
[Link]
package [Link] import [Link].* import
[Link] import [Link] import
[Link].* import [Link].material3.* import [Link].*
import [Link].* import [Link] import
[Link] import [Link] import
[Link].* @Composable fun LeaderboardScreen() { // Sample data — replace with Firestore
query val entries = listOf( Triple("Arjun Singh", "Athletics", 1240), Triple("Priya Rao", "Kabaddi", 1180),
Triple("Vikram Das", "Athletics", 1050), Triple("Sneha Patel", "Football", 980), Triple("Rohit Kumar",
"Basketball", 920), ) val medals = listOf("■","■","■") Column(modifier =
[Link]().padding([Link])) { Text("■ Leaderboard", fontSize = [Link], fontWeight =
[Link], color = Color(0xFF1565C0)) Text("School Rankings", fontSize = [Link], color = [Link])
Spacer([Link]([Link])) LazyColumn(verticalArrangement = [Link]([Link])) {
itemsIndexed(entries) { idx, (name, sport, pts) -> val isTop3 = idx < 3 Card(modifier =
[Link](), shape = RoundedCornerShape([Link]), colors = [Link](
containerColor = when(idx) { 0 -> Color(0xFFFFF8E1) 1 -> Color(0xFFF5F5F5) 2 -> Color(0xFFFBE9E7) else ->
[Link] })) { Row(modifier = [Link]([Link]), verticalAlignment = [Link])
{ // Rank Box(modifier = [Link]([Link]) .clip(CircleShape) .run { if (isTop3) this else this },
contentAlignment = [Link]) { Text(if (isTop3) medals[idx] else "#${idx+1}", fontSize = if
(isTop3) [Link] else [Link], fontWeight = [Link]) } Spacer([Link]([Link])) // Avatar initial
Box(modifier = [Link]([Link]) .clip(CircleShape) .run { background(Color(0xFF1565C0)) },
contentAlignment = [Link]) { Text([Link]().toString(), color = [Link], fontWeight =
[Link], fontSize = [Link]) } Spacer([Link]([Link])) Column([Link](1f)) {
Text(name, fontWeight = [Link], fontSize = [Link]) Text(sport, fontSize = [Link], color =
[Link]) } Text("$pts pts", fontWeight = [Link], color = Color(0xFFE65100), fontSize = [Link]) }
} } } } }
12. Navigation Graph
[Link]
package [Link] import [Link] import
[Link] import [Link].* import
[Link].material3.* import [Link].* import [Link] import
[Link] import
[Link] import [Link].* import
[Link] import [Link] import
[Link] import [Link] import
[Link] import
[Link] import [Link]
data class BottomNavItem(val route: String, val label: String, val icon: ImageVector) @Composable fun
AppNavigation() { val navController = rememberNavController() val bottomItems = listOf(
BottomNavItem("home", "Home", [Link]), BottomNavItem("trial", "Trials", [Link]),
BottomNavItem("badges", "Badges", [Link]), BottomNavItem("leaderboard", "Ranks",
[Link]), ) val noBottomBarRoutes = setOf("splash", "login", "register") val
currentBackStack by [Link]() val currentRoute =
currentBackStack?.destination?.route val showBottomBar = currentRoute !in noBottomBarRoutes Scaffold(
bottomBar = { if (showBottomBar) { NavigationBar { [Link] { item -> val selected =
currentBackStack?.destination ?.hierarchy?.any { [Link] == [Link] } == true NavigationBarItem(
selected = selected, onClick = { [Link]([Link]) {
popUpTo([Link]) launchSingleTop = true } }, icon = { Icon([Link],
[Link]) }, label = { Text([Link]) } ) } } } } ) { padding -> NavHost(navController = navController,
startDestination = "splash", modifier = [Link](padding)) { composable("splash") {
SplashScreen(navController) } composable("login") { LoginScreen(navController) } composable("register") {
RegisterScreen(navController) } composable("home") { HomeScreen(navController) } composable("trial") {
TrialLoggerScreen() } composable("badges") { BadgesScreen() } composable("leaderboard") {
LeaderboardScreen() } } } }
13. Firebase Auth Repository
[Link]
package [Link] import [Link] import
[Link] import [Link] import
[Link] class AuthRepository { private val auth = [Link]()
private val db = [Link]() val currentUser get() = [Link] suspend fun
getCurrentAthlete(): Athlete? { val uid = [Link]?.uid ?: return null val doc =
[Link]("athletes").document(uid).get().await() return [Link](Athlete::[Link]) } suspend
fun updateProfile(athlete: Athlete) { val uid = [Link]?.uid ?: return
[Link]("athletes").document(uid) .set(athlete).await() } fun signOut() = [Link]() }
14. Room DB Setup
[Link]
package [Link] import [Link].* import
[Link] import [Link] @Dao interface AthleteDao {
@Query("SELECT * FROM athletes") fun getAllAthletes(): Flow> @Insert(onConflict =
[Link]) suspend fun insertAthlete(athlete: Athlete) @Query("SELECT * FROM athletes
WHERE id = :id") suspend fun getAthleteById(id: String): Athlete? @Delete suspend fun
deleteAthlete(athlete: Athlete) } @Database(entities = [Athlete::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() { abstract fun athleteDao(): AthleteDao companion object {
@Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: [Link]):
AppDatabase { return INSTANCE ?: synchronized(this) { [Link](context,
AppDatabase::[Link], "athlete_tracker_db") .fallbackToDestructiveMigration() .build().also { INSTANCE
= it } } } } }
15. UI Color Theme & Typography
[Link]
package [Link] import [Link].material3.* import
[Link] import [Link] // Brand Colors val Blue800
= Color(0xFF1565C0) val Blue600 = Color(0xFF1E88E5) val Orange700 = Color(0xFFE65100) val Orange500 =
Color(0xFFFF6D00) val GreenBadge = Color(0xFF43A047) val BackgroundLight = Color(0xFFF8F9FA) private val
AppColorScheme = lightColorScheme( primary = Blue800, secondary = Orange700, tertiary = GreenBadge,
background = BackgroundLight, surface = [Link], onPrimary = [Link], onSecondary = [Link],
onBackground = Color(0xFF1C1B1F), onSurface = Color(0xFF1C1B1F), ) @Composable fun
AthleteTrackerTheme(content: @Composable () -> Unit) { MaterialTheme( colorScheme = AppColorScheme,
typography = Typography(), content = content ) }
16. Drawable Resources Guide
Since actual image assets cannot be bundled in this PDF, here is a complete guide to create or source all required
images for the app:
File Name Type Size Description / Where to Get
ic_launcher.png Launcher Icon 512×512 Use Android Studio → File → New → Image Asset (choose ■ or sports icon)
ic_athlete_placeholder.xml Vector Drawable Any Use Material Icons: person_outline — paste as VectorDrawable XML
ic_badge_district.xml Vector Drawable 48dp Use star / award icon from [Link]/icons
ic_badge_speed.xml Vector Drawable 48dp Use bolt icon from [Link]/icons
ic_stopwatch.xml Vector Drawable 24dp Use timer icon from [Link]/icons
bg_gradient.xml Gradient Drawable — Create XML gradient: #1565C0 → #0D47A1 (see code below)
ic_kabaddi.xml Vector Drawable 24dp Use sports_martial_arts from Material Icons
ic_athletics.xml Vector Drawable 24dp Use directions_run from Material Icons
bg_gradient.xml (place in res/drawable/)
Free Icon Sources:
• [Link]/icons — Official Material Icons (download as XML VectorDrawable)
• [Link]/icons — Search and download any icon as Android XML
• Android Studio → File → New → Vector Asset — built-in icon picker with 1000+ icons
• [Link] — PNG icons for custom badge images (license: free with attribution)
17. [Link]
[Link]
package [Link] import [Link] import [Link] import
[Link] import [Link] import
[Link] class MainActivity : ComponentActivity() { override
fun onCreate(savedInstanceState: Bundle?) { [Link](savedInstanceState) setContent {
AthleteTrackerTheme { AppNavigation() } } } }
Quick Start — Build Steps
1. Create Android Project
In Android Studio, select 'Empty Activity' → Kotlin → Jetpack Compose. Set package name: [Link]
2. Add Dependencies
Replace your app/[Link] with Section 1 code. Sync Gradle.
3. Firebase Setup
Follow Section 2 steps. Download and place [Link] in /app folder.
4. Create Package Structure
Create packages: ui/auth, ui/home, ui/trial, ui/badges, ui/leaderboard, ui/profile, ui/splash, data/model, data/local,
data/repository, navigation
5. Copy Source Files
Copy each Kotlin file from this PDF into the correct package folder.
6. Add Resources
Create drawable XML files from Section 16. Use Android Studio vector asset tool for icons.
7. Update Manifest
Replace [Link] content with Section 17.
8. Run the App
Connect an Android device or start an emulator (API 26+). Click Run ■
AthleteTracker Pro — Complete Android Source Code Guide | Built with Kotlin + Jetpack Compose + Firebase