so, I have two columns where one has column has all the images and the other column has all the names associated with these images. in Short, images are the Keys and names are the values but the names are randomly generated and displayed, they are not displayed in order. User needs to click one of the Cards from first column and click the right name from second column. So, what I want is ; when i click on one of the cards in first column[image card] it's background color should become green and at the same time if i click correct [name card] from other column, it's background should also become green or it's background should become Red. I have tried to achieve it by using remember and storing a value of selectedImageId but my app just crashes. Below is my screen this is how it looks and here if i click one of the images it should turn into green and then when i click correct name from other column, that name card should also turn to green

Below is the code of my Activity MainActivity.kt

package com.example.composegrid

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
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.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.Role.Companion.Image
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.composegrid.ui.theme.ComposeGridTheme

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.font.FontWeight

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeGridTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    PlayScreen()
                }
            }
        }
    }
}



@Composable
fun PlayScreen() {
    // Generate the list of images and names as key-value pairs
    val imageNamesMap = generateImageNamesMap()

    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) {
        // Center the Row in the screen
        Row(
            modifier = Modifier.fillMaxSize(),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            // First column for images (keys)
            CardColumn(imageNamesMap.keys.toList())

            // Second column for names (values)
            CardColumn(imageNamesMap.values.toList(), true)
        }
    }
}



@Composable
fun CardColumn(items: List<Any>, isNamesColumn: Boolean = false) {
    LazyColumn(
        modifier = Modifier.padding(start = if (isNamesColumn) 16.dp else 0.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(items) { item ->
            Card(modifier = Modifier.size(100.dp).padding(start = 30.dp, top = 20.dp)) {
                if (isNamesColumn) {
                    NameItem(name = item as String)
                } else {
                    ImageItem(imageResId = item as Int)
                }
            }
        }
    }
}


fun generateImageNamesMap(): HashMap<Int, String> {
    // Add image resources to a list
    val images = listOf(
        R.drawable.he,
        R.drawable.how,
        R.drawable.what,
        R.drawable.we,
        // Add more image resources as needed
    )

    // Add image names to a list in random order
    val imageNames = listOf(
        "Name1",
        "Name2",
        "Name3",
        "Name4",
        "Name5"
        // Add more image names corresponding to the resources
    )

    // Shuffle the image names list randomly
    val shuffledNames = imageNames.shuffled()

    // Use the shuffled names to assign each image resource ID a corresponding name in the imageNamesMap
    val imageNamesMap = HashMap<Int, String>()
    for (i in images.indices) {
        imageNamesMap[images[i]] = shuffledNames[i]
    }

    return imageNamesMap
}

@Composable
fun ImageItem(imageResId: Int) {
    val painter: Painter = painterResource(id = imageResId)
    Image(
        painter = painter,
        contentDescription = null, // Provide proper content description if needed
        modifier = Modifier
            .size(120.dp)
            .padding(8.dp)
    )
}

@Composable
fun NameItem(name: String) {
    Text(
        text = name,
        style = TextStyle(
            fontSize = 16.sp,
            fontWeight = FontWeight.Bold,
            color = MaterialTheme.typography.bodyMedium.color,
            textAlign = TextAlign.Center
        ),
        modifier = Modifier
            .padding(8.dp)
    )
}


@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    ComposeGridTheme {

        PlayScreen()

    }
}

below is the code for data class ImageInfoData required for it

package com.example.composegrid

data class ImageInfoData(val imageResId:Int,val imageName:String)
2

There are 2 best solutions below

8
Thiên Ân On

I'm not entirely clear about your question, but you may want to try this approach to achieve the desired result similar to this RESULT
You have to use the remember function to store the selected image ID and name ID like this:

var selectedImageId by remember { mutableStateOf(-1) }

var selectedNameValue by remember { mutableStateOf("") }

The isSelected variable in the CardColumn composable is used to track whether a card is selected or not, and the background color is set accordingly.

Updated: Full code

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.myapplication.ui.theme.MyApplicationTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                val openDialog = remember { mutableStateOf(false) }
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    PlayScreen()
                }
            }
        }
    }
}


@Composable
fun PlayScreen() {
    var selectedImageId by remember { mutableStateOf(-1) }
    var selectedNameValue by remember { mutableStateOf("") }
    // Generate the list of images and names as key-value pairs
    val imageNamesMap = generateImageNamesMap()
    val randomKeys by remember { mutableStateOf(imageNamesMap.values.toList().shuffled()) }
    var selectedIndex by remember { mutableStateOf(-1) }

    Surface(
        modifier = Modifier.fillMaxSize(),
        color = MaterialTheme.colorScheme.background
    ) {
        // Center the Row in the screen
        Row(
            modifier = Modifier.fillMaxSize(),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            // First column for images (keys)
            CardColumn(
                imageNamesMap.keys.toList(),
                isNamesColumn = false,
                selectedId = selectedImageId,
                onCardClick = {
                    selectedImageId = it as Int
                    selectedNameValue = ""
                }
            )

            // Second column for names (values)
            CardColumn(
                randomKeys,
                isNamesColumn = true,
                selectedId = selectedNameValue,
                selectedIndex = selectedIndex,
                onCardClick = {
                    if (imageNamesMap.getOrDefault(selectedImageId, "") == it) {
                        selectedNameValue = it // Unique ID for name
                        selectedIndex = -1
                    } else {
                        selectedNameValue = "" // Reset if it's incorrect
                        selectedIndex = randomKeys.indexOf(it)
                    }
                }
            )
        }
    }
}

