1

this started as a new compose project with the following code the intent is to change the text to the picked time. The code is commented where the behavior occurs

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

                TimeCardButton(id = 1, symbol ="In", enabled=true,modifier = Modifier) { entry ->
                    Log.d("click", "$entry result")
                    }

                }
            }
        }
    }
}


data class TimeCardEntry(val id: Int = -1, var entry: String = "")

@Composable
fun TimeCardButton(
id: Int,
symbol: String,
enabled: Boolean = false,
modifier: Modifier,
onValueChange: (TimeCardEntry) -> Unit = {},
) {

// Value for storing time as a string
val timeState = remember {
    mutableStateOf(TimeCardEntry(id, symbol))
}

val validState = remember {
    timeState.value.entry.trim().isNotEmpty()
}

val mTime = remember { mutableStateOf(symbol) }


if (enabled) {
    // Fetching local context
    val mContext = LocalContext.current

    // Declaring and initializing a calendar
    val mCalendar = Calendar.getInstance()
    val mHour = mCalendar[Calendar.HOUR_OF_DAY]
    val mMinute = mCalendar[Calendar.MINUTE]

    // Creating a TimePicker dialog
    val mTimePickerDialog = TimePickerDialog(
        mContext,
        { _, mHour: Int, mMinute: Int ->
            timeState.value.entry = "$mHour:$mMinute"
            mTime.value = "$mHour:$mMinute"
            onValueChange(timeState.value)
        }, mHour, mMinute, false
    )

    Box(
        contentAlignment = Alignment.Center,
        modifier = Modifier
            .clip(CircleShape)
            .then(modifier)
    ) {
        TextButton(onClick = { mTimePickerDialog.show() }.also {  
Log.d("click", "id $id clicked!") }) {
           Column() {
               // if I use just this it works [in changes to the time picked]
               //Text(text = mTime.value)
               
               // if i use both of these BOTH are set when the date picker is invoked
               // if I just use the second one alone, the text never changes
               Text(text = timeState.value.entry)
           }

        }
    }
} else {
    Box(
        contentAlignment = Alignment.Center,
        modifier = Modifier
            .clip(CircleShape)
            .then(modifier)
    ) {
        Text(text = symbol, color = 
MaterialTheme.colors.onBackground)
    }
}
}



@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
MyApplicationTestTheme {

    }
}

1 Answers1

0

First of all how to fix it: Your problem basically is this. The easiest way to fix it would be to reassign the whole value of TimeState, not just entry by calling

timeState.value = timeState.value.copy(entry = "$mHour:$mMinute")

The reason it doesn't work with only the second one is that the change of a property doesn't trigger recomposition, even if the variable containing it is a mutableState. To fix (as outlined in the answers to the question linked above) this you either have to reassign the whole variable or make the parameter you want to observe observable (for example changing the String to State<String>)

PS: if you use by with mutableStateOf (i.e. val timeState = remember { mutableStateOf(TimeCardEntry(id, symbol)) }) you don't have to use .value every time. I find that a lot cleaner and more readable

Mr. Pine
  • 239
  • 3
  • 13