Search code examples
androidkotlinviewandroid-jetpack-composecomposable

Invisible Text When Resuming from backstack


I am building parking app,and I want to get the duration of the parking and update it every second. Everyting is working great UNTIL I pause the app and using other apps on my phone,after about 1 minute when resuming the app,everything stays the same but the duration text is dissapear.

this is the duration composable:

@Composable
fun ParkingDurationText(startingDate: String?, startingTime: String?) {
    val context = LocalContext.current
    val dateFormatter = DateTimeFormatter.ofPattern(ConstHelper.TimePatterns.DATE_PATTERN)
    val timeFormatter = DateTimeFormatter.ofPattern(ConstHelper.TimePatterns.TIME_PATTERN)
    val dateTimeFormatter = DateTimeFormatter.ofPattern(ConstHelper.TimePatterns.DATE_TIME_PATTERN)

    // State for duration
    var duration by remember { mutableStateOf("") }

    // Function to update duration
    val updateDuration = {
        val startDateTime = LocalDateTime.parse(
            "${startingDate ?: LocalDate.now().format(dateFormatter)} ${
                startingTime ?: LocalTime.now().format(timeFormatter)
            }",
            dateTimeFormatter
        )
        val currentDuration = Duration.between(startDateTime, LocalDateTime.now()).seconds
        duration = "Days: ${currentDuration / 86400} Hours: ${(currentDuration % 86400) / 3600} Minutes: ${((currentDuration % 86400) % 3600) / 60} Seconds: ${currentDuration % 60}"
    }

    LaunchedEffect(Unit) {
        while (true) {
Log.e("TAG", "ParkingDurationText:$duration ", )
            delay(1000)
            updateDuration()
        }
    }

    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(5.dp),
        horizontalArrangement = Arrangement.Start,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text(
            modifier = Modifier.padding(end = 5.dp),
            text = stringResource(id = R.string.parking_duration),
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = FontWeight.Bold
        )
        Text(
            text = duration,
            modifier = Modifier,
            style = MaterialTheme.typography.bodyMedium,
            textAlign = TextAlign.Center
        )
    }
}

This is where its called from:

@Composable
fun ParkingInfoLayoutComposable(sharedViewModel: SharedViewModel, clicked: () -> Unit) {
    val context = LocalContext.current

    var showEditDialog by rememberSaveable { mutableStateOf(false) }
    val lastParking by sharedViewModel.currentParking.collectAsState()

    if (showEditDialog) {
        EditParkingInfoDialog(
            onDismiss = {
                showEditDialog = false
            },
            onConfirm = {
                showEditDialog = false
            }
        )
    }

    Column(
        modifier = Modifier
            .wrapContentHeight()
            .padding(8.dp)
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = null,
                onClick = clicked
            )
    ) {
        Row(
            horizontalArrangement = Arrangement.Start,
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier.fillMaxWidth()
        ) {
            InfoRow(
                modifier = Modifier.weight(1f),
                title = stringResource(R.string.parking_location_title),
                value = lastParking?.parkingAddress ?: ""
            )
            Row(
                modifier = Modifier.wrapContentWidth(),
                horizontalArrangement = Arrangement.End
            ) {
                Icon(
                    modifier = Modifier
                        .padding(horizontal = 8.dp)
                        .clickable(
                            interactionSource = remember { MutableInteractionSource() },
                            indication = null,
                            onClick = { showEditDialog = true }
                        ),
                    painter = painterResource(id = R.drawable.edit_icon),
                    contentDescription = "Edit Icon"
                )
                Icon(
                    modifier = Modifier
                        .padding(horizontal = 8.dp)
                        .clickable(
                            interactionSource = remember { MutableInteractionSource() },
                            indication = null,
                            onClick = {
                                lastParking?.let {
                                    openShareIntent(context, it)
                                }
                            }
                        ),
                    painter = painterResource(id = R.drawable.share_icon),
                    contentDescription = "Share Icon"
                )
            }
        }

        InfoRow(
            title = stringResource(R.string.parking_date),
            value = lastParking?.startingDate ?: ""
        )
        InfoRow(
            title = stringResource(id = R.string.parking_starting_time),
            value = lastParking?.startingTime ?: ""
        )
        ParkingDurationText(lastParking?.startingDate, lastParking?.startingTime)
    }
}

and this is ParkingInfoLayoutComposable parent:

@Composable
fun NavigationInfoTopLayout(sharedViewModel: SharedViewModel) {
    val onPrimaryColor = MaterialTheme.colorScheme.primary

    var isExpandable by rememberSaveable { mutableStateOf(sharedViewModel.getIsNavigationLayoutExpanded()) }

    Column(
        modifier = Modifier
            .fillMaxWidth()
            .wrapContentSize()
            .padding(vertical = 20.dp, horizontal = 30.dp)
            .clip(RoundedCornerShape(16.dp))
            .background(onPrimaryColor)
            .padding(vertical = 8.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        ParkingInfoLayoutComposable(sharedViewModel) {
            isExpandable = !isExpandable
        }
        NavigationButtonsLayoutComposable(sharedViewModel, isExpandable)
    }
}

In Addition,if I use the duration composable in the NavigationInfoTopLayout,when resuming,everything will dissapear,but still clickable,I just can't see it, and I only left with the background of the column. The log in the LanchEffect is working and showing the duration without any problems even when the text is gone.

I also tried to do it with the viewmodel and collect the state but it did the same thing.

I can really use some help here,Thanks !


Solution

  • This is a bug, I created an issue on the Google Issue Tracker.
    Edit: The bug was fixed with Compose 1.7.0-beta05. If you update your dependencies accordingly in your build.gradle file, it will work.


    However, it still might be a cleaner approach to specify that the LaunchedEffect observes the Activity lifecycle and starts the loop if the Activity was resumed (onResume).

    You can update your code as follows:

    val lifecycleOwner = LocalLifecycleOwner.current
    val lifecycleState by lifecycleOwner.lifecycle.currentStateFlow.collectAsState()
    
    LaunchedEffect(lifecycleState) {
        while (lifecycleState == Lifecycle.State.RESUMED) {
            updateDuration()
            Log.e("TAG", "ParkingDurationText:$duration ")
            delay(1000)
        }
    }
    

    You need to add the following dependency to your build.gradle file:

    implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.3"