Skip to content

Instantly share code, notes, and snippets.

@BlakeBarrett
Created August 21, 2023 00:42
Show Gist options
  • Save BlakeBarrett/58ce9d62e510ee726b035143e4941d7e to your computer and use it in GitHub Desktop.
Save BlakeBarrett/58ce9d62e510ee726b035143e4941d7e to your computer and use it in GitHub Desktop.
JSON USER DATA EXAMPLE: Fake Users JSON array generated by ChatGPT
[
{
"id": 1,
"first_name": "Alice",
"last_name": "Smith",
"email": "alice@example.com",
"age": 28,
"last_login_date": "2023-08-09T10:30:00",
"friends_user_ids": [2, 3, 5, 7],
"favorite_animal": "Dog",
"least_favorite_smell": "Durian",
"hobbies": ["Painting", "Hiking"],
"birthplace": "Cityville",
"favorite_color": "Purple",
"unread_messages": 3,
"has_pets": true,
"shoe_size": 7.5,
"coffee_preference": "Black",
"dream_destination": "Bora Bora",
"music_genre": "Indie Rock",
"is_vegan": false,
"fitness_level": "Intermediate",
"lucky_number": 17
},
{
"id": 2,
"first_name": "Bob",
"last_name": "Johnson",
"email": "bob@example.com",
"age": 35,
"last_login_date": "2023-08-08T15:20:00",
"friends_user_ids": [1, 4, 8],
"favorite_animal": "Cat",
"least_favorite_smell": "Sulfur",
"hobbies": ["Reading", "Gardening"],
"birthplace": "Villageville",
"favorite_color": "Blue",
"unread_messages": 0,
"has_pets": true,
"shoe_size": 9.0,
"coffee_preference": "Latte",
"dream_destination": "Paris",
"music_genre": "Classical",
"is_vegan": true,
"fitness_level": "Beginner",
"lucky_number": 5
},
{
"id": 3,
"first_name": "Charlie",
"last_name": "Brown",
"email": "charlie@example.com",
"age": 22,
"last_login_date": "2023-08-09T08:45:00",
"friends_user_ids": [1, 5, 9],
"favorite_animal": "Elephant",
"least_favorite_smell": "Fish",
"hobbies": ["Cooking", "Playing Guitar"],
"birthplace": "Townsville",
"favorite_color": "Green",
"unread_messages": 10,
"has_pets": false,
"shoe_size": 8.5,
"coffee_preference": "Cappuccino",
"dream_destination": "Tokyo",
"music_genre": "Pop",
"is_vegan": false,
"fitness_level": "Advanced",
"lucky_number": 12
},
{
"id": 4,
"first_name": "David",
"last_name": "Lee",
"email": "david@example.com",
"age": 31,
"last_login_date": "2023-08-07T18:10:00",
"friends_user_ids": [2, 8, 10],
"favorite_animal": "Lion",
"least_favorite_smell": "Garlic",
"hobbies": ["Soccer", "Photography"],
"birthplace": "Countryville",
"favorite_color": "Yellow",
"unread_messages": 5,
"has_pets": true,
"shoe_size": 10.5,
"coffee_preference": "Espresso",
"dream_destination": "New York",
"music_genre": "Rock",
"is_vegan": false,
"fitness_level": "Intermediate",
"lucky_number": 9
},
{
"id": 5,
"first_name": "Eve",
"last_name": "Williams",
"email": "eve@example.com",
"age": 27,
"last_login_date": "2023-08-09T12:05:00",
"friends_user_ids": [1, 3, 9, 11],
"favorite_animal": "Dolphin",
"least_favorite_smell": "Onion",
"hobbies": ["Yoga", "Traveling"],
"birthplace": "Seaville",
"favorite_color": "Turquoise",
"unread_messages": 8,
"has_pets": true,
"shoe_size": 8.0,
"coffee_preference": "Mocha",
"dream_destination": "Maldives",
"music_genre": "Electronic",
"is_vegan": true,
"fitness_level": "Advanced",
"lucky_number": 3
},
{
"id": 6,
"first_name": "Frank",
"last_name": "Davis",
"email": "frank@example.com",
"age": 45,
"last_login_date": "2023-08-08T09:40:00",
"friends_user_ids": [12, 13, 14],
"favorite_animal": "Giraffe",
"least_favorite_smell": "Vinegar",
"hobbies": ["Golf", "Painting"],
"birthplace": "Mountainville",
"favorite_color": "Orange",
"unread_messages": 2,
"has_pets": false,
"shoe_size": 11.0,
"coffee_preference": "Americano",
"dream_destination": "Switzerland",
"music_genre": "Jazz",
"is_vegan": false,
"fitness_level": "Beginner",
"lucky_number": 8
},
{
"id": 7,
"first_name": "Grace",
"last_name": "Martinez",
"email": "grace@example.com",
"age": 29,
"last_login_date": "2023-08-09T16:15:00",
"friends_user_ids": [1, 15, 16],
"favorite_animal": "Penguin",
"least_favorite_smell": "Cabbage",
"hobbies": ["Singing", "Cooking"],
"birthplace": "Snowville",
"favorite_color": "White",
"unread_messages": 1,
"has_pets": true,
"shoe_size": 6.5,
"coffee_preference": "Latte",
"dream_destination": "Hawaii",
"music_genre": "Reggae",
"is_vegan": true,
"fitness_level": "Intermediate",
"lucky_number": 13
},
{
"id": 8,
"first_name": "Henry",
"last_name": "Taylor",
"email": "henry@example.com",
"age": 32,
"last_login_date": "2023-08-08T13:30:00",
"friends_user_ids": [2, 4, 9, 17],
"favorite_animal": "Tiger",
"least_favorite_smell": "Mold",
"hobbies": ["Running", "Chess"],
"birthplace": "Desertville",
"favorite_color": "Red",
"unread_messages": 12,
"has_pets": true,
"shoe_size": 10.0,
"coffee_preference": "Cappuccino",
"dream_destination": "Australia",
"music_genre": "Country",
"is_vegan": false,
"fitness_level": "Advanced",
"lucky_number": 21
},
{
"id": 9,
"first_name": "Ivy",
"last_name": "Anderson",
"email": "ivy@example.com",
"age": 24,
"last_login_date": "2023-08-09T07:55:00",
"friends_user_ids": [3, 5, 8, 18],
"favorite_animal": "Kangaroo",
"least_favorite_smell": "Smoked Fish",
"hobbies": ["Dancing", "Biking"],
"birthplace": "Jungleville",
"favorite_color": "Yellow",
"unread_messages": 6,
"has_pets": true,
"shoe_size": 8.0,
"coffee_preference": "Espresso",
"dream_destination": "Brazil",
"music_genre": "Hip Hop",
"is_vegan": true,
"fitness_level": "Beginner",
"lucky_number": 6
},
{
"id": 10,
"first_name": "Jack",
"last_name": "Moore",
"email": "jack@example.com",
"age": 30,
"last_login_date": "2023-08-07T21:20:00",
"friends_user_ids": [4, 7, 9, 19],
"favorite_animal": "Horse",
"least_favorite_smell": "Burnt Rubber",
"hobbies": ["Photography", "Travelling"],
"birthplace": "Farmville",
"favorite_color": "Brown",
"unread_messages": 9,
"has_pets": true,
"shoe_size": 10.5,
"coffee_preference": "Black",
"dream_destination": "New Zealand",
"music_genre": "Alternative",
"is_vegan": false,
"fitness_level": "Intermediate",
"lucky_number": 11
},
{
"id": 11,
"first_name": "Karen",
"last_name": "Jackson",
"email": "karen@example.com",
"age": 26,
"last_login_date": "2023-08-09T14:50:00",
"friends_user_ids": [5, 10, 20],
"favorite_animal": "Panda",
"least_favorite_smell": "Rotting Fruit",
"hobbies": ["Yoga", "Reading"],
"birthplace": "Cityville",
"favorite_color": "Pink",
"unread_messages": 4,
"has_pets": false,
"shoe_size": 7.0,
"coffee_preference": "Mocha",
"dream_destination": "Thailand",
"music_genre": "R&B",
"is_vegan": true,
"fitness_level": "Advanced",
"lucky_number": 15
},
{
"id": 12,
"first_name": "Liam",
"last_name": "Garcia",
"email": "liam@example.com",
"age": 21,
"last_login_date": "2023-08-08T08:00:00",
"friends_user_ids": [6, 13, 15],
"favorite_animal": "Sloth",
"least_favorite_smell": "Dirty Socks",
"hobbies": ["Gaming", "Cooking"],
"birthplace": "Villageville",
"favorite_color": "Black",
"unread_messages": 7,
"has_pets": true,
"shoe_size": 9.5,
"coffee_preference": "Latte",
"dream_destination": "Greece",
"music_genre": "Metal",
"is_vegan": false,
"fitness_level": "Beginner",
"lucky_number": 14
},
{
"id": 13,
"first_name": "Mia",
"last_name": "Rodriguez",
"email": "mia@example.com",
"age": 33,
"last_login_date": "2023-08-09T11:40:00",
"friends_user_ids": [6, 12, 14, 16],
"favorite_animal": "Owl",
"least_favorite_smell": "Stale Beer",
"hobbies": ["Singing", "Painting"],
"birthplace": "Countryville",
"favorite_color": "Blue",
"unread_messages": 5,
"has_pets": true,
"shoe_size": 8.5,
"coffee_preference": "Cappuccino",
"dream_destination": "Italy",
"music_genre": "Pop",
"is_vegan": true,
"fitness_level": "Intermediate",
"lucky_number": 8
},
{
"id": 14,
"first_name": "Noah",
"last_name": "Martinez",
"email": "noah@example.com",
"age": 40,
"last_login_date": "2023-08-08T17:55:00",
"friends_user_ids": [6, 13, 15],
"favorite_animal": "Wolf",
"least_favorite_smell": "Mothballs",
"hobbies": ["Running", "Cooking"],
"birthplace": "Cityville",
"favorite_color": "Green",
"unread_messages": 2,
"has_pets": false,
"shoe_size": 10.0,
"coffee_preference": "Espresso",
"dream_destination": "Australia",
"music_genre": "Rock",
"is_vegan": false,
"fitness_level": "Advanced",
"lucky_number": 19
},
{
"id": 15,
"first_name": "Olivia",
"last_name": "Thompson",
"email": "olivia@example.com",
"age": 29,
"last_login_date": "2023-08-09T10:15:00",
"friends_user_ids": [7, 13, 14, 17],
"favorite_animal": "Duck",
"least_favorite_smell": "Wet Dog",
"hobbies": ["Soccer", "Gardening"],
"birthplace": "Townsville",
"favorite_color": "Red",
"unread_messages": 1,
"has_pets": true,
"shoe_size": 7.0,
"coffee_preference": "Black",
"dream_destination": "Japan",
"music_genre": "Electronic",
"is_vegan": true,
"fitness_level": "Intermediate",
"lucky_number": 10
},
{
"id": 16,
"first_name": "Peter",
"last_name": "Wright",
"email": "peter@example.com",
"age": 37,
"last_login_date": "2023-08-08T12:30:00",
"friends_user_ids": [6, 15, 18],
"favorite_animal": "Bear",
"least_favorite_smell": "Gasoline",
"hobbies": ["Reading", "Biking"],
"birthplace": "Seaville",
"favorite_color": "Blue",
"unread_messages": 0,
"has_pets": false,
"shoe_size": 9.5,
"coffee_preference": "Mocha",
"dream_destination": "New Zealand",
"music_genre": "Indie Rock",
"is_vegan": false,
"fitness_level": "Advanced",
"lucky_number": 7
},
{
"id": 17,
"first_name": "Quinn",
"last_name": "Hernandez",
"email": "quinn@example.com",
"age": 25,
"last_login_date": "2023-08-09T09:05:00",
"friends_user_ids": [8, 16, 19],
"favorite_animal": "Fox",
"least_favorite_smell": "Wet Carpet",
"hobbies": ["Painting", "Singing"],
"birthplace": "Mountainville",
"favorite_color": "Orange",
"unread_messages": 4,
"has_pets": true,
"shoe_size": 8.0,
"coffee_preference": "Latte",
"dream_destination": "Greece",
"music_genre": "Pop",
"is_vegan": true,
"fitness_level": "Beginner",
"lucky_number": 22
},
{
"id": 18,
"first_name": "Ryan",
"last_name": "Adams",
"email": "ryan@example.com",
"age": 32,
"last_login_date": "2023-08-08T14:25:00",
"friends_user_ids": [9, 15, 20],
"favorite_animal": "Gorilla",
"least_favorite_smell": "Burnt Popcorn",
"hobbies": ["Gaming", "Hiking"],
"birthplace": "Villageville",
"favorite_color": "Green",
"unread_messages": 6,
"has_pets": true,
"shoe_size": 10.5,
"coffee_preference": "Black",
"dream_destination": "Hawaii",
"music_genre": "Electronic",
"is_vegan": false,
"fitness_level": "Intermediate",
"lucky_number": 18
},
{
"id": 19,
"first_name": "Sophia",
"last_name": "Gonzalez",
"email": "sophia@example.com",
"age": 28,
"last_login_date": "2023-08-09T13:00:00",
"friends_user_ids": [10, 17, 20],
"favorite_animal": "Llama",
"least_favorite_smell": "Public Restrooms",
"hobbies": ["Reading", "Dancing"],
"birthplace": "Cityville",
"favorite_color": "Purple",
"unread_messages": 2,
"has_pets": true,
"shoe_size": 8.0,
"coffee_preference": "Latte",
"dream_destination": "Paris",
"music_genre": "Pop",
"is_vegan": true,
"fitness_level": "Advanced",
"lucky_number": 4
},
{
"id": 20,
"first_name": "Thomas",
"last_name": "Walker",
"email": "thomas@example.com",
"age": 30,
"last_login_date": "2023-08-08T19:45:00",
"friends_user_ids": [11, 19],
"favorite_animal": "Monkey",
"least_favorite_smell": "Sewage",
"hobbies": ["Cooking", "Traveling"],
"birthplace": "Townsville",
"favorite_color": "Blue",
"unread_messages": 7,
"has_pets": false,
"shoe_size": 9.5,
"coffee_preference": "Cappuccino",
"dream_destination": "Maldives",
"music_genre": "Rock",
"is_vegan": false,
"fitness_level": "Intermediate",
"lucky_number": 16
}
]
@BlakeBarrett
Copy link
Author