@Composable
fun CardColumn(
    dataImage: List<Any>,
    isNamesColumn: Boolean = false,
    selectedId: Any,
    selectedIndex: Int = -1,
    onCardClick: (Any) -> Unit
) {
    LazyColumn(
        modifier = Modifier.padding(start = if (isNamesColumn) 16.dp else 0.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        itemsIndexed(dataImage) {index, item ->
            val isSelected = if (isNamesColumn) {
                item.toString() == selectedId as String
            } else {
                item as Int == selectedId as Int
            }

            Card(
                modifier = Modifier
                    .size(100.dp)
                    .padding(start = 30.dp, top =    20.dp)
                    .clickable {
                        onCardClick(item)
                    },
                colors = cardColors( containerColor = when {
                    isSelected -> Color.Green
                    else -> if (selectedIndex != -1 && selectedIndex == index) Color.Red else Color.LightGray
                })
            ) {
                if (isNamesColumn) {
                    NameItem(name = item as String, isSelected = isSelected)
                } else {
                    ImageItem(imageResId = item as Int, isSelected = isSelected)
                }
            }
        }
    }
}

data class ImageInfoData(val imageResId: Int, val imageName: String)

@Composable
fun ImageItem(imageResId: Int, isSelected: Boolean) {
    val painter: Painter = painterResource(id = imageResId)
    Image(
        painter = painter,
        contentDescription = null,
        modifier = Modifier
            .size(120.dp)
            .padding(8.dp),
        contentScale = ContentScale.Crop,
        alignment = if (isSelected) Alignment.TopCenter else Alignment.Center
    )
}


fun generateImageNamesMap(): Map<Int, String> {
    // Add image resources to a list
    val images = listOf(
        R.drawable.account_avatar_1,
        R.drawable.account_avatar_2,
        R.drawable.account_avatar_3,
        R.drawable.account_avatar_4,
        // Add more image resources as needed
    )

    // Add image names to a list in random order
    val imageNames = listOf(
        "Name1",
        "Name2",
        "Name3",
        "Name4",
        // Add more image names corresponding to the resources
    )

    // Use the 'zip' function to pair the images with the shuffled names and then convert it to a map
    return images.zip(imageNames).toMap()
}


@Composable
fun NameItem(name: String, isSelected: Boolean) {
    Text(
        text = name,
        style = TextStyle(
            fontSize = 16.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center
        ),
        modifier = Modifier
            .padding(8.dp)
            .fillMaxWidth(),
    )
}
0
zaid khan On

Create a Ui state data class which will be initialized as a MutableStateFlow() object and will be collectAsStateWithLifeCycle() and pass it to the sub composables. And an event class that would contain user interaction events like image selected and name selected. Example uiState Class :

data class PlayScreenUiState(
  val selectedImage : Int? = null,
  val selectedName : String? = null,
  
)
interface PlayScreenEvent{

  data class ImageSelected(val imageId : Int) : PlayScreenEvent

  data class NameSelected(val name : String) : PlayScreenEvent

}

Here's how you'll collect the change the depending on the event.

class MainViewModel : ViewModel(){

  private val _playScreenUiState  = MutableStateFlow(PlayScreenUiState())
  val playScreenUiState  = _playScreenUiState.asStateFlow()

  fun onEvent(event : PlayScreenEvent) {

  is PlayScreenEvent.ImageSelected -> {
         _playScreenUiState.update {
               it.copy(
                  selectedImage = it.imageId
             )
      }
   }

  is PlayScreenEvent.NameSelected-> {
         _playScreenUiState.update {
               it.copy(
                  selectedName = it.name
             )
      }
   }
}

}


Create the viewModel collect the state and pass in to the sub composable.

val viewModel : MainViewModel by viewModels()
val uiState by viewModel.playerScreenUiState.collectAsStateWithLifecylce()


PlayScreen(uiState,viewModel::onEvent)
@Composable
fun PlayScreen(
   uiState : PlayScreenUiState,
   onEvent : (PlayScreenEvent) -> Unit
) {
   var imageBackGroundColor by remember{
           mutableStateOf(Color.White)
    }
    var textBackGroundColor by remember{
           mutableStateOf(Color.White)
    }

    if(uiState.selectedImage != null && uiState.selectedImage != null){

       //check if both the values are correct according to the hashmap and
        //change both the background color to green or red accordingly


       
     }

    //similarly check the condition where only one of the is selected and set it the background to red.
}