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 !
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"