BlakeBarrett commented Aug 25, 2023

UserProfile.kt

package com.blakebarrett.myapplication

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.blakebarrett.myapplication.ui.theme.DIALTheme
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Preview(showBackground = true)
@Composable
fun UserProfileScreenPreview() {
    DIALTheme {
        InitAppWithNavigation()
    }
}

@Composable
fun UserProfileApp() {
    DIALTheme {
        InitAppWithNavigation()
    }
}

object Routes {
    const val FRIENDS_LIST = "friendsList"
    private const val PROFILE = "profile"
    fun profile(userId: Int) = "$PROFILE/$userId"
}

@Composable
fun InitAppWithNavigation() {
    val navController = rememberNavController()
    NavHost(
        navController = navController,
        startDestination = Routes.FRIENDS_LIST
    ) {
        composable(
            "profile/{userId}",
            arguments = listOf(navArgument("userId") { type = NavType.IntType })
        ) { backStackEntry ->
            val userId = backStackEntry.arguments?.getInt("userId")
            userId?.let {
                Scaffold(
                    bottomBar = {
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(56.dp)
                                .background(Color.Magenta),
                            verticalAlignment = Alignment.CenterVertically,
                            horizontalArrangement = Arrangement.SpaceEvenly
                        ) {
                            Text(
                                text = "All Users",
                                modifier = Modifier
                                    .weight(1f)
                                    .clickable {
                                        navController.navigate(Routes.FRIENDS_LIST)
                                    }
                                    .padding(16.dp),
                                fontStyle = MaterialTheme.typography.bodyMedium.fontStyle
                            )
                            Text(
                                text = "Back",
                                modifier = Modifier
                                    .weight(1f)
                                    .clickable {
                                        navController.popBackStack()
                                    }
                                    .padding(16.dp),
                                fontStyle = MaterialTheme.typography.bodyMedium.fontStyle
                            )
                        }
                    }
                ) { padding ->
                    UserProfileScreen(
                        modifier = Modifier.padding(padding),
                        userId = userId,
                        navigateToUserId = { friendId ->
                            navController.navigate(Routes.profile(friendId))
                        }
                    )
                }
            }
        }
        composable(Routes.FRIENDS_LIST) {
            LazyColumn (
                modifier = Modifier
                    .fillMaxSize()
                    .padding(16.dp)
            ) {
                val allUsers = User.parse(data)
                items(allUsers.count()) {index ->
                    val user = allUsers[index]
                    Box(
                        modifier = Modifier
                            .fillMaxWidth()
                            .background(Color.LightGray, MaterialTheme.shapes.medium)
                            .clickable {
                                navController.navigate(Routes.profile(user.id))
                            }
                            .padding(16.dp)
                    ) {
                        Text("${user.first_name}  ${user.last_name}")
                    }
                }
            }
        }
    }
}

@Composable
fun UserProfileScreen(modifier: Modifier = Modifier,
                      userId: Int,
                      navigateToUserId: (userId: Int) -> Unit = { _ -> }) {
    val model = viewModel<UserViewModelLocator>()
    model.updateUiStateWithUserViewModel(userId)
    model.uiState.collectAsStateWithLifecycle().let { user ->
        Column(
            modifier = modifier
        ) {
            Text(
                text = "User Profile",
                style = MaterialTheme.typography.headlineSmall
            )
            Spacer(modifier = Modifier.height(16.dp))
            user.value?.let {
                UserDetailCard(it, navigateToUserId)
            }
            Spacer(modifier = Modifier.height(16.dp))
        }
    } ?: run {
        Text(
            text = "User not found",
            style = MaterialTheme.typography.headlineMedium
        )
    }
}

@Composable
fun UserDetailCard(
    user: UserViewModelUiState,
    friendClick: (Int) -> Unit = { _ -> }
) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        elevation = CardDefaults.cardElevation(
            defaultElevation = 10.dp
        )
    ) {
        Column(
            modifier = Modifier.padding(16.dp)
        ) {
            Text(
                text = "${user.first_name} ${user.last_name}",
                style = MaterialTheme.typography.headlineMedium
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                text = "Age: ${user.age}",
                style = MaterialTheme.typography.bodyMedium
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                text = "Email: ${user.email}",
                style = MaterialTheme.typography.bodySmall
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                text = "Favorite Animal: ${user.favorite_animal}",
                style = MaterialTheme.typography.bodySmall
            )
            Spacer(modifier = Modifier.height(8.dp))
            UserFriendsList(
                user.friends_user_ids,
                onFriendClicked = friendClick
            )
            Spacer(modifier = Modifier.height(8.dp))
            UserHobbies(user.hobbies)
        }
    }
}

@Composable
fun UserHobbies(hobbies: List<String>) {
    Column {
        Text(
            text = "Hobbies",
            style = MaterialTheme.typography.labelSmall
        )
        Spacer(modifier = Modifier.height(8.dp))
        Column {
            hobbies.forEach { hobby ->
                Text(
                    text = "- $hobby",
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

@Composable
fun UserFriendsList(
    friends_user_ids: List<Int>,
    onFriendClicked: (Int) -> Unit = {}
) {
    Column {
        Text(
            text = "Friends",
            style = MaterialTheme.typography.labelSmall
        )
        Spacer(modifier = Modifier.height(8.dp))
        Column {
            friends_user_ids.forEach { friendId ->
                viewModel<UserViewModelLocator>()
                    .getUserById(friendId)?.let { user ->
                    Text(
                        text = "- ${user.first_name} ${user.last_name}",
                        style = MaterialTheme.typography.bodyMedium,
                        modifier = Modifier.clickable {
                            onFriendClicked(friendId)
                        }
                    )
                }
            }
        }
    }
}

data class UserViewModelUiState(
    val first_name: String,
    val last_name: String,
    val age: Int,
    val email: String,
    val favorite_animal: String,
    val hobbies: List<String>,
    val friends_user_ids: List<Int>
)

class UserViewModelLocator : ViewModel() {
    private val _allUsers: List<User> = User.parse(data)
    private val _uiState = MutableStateFlow<UserViewModelUiState?>(null)
    val uiState: StateFlow<UserViewModelUiState?> = _uiState.asStateFlow()

    fun getUserById(userId: Int): UserViewModelUiState? {
        return _allUsers.find { it.id == userId }?.let { user ->
            UserViewModelUiState(
                first_name = user.first_name,
                last_name = user.last_name,
                age = user.age,
                email = user.email,
                favorite_animal = user.favorite_animal,
                hobbies = user.hobbies,
                friends_user_ids = user.friends_user_ids
            )
        }
    }

    fun updateUiStateWithUserViewModel(userId: Int) {
        getUserById(userId)?.let { user ->
            _uiState.update {
                it?.copy(
                    first_name = user.first_name,
                    last_name = user.last_name,
                    age = user.age,
                    email = user.email,
                    favorite_animal = user.favorite_animal,
                    hobbies = user.hobbies,
                    friends_user_ids = user.friends_user_ids
                ) ?: run {
                    UserViewModelUiState(
                        first_name = user.first_name,
                        last_name = user.last_name,
                        age = user.age,
                        email = user.email,
                        favorite_animal = user.favorite_animal,
                        hobbies = user.hobbies,
                        friends_user_ids = user.friends_user_ids
                    )
                }
            }
        }
    }
}

@Serializable
data class User(
    val id: Int,
    val first_name: String,
    val last_name: String,
    val email: String,
    val age: Int,
    val last_login_date: String,
    val friends_user_ids: List<Int>,
    val favorite_animal: String,
    val least_favorite_smell: String,
    val hobbies: List<String>,
    val birthplace: String,
    val favorite_color: String,
    val unread_messages: Int,
    val has_pets: Boolean,
    val shoe_size: Double,
    val coffee_preference: String,
    val dream_destination: String,
    val music_genre: String,
    val is_vegan: Boolean,
    val fitness_level: String,
    val lucky_number: Int
) {
    companion object {
        fun parse(source: String): List<User> {
            return Json.decodeFromString(source)
        }
    }
}

const val data = """
[
    {
        "id": 1,
        "first_name": "Alice",
        "last_name": "Smith",
        "email": "alice@example.com",
        "age": 28,
        "last_login_date": "2023-08-09T10:30:00",
        "friends_user_ids": [2, 3, 5, 7],
        "favorite_animal": "Dog",
        "least_favorite_smell": "Durian",
        "hobbies": ["Painting", "Hiking"],
        "birthplace": "Cityville",
        "favorite_color": "Purple",
        "unread_messages": 3,
        "has_pets": true,
        "shoe_size": 7.5,
        "coffee_preference": "Black",
        "dream_destination": "Bora Bora",
        "music_genre": "Indie Rock",
        "is_vegan": false,
        "fitness_level": "Intermediate",
        "lucky_number": 17
    },
    {
        "id": 2,
        "first_name": "Bob",
        "last_name": "Johnson",
        "email": "bob@example.com",
        "age": 35,
        "last_login_date": "2023-08-08T15:20:00",
        "friends_user_ids": [1, 4, 8],
        "favorite_animal": "Cat",
        "least_favorite_smell": "Sulfur",
        "hobbies": ["Reading", "Gardening"],
        "birthplace": "Villageville",
        "favorite_color": "Blue",
        "unread_messages": 0,
        "has_pets": true,
        "shoe_size": 9.0,
        "coffee_preference": "Latte",
        "dream_destination": "Paris",
        "music_genre": "Classical",
        "is_vegan": true,
        "fitness_level": "Beginner",
        "lucky_number": 5
    },
    {
        "id": 3,
        "first_name": "Charlie",
        "last_name": "Brown",
        "email": "charlie@example.com",
        "age": 22,
        "last_login_date": "2023-08-09T08:45:00",
        "friends_user_ids": [1, 5, 9],
        "favorite_animal": "Elephant",
        "least_favorite_smell": "Fish",
        "hobbies": ["Cooking", "Playing Guitar"],
        "birthplace": "Townsville",
        "favorite_color": "Green",
        "unread_messages": 10,
        "has_pets": false,
        "shoe_size": 8.5,
        "coffee_preference": "Cappuccino",
        "dream_destination": "Tokyo",
        "music_genre": "Pop",
        "is_vegan": false,
        "fitness_level": "Advanced",
        "lucky_number": 12
    },
    {
        "id": 4,
        "first_name": "David",
        "last_name": "Lee",
        "email": "david@example.com",
        "age": 31,
        "last_login_date": "2023-08-07T18:10:00",
        "friends_user_ids": [2, 8, 10],
        "favorite_animal": "Lion",
        "least_favorite_smell": "Garlic",
        "hobbies": ["Soccer", "Photography"],
        "birthplace": "Countryville",
        "favorite_color": "Yellow",
        "unread_messages": 5,
        "has_pets": true,
        "shoe_size": 10.5,
        "coffee_preference": "Espresso",
        "dream_destination": "New York",
        "music_genre": "Rock",
        "is_vegan": false,
        "fitness_level": "Intermediate",
        "lucky_number": 9
    },
    {
        "id": 5,
        "first_name": "Eve",
        "last_name": "Williams",
        "email": "eve@example.com",
        "age": 27,
        "last_login_date": "2023-08-09T12:05:00",
        "friends_user_ids": [1, 3, 9, 11],
        "favorite_animal": "Dolphin",
        "least_favorite_smell": "Onion",
        "hobbies": ["Yoga", "Traveling"],
        "birthplace": "Seaville",
        "favorite_color": "Turquoise",
        "unread_messages": 8,
        "has_pets": true,
        "shoe_size": 8.0,
        "coffee_preference": "Mocha",
        "dream_destination": "Maldives",
        "music_genre": "Electronic",
        "is_vegan": true,
        "fitness_level": "Advanced",
        "lucky_number": 3
    },
    {
        "id": 6,
        "first_name": "Frank",
        "last_name": "Davis",
        "email": "frank@example.com",
        "age": 45,
        "last_login_date": "2023-08-08T09:40:00",
        "friends_user_ids": [12, 13, 14],
        "favorite_animal": "Giraffe",
        "least_favorite_smell": "Vinegar",
        "hobbies": ["Golf", "Painting"],
        "birthplace": "Mountainville",
        "favorite_color": "Orange",
        "unread_messages": 2,
        "has_pets": false,
        "shoe_size": 11.0,
        "coffee_preference": "Americano",
        "dream_destination": "Switzerland",
        "music_genre": "Jazz",
        "is_vegan": false,
        "fitness_level": "Beginner",
        "lucky_number": 8
    },
    {
        "id": 7,
        "first_name": "Grace",
        "last_name": "Martinez",
        "email": "grace@example.com",
        "age": 29,
        "last_login_date": "2023-08-09T16:15:00",
        "friends_user_ids": [1, 15, 16],
        "favorite_animal": "Penguin",
        "least_favorite_smell": "Cabbage",
        "hobbies": ["Singing", "Cooking"],
        "birthplace": "Snowville",
        "favorite_color": "White",
        "unread_messages": 1,
        "has_pets": true,
        "shoe_size": 6.5,
        "coffee_preference": "Latte",
        "dream_destination": "Hawaii",
        "music_genre": "Reggae",
        "is_vegan": true,
        "fitness_level": "Intermediate",
        "lucky_number": 13
    },
    {
        "id": 8,
        "first_name": "Henry",
        "last_name": "Taylor",
        "email": "henry@example.com",
        "age": 32,
        "last_login_date": "2023-08-08T13:30:00",
        "friends_user_ids": [2, 4, 9, 17],
        "favorite_animal": "Tiger",
        "least_favorite_smell": "Mold",
        "hobbies": ["Running", "Chess"],
        "birthplace": "Desertville",
        "favorite_color": "Red",
        "unread_messages": 12,
        "has_pets": true,
        "shoe_size": 10.0,
        "coffee_preference": "Cappuccino",
        "dream_destination": "Australia",
        "music_genre": "Country",
        "is_vegan": false,
        "fitness_level": "Advanced",
        "lucky_number": 21
    },
    {
        "id": 9,
        "first_name": "Ivy",
        "last_name": "Anderson",
        "email": "ivy@example.com",
        "age": 24,
        "last_login_date": "2023-08-09T07:55:00",
        "friends_user_ids": [3, 5, 8, 18],
        "favorite_animal": "Kangaroo",
        "least_favorite_smell": "Smoked Fish",
        "hobbies": ["Dancing", "Biking"],
        "birthplace": "Jungleville",
        "favorite_color": "Yellow",
        "unread_messages": 6,
        "has_pets": true,
        "shoe_size": 8.0,
        "coffee_preference": "Espresso",
        "dream_destination": "Brazil",
        "music_genre": "Hip Hop",
        "is_vegan": true,
        "fitness_level": "Beginner",
        "lucky_number": 6
    },
    {
        "id": 10,
        "first_name": "Jack",
        "last_name": "Moore",
        "email": "jack@example.com",
        "age": 30,
        "last_login_date": "2023-08-07T21:20:00",
        "friends_user_ids": [4, 7, 9, 19],
        "favorite_animal": "Horse",
        "least_favorite_smell": "Burnt Rubber",
        "hobbies": ["Photography", "Travelling"],
        "birthplace": "Farmville",
        "favorite_color": "Brown",
        "unread_messages": 9,
        "has_pets": true,
        "shoe_size": 10.5,
        "coffee_preference": "Black",
        "dream_destination": "New Zealand",
        "music_genre": "Alternative",
        "is_vegan": false,
        "fitness_level": "Intermediate",
        "lucky_number": 11
    },
    {
        "id": 11,
        "first_name": "Karen",
        "last_name": "Jackson",
        "email": "karen@example.com",
        "age": 26,
        "last_login_date": "2023-08-09T14:50:00",
        "friends_user_ids": [5, 10, 20],
        "favorite_animal": "Panda",
        "least_favorite_smell": "Rotting Fruit",
        "hobbies": ["Yoga", "Reading"],
        "birthplace": "Cityville",
        "favorite_color": "Pink",
        "unread_messages": 4,
        "has_pets": false,
        "shoe_size": 7.0,
        "coffee_preference": "Mocha",
        "dream_destination": "Thailand",
        "music_genre": "R&B",
        "is_vegan": true,
        "fitness_level": "Advanced",
        "lucky_number": 15
    },
    {
        "id": 12,
        "first_name": "Liam",
        "last_name": "Garcia",
        "email": "liam@example.com",
        "age": 21,
        "last_login_date": "2023-08-08T08:00:00",
        "friends_user_ids": [6, 13, 15],
        "favorite_animal": "Sloth",
        "least_favorite_smell": "Dirty Socks",
        "hobbies": ["Gaming", "Cooking"],
        "birthplace": "Villageville",
        "favorite_color": "Black",
        "unread_messages": 7,
        "has_pets": true,
        "shoe_size": 9.5,
        "coffee_preference": "Latte",
        "dream_destination": "Greece",
        "music_genre": "Metal",
        "is_vegan": false,
        "fitness_level": "Beginner",
        "lucky_number": 14
    },
    {
        "id": 13,
        "first_name": "Mia",
        "last_name": "Rodriguez",
        "email": "mia@example.com",
        "age": 33,
        "last_login_date": "2023-08-09T11:40:00",
        "friends_user_ids": [6, 12, 14, 16],
        "favorite_animal": "Owl",
        "least_favorite_smell": "Stale Beer",
        "hobbies": ["Singing", "Painting"],
        "birthplace": "Countryville",
        "favorite_color": "Blue",
        "unread_messages": 5,
        "has_pets": true,
        "shoe_size": 8.5,
        "coffee_preference": "Cappuccino",
        "dream_destination": "Italy",
        "music_genre": "Pop",
        "is_vegan": true,
        "fitness_level": "Intermediate",
        "lucky_number": 8
    },
    {
        "id": 14,
        "first_name": "Noah",
        "last_name": "Martinez",
        "email": "noah@example.com",
        "age": 40,
        "last_login_date": "2023-08-08T17:55:00",
        "friends_user_ids": [6, 13, 15],
        "favorite_animal": "Wolf",
        "least_favorite_smell": "Mothballs",
        "hobbies": ["Running", "Cooking"],
        "birthplace": "Cityville",
        "favorite_color": "Green",
        "unread_messages": 2,
        "has_pets": false,
        "shoe_size": 10.0,
        "coffee_preference": "Espresso",
        "dream_destination": "Australia",
        "music_genre": "Rock",
        "is_vegan": false,
        "fitness_level": "Advanced",
        "lucky_number": 19
    },
    {
        "id": 15,
        "first_name": "Olivia",
        "last_name": "Thompson",
        "email": "olivia@example.com",
        "age": 29,
        "last_login_date": "2023-08-09T10:15:00",
        "friends_user_ids": [7, 13, 14, 17],
        "favorite_animal": "Duck",
        "least_favorite_smell": "Wet Dog",
        "hobbies": ["Soccer", "Gardening"],
        "birthplace": "Townsville",
        "favorite_color": "Red",
        "unread_messages": 1,
        "has_pets": true,
        "shoe_size": 7.0,
        "coffee_preference": "Black",
        "dream_destination": "Japan",
        "music_genre": "Electronic",
        "is_vegan": true,
        "fitness_level": "Intermediate",
        "lucky_number": 10
    },
    {
        "id": 16,
        "first_name": "Peter",
        "last_name": "Wright",
        "email": "peter@example.com",
        "age": 37,
        "last_login_date": "2023-08-08T12:30:00",
        "friends_user_ids": [6, 15, 18],
        "favorite_animal": "Bear",
        "least_favorite_smell": "Gasoline",
        "hobbies": ["Reading", "Biking"],
        "birthplace": "Seaville",
        "favorite_color": "Blue",
        "unread_messages": 0,
        "has_pets": false,
        "shoe_size": 9.5,
        "coffee_preference": "Mocha",
        "dream_destination": "New Zealand",
        "music_genre": "Indie Rock",
        "is_vegan": false,
        "fitness_level": "Advanced",
        "lucky_number": 7
    },
    {
        "id": 17,
        "first_name": "Quinn",
        "last_name": "Hernandez",
        "email": "quinn@example.com",
        "age": 25,
        "last_login_date": "2023-08-09T09:05:00",
        "friends_user_ids": [8, 16, 19],
        "favorite_animal": "Fox",
        "least_favorite_smell": "Wet Carpet",
        "hobbies": ["Painting", "Singing"],
        "birthplace": "Mountainville",
        "favorite_color": "Orange",
        "unread_messages": 4,
        "has_pets": true,
        "shoe_size": 8.0,
        "coffee_preference": "Latte",
        "dream_destination": "Greece",
        "music_genre": "Pop",
        "is_vegan": true,
        "fitness_level": "Beginner",
        "lucky_number": 22
    },
    {
        "id": 18,
        "first_name": "Ryan",
        "last_name": "Adams",
        "email": "ryan@example.com",
        "age": 32,
        "last_login_date": "2023-08-08T14:25:00",
        "friends_user_ids": [9, 15, 20],
        "favorite_animal": "Gorilla",
        "least_favorite_smell": "Burnt Popcorn",
        "hobbies": ["Gaming", "Hiking"],
        "birthplace": "Villageville",
        "favorite_color": "Green",
        "unread_messages": 6,
        "has_pets": true,
        "shoe_size": 10.5,
        "coffee_preference": "Black",
        "dream_destination": "Hawaii",
        "music_genre": "Electronic",
        "is_vegan": false,
        "fitness_level": "Intermediate",
        "lucky_number": 18
    },
    {
        "id": 19,
        "first_name": "Sophia",
        "last_name": "Gonzalez",
        "email": "sophia@example.com",
        "age": 28,
        "last_login_date": "2023-08-09T13:00:00",
        "friends_user_ids": [10, 17, 20],
        "favorite_animal": "Llama",
        "least_favorite_smell": "Public Restrooms",
        "hobbies": ["Reading", "Dancing"],
        "birthplace": "Cityville",
        "favorite_color": "Purple",
        "unread_messages": 2,
        "has_pets": true,
        "shoe_size": 8.0,
        "coffee_preference": "Latte",
        "dream_destination": "Paris",
        "music_genre": "Pop",
        "is_vegan": true,
        "fitness_level": "Advanced",
        "lucky_number": 4
    },
    {
        "id": 20,
        "first_name": "Thomas",
        "last_name": "Walker",
        "email": "thomas@example.com",
        "age": 30,
        "last_login_date": "2023-08-08T19:45:00",
        "friends_user_ids": [11, 19],
        "favorite_animal": "Monkey",
        "least_favorite_smell": "Sewage",
        "hobbies": ["Cooking", "Traveling"],
        "birthplace": "Townsville",
        "favorite_color": "Blue",
        "unread_messages": 7,
        "has_pets": false,
        "shoe_size": 9.5,
        "coffee_preference": "Cappuccino",
        "dream_destination": "Maldives",
        "music_genre": "Rock",
        "is_vegan": false,
        "fitness_level": "Intermediate",
        "lucky_number": 16
    }
]
"""

@BlakeBarrett
Copy link
Author

MyComposableUnitTest.kt

import androidx.compose.material3.Text
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import org.junit.Rule
import org.junit.Test

class MyComposableTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun testMyComposable() {
        composeTestRule.setContent {
            // Call your Composable function here
            Text("Hello, World!")
        }

        // Use onNodeWithText or other assertions if needed
        composeTestRule.onNodeWithText("Hello, World!").assertExists()
    }
}

@BlakeBarrett
Copy link
Author

@BlakeBarrett
Copy link
Author

@BlakeBarrett
Copy link
Author

build.gradle

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    kotlin("plugin.serialization") version "1.8.10"
}

android {
    namespace = "com.blakebarrett.myapplication"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.blakebarrett.myapplication"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.3"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    val lifecycle_version = "2.6.1"
    val compose_version = "1.5.0"

    implementation("androidx.core:core-ktx:1.10.1")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
    implementation("androidx.activity:activity-compose:1.7.2")
    implementation(platform("androidx.compose:compose-bom:2023.08.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3:1.1.1")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest:1.5.0")
    // Kotlin serialization
    implementation("org.jetbrains.kotlin:kotlin-serialization-compiler-plugin-embeddable:1.9.0")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")

    // ViewModel
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
    // ViewModel utilities for Compose
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version")
    // LiveData
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
    // Lifecycles only (without ViewModel or LiveData)
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")
    // Lifecycle utilities for Compose
    implementation("androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version")
    // Saved state module for ViewModel
    implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version")

    implementation("androidx.compose.runtime:runtime:$compose_version")
    implementation("androidx.compose.runtime:runtime-livedata:$compose_version")
    // Test rules and transitive dependencies:
    testImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
    // Needed for createAndroidComposeRule, but not createComposeRule:
    testImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")

    // Test rules and transitive dependencies:
    androidTestImplementation("androidx.compose.ui:ui-test-junit4:$compose_version")
    // Needed for createAndroidComposeRule, but not createComposeRule:
    debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")

    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")

    // or Material Design 2
    implementation("androidx.compose.material:material")
    // or skip Material Design and build directly on top of foundational components
    implementation("androidx.compose.foundation:foundation")

    val nav_version = "2.7.1"
    implementation("androidx.navigation:navigation-compose:$nav_version")
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment