У меня возникла проблема: при первом открытии приложения, когда пользователь проходит регистрацию, а затем переходит на главный экран, пользовательский интерфейс не загружает данные из базы данных и зависает на пустом белом экране на неопределенный срок. Но после закрытия и повторного открытия приложения данные загружаются как обычно.
Во время этого я также получаю следующие ошибки в журнале:
kotlinx.coroutines.JobCancellationException: задание было отменено; job=SupervisorJobImpl{Cancelling}@6cd224a
Часть кода приведена ниже:
Экран, который не загружается при первом запуске:
@Composable
fun DiaryComponentInit(
viewmodel: DiaryLogic = koinViewModel(),
navigator: Navigator?,
selectedDate: LocalDate = getCurrentDate()
) {
val state by viewmodel.homeState.collectAsState()
LaunchedEffect(selectedDate) {
viewmodel.fetchDataForDate(selectedDate)
}
state.nutrientsIndicator?.let { nutrientsData ->
DiaryComponent(
navigator = navigator,
userName = state.userName ?: "",
nutrientsData = nutrientsData,
waterIntakeData = state.waterIntake,
mealsData = state.meals,
selectedDate = state.selectedDate,
onDateChange = { viewmodel.fetchDataForDate(it) },
onWaterValueChange = { value, date ->
viewmodel.updateWaterIntake(value, date)
}
)
}
}
@Composable
private fun DiaryComponent(
navigator: Navigator?,
userName: String = "",
nutrientsData: NutrientsIndicatorState,
waterIntakeData: WaterIntakeState? = null,
mealsData: MealsState? = null,
selectedDate: LocalDate,
onDateChange: (LocalDate) -> Unit = {},
onWaterValueChange: (Float, LocalDate) -> Unit = { _, _ -> }
) {
val scrollState = rememberScrollState()
val navBarHeight = 85.dp
var calendarDialogShow by remember { mutableStateOf(false) }
val currentDate = remember { mutableStateOf("Today") }
Box(
modifier = Modifier
.fillMaxSize()
.safeDrawingPadding()
) {
Column(
modifier = Modifier
.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(scrollState)
.padding(16.dp)
) {
// Header: Profile and Calendar
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// User Profile with Name
Row(
modifier = Modifier
.clickable {
navigator?.parent?.push(SettingsComponent())
},
verticalAlignment = Alignment.CenterVertically
) {
Card(
modifier = Modifier.size(45.dp),
shape = CircleShape,
backgroundColor = Color.Black
) {
Icon(
modifier = Modifier
.fillMaxSize()
.padding(8.dp),
painter = painterResource(Res.drawable.banana),
contentDescription = "Profile Picture",
tint = Color.Unspecified
)
}
Spacer(modifier = Modifier.width(8.dp))
Text(
text = userName,
fontSize = 16.sp,
color = Color.Black
)
}
// Date and Calendar
Row(
modifier = Modifier.clickable { calendarDialogShow = true },
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = currentDate.value,
fontSize = 16.sp,
color = Color.Black
)
Spacer(modifier = Modifier.width(8.dp))
Icon(
painter = painterResource(Res.drawable.calendar),
contentDescription = "Calendar Icon",
tint = Color(0xFF5CB85C),
modifier = Modifier.size(30.dp)
)
}
}
Spacer(modifier = Modifier.height(24.dp))
// Nutrients Section
Text(
modifier = Modifier.padding(bottom = 8.dp),
text = "Nutrients Indicator",
fontSize = 16.sp
)
NutrientsIndicatorComponent(nutrientsData)
Spacer(modifier = Modifier.height(24.dp))
// Water Intake Section
Text(
modifier = Modifier.padding(bottom = 8.dp),
text = "Water Intake",
fontSize = 16.sp
)
WaterIntakeComponent(
waterIntakeData = waterIntakeData,
selectedDate = selectedDate,
onWaterIntakeChange = { value, date ->
onWaterValueChange(value, date)
}
)
Spacer(modifier = Modifier.height(24.dp))
// Meals Section Header
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
modifier = Modifier.padding(bottom = 8.dp),
text = "Meals",
fontSize = 16.sp
)
Icon(
modifier = Modifier
.clickable { navigator?.parent?.push(CreateMealComponent(date = selectedDate)) }
.alignByBaseline()
.size(24.dp),
painter = painterResource(Res.drawable.add),
contentDescription = null
)
}
}
if (!mealsData?.meals.isNullOrEmpty()) {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.safeDrawingPadding()
.padding(bottom = navBarHeight, start = 16.dp, end = 16.dp)
) {
mealsData?.meals?.forEachIndexed { index, meal ->
item {
MealsItem(
mealType = meal.name ?: "Meal $index",
calories = meal.totalCalories.toString(),
time = meal.date ?: "Unknown"
)
}
}
}
} else {
Box(
modifier = Modifier
.fillMaxWidth()
) {
Text(
modifier = Modifier
.align(Alignment.Center)
.padding(16.dp),
text = "No meals for Today",
fontSize = 18.sp
)
}
}
}
}
if (calendarDialogShow) {
CalendarComponent(onDismiss = { formattedDate, newDate ->
calendarDialogShow = false
currentDate.value = formattedDate ?: currentDate.value
newDate?.let(onDateChange)
})
}
}
Вид модели экрана:
class DiaryLogic: ViewModel(), KoinComponent {
private val databaseRepo: DatabaseRepo by inject()
private val _homeState = MutableStateFlow(HomeUiState())
val homeState: StateFlow = _homeState
init {
getUserName()
updateDataForSelectedDate()
}
fun fetchDataForDate(date: LocalDate) {
viewModelScope.launch {
// Update selected date first
_homeState.value = _homeState.value.copy(selectedDate = date)
// Then fetch data for that date
getUserName()
getNutrientsData(date)
getWaterIntakeData(date)
getMealsData(date)
}
}
private fun updateDataForSelectedDate() {
val selectedDate = _homeState.value.selectedDate
getWaterIntakeData(selectedDate)
getNutrientsData(selectedDate)
getMealsData(selectedDate)
}
private fun getUserName() {
viewModelScope.launch {
val userName = databaseRepo.getUserName().toString()
_homeState.value = _homeState.value.copy(userName = userName)
}
}
private fun getNutrientsData(date: LocalDate) {
viewModelScope.launch {
val recommendedUserData = databaseRepo.getUser()
val nutrientsData = databaseRepo.getMealsByDate(date = date)
val totalCalories = nutrientsData.sumOf { it.totalCalories.toInt() }
val totalProteins = nutrientsData.sumOf { it.totalProteins.toInt() }
val totalFats = nutrientsData.sumOf { it.totalFats.toInt() }
val totalCarbs = nutrientsData.sumOf { it.totalCarbs.toInt() }
_homeState.value = _homeState.value.copy(nutrientsIndicator = NutrientsIndicatorState(
currentCalories = totalCalories,
currentProteins = totalProteins,
currentFats = totalFats,
currentCarbs = totalCarbs,
recommendedCalories = recommendedUserData!!.recommendedCalories,
recommendedProteins = recommendedUserData.recommendedProteins,
recommendedCarbs = recommendedUserData.recommendedCarbs,
recommendedFats = recommendedUserData.recommendedFats
))
}
}
private fun getWaterIntakeData(date: LocalDate) {
viewModelScope.launch {
val waterIntakeData = databaseRepo.getWaterIntake(date = date)
val recommendedUserData = databaseRepo.getUser()
val currentIntake = (waterIntakeData?.currentIntake ?: 0f).roundToDecimals(1)
val recommendedIntake = recommendedUserData?.recommendedWaterIntake ?: 1f
// Store the water intake data for the selected date
_homeState.value = _homeState.value.copy(
selectedDateWaterIntake = waterIntakeData,
waterIntake = WaterIntakeState(
currentWaterIntake = currentIntake,
recommendedWaterIntake = recommendedIntake,
lastUpdatedTime = waterIntakeData?.lastUpdatedTime,
currentWaterIntakePercentage = ((currentIntake / recommendedIntake) * 100).toInt()
)
)
}
}
private fun getMealsData(date: LocalDate) {
viewModelScope.launch {
val mealData = databaseRepo.getMealsByDate(date = date)
_homeState.value = _homeState.value.copy(
meals = MealsState(
meals = mealData.map { meal ->
MealState(
name = meal.name,
totalCalories = meal.totalCalories.toInt(),
date = meal.date.toString()
)
}
)
)
}
}
fun updateWaterIntake(value: Float, date: LocalDate) {
viewModelScope.launch {
val existingWaterIntake = _homeState.value.selectedDateWaterIntake ?:
databaseRepo.getWaterIntake(date = date)
val currentValue = existingWaterIntake?.currentIntake ?: 0f
val newValue = (currentValue + value).coerceAtLeast(0f)
val currentIntake = newValue.roundToDecimals(1)
val recommendedIntake = homeState.value.waterIntake?.recommendedWaterIntake ?: 1f
// Create new water intake data
val updatedWaterIntake = WaterIntake(
id = existingWaterIntake?.id ?: 0,
date = date,
currentIntake = currentIntake,
lastUpdatedTime = getCurrentTimeFormatted()
)
// Update database
databaseRepo.updateWaterIntake(updatedWaterIntake)
// Update state
_homeState.value = _homeState.value.copy(
selectedDateWaterIntake = updatedWaterIntake,
waterIntake = WaterIntakeState(
currentWaterIntake = currentIntake,
recommendedWaterIntake = recommendedIntake,
lastUpdatedTime = getCurrentTimeFormatted(),
currentWaterIntakePercentage = ((currentIntake / recommendedIntake) * 100).toInt()
)
)
}
}
}
data class HomeUiState(
val userName: String? = null,
val selectedDate: LocalDate = getCurrentDate(),
val selectedDateWaterIntake: WaterIntake? = null,
val nutrientsIndicator: NutrientsIndicatorState? = null,
val waterIntake: WaterIntakeState? = null,
val meals: MealsState? = null
)
data class NutrientsIndicatorState(
val currentProteins: Int? = null,
val currentFats: Int? = null,
val currentCarbs: Int? = null,
val currentCalories: Int? = null,
val recommendedProteins: Int? = null,
val recommendedFats: Int? = null,
val recommendedCarbs: Int? = null,
val recommendedCalories: Int? = null,
)
data class WaterIntakeState(
val currentWaterIntake: Float? = null,
val recommendedWaterIntake: Float? = null,
val lastUpdatedTime: String? = null,
val currentWaterIntakePercentage: Int? = null
)
data class MealsState(
val meals: List? = null
)
data class MealState(
val name: String? = null,
val totalCalories: Int? = null,
val date: String? = null
)
Модель просмотра экрана перед экраном дневника:
class BaseOnboardingLogic: ViewModel(), KoinComponent {
private val databaseRepo: DatabaseRepo by inject()
private val userRepo: UserRepo by inject()
private val _onboardingState = MutableStateFlow(OnboardingUiState())
val onboardingState: StateFlow = _onboardingState
fun setUserExists() {
viewModelScope.launch {
userRepo.setUserExists(true)
}
}
fun updateGoal(goal: String) {
_onboardingState.value = _onboardingState.value.copy(goal = goal)
}
fun updateGender(gender: String) {
_onboardingState.value = _onboardingState.value.copy(gender = gender)
}
fun updateActivity(activity: String) {
_onboardingState.value = _onboardingState.value.copy(activity = activity)
}
fun updateHeightFeet(heightFeet: Int) {
_onboardingState.value = _onboardingState.value.copy(heightFeet = heightFeet)
}
fun updateHeightInches(heightInches: Int) {
_onboardingState.value = _onboardingState.value.copy(heightInches = heightInches)
}
fun updateWeight(weight: Int) {
_onboardingState.value = _onboardingState.value.copy(weight = weight)
}
fun updateWeightUnit(weightUnit: String) {
_onboardingState.value = _onboardingState.value.copy(weightUnit = weightUnit)
}
fun updateAge(age: String) {
_onboardingState.value = _onboardingState.value.copy(age = age)
}
suspend fun saveUserDetails() {
val user = User(
name = "Adrian",
age = _onboardingState.value.age!!.toInt(),
gender = _onboardingState.value.gender!!,
heightFeet = _onboardingState.value.heightFeet!!,
heightInches = _onboardingState.value.heightInches!!,
weight = _onboardingState.value.weight!!.toFloat(),
weightUnit = _onboardingState.value.weightUnit!!,
goal = _onboardingState.value.goal!!,
activityLevel = _onboardingState.value.activity!!,
recommendedProteins = _onboardingState.value.recommendedProteins!!,
recommendedFats = _onboardingState.value.recommendedFats!!,
recommendedCarbs = _onboardingState.value.recommendedCarbs!!,
recommendedCalories = _onboardingState.value.recommendedCalories!!,
recommendedWaterIntake = _onboardingState.value.recommendedWaterIntake!!,
)
databaseRepo.saveUser(user)
}
fun calculatePFC(userDetails: OnboardingUiState): Map? {
// Retrieve user details
val weight = userDetails.weight!!.toInt()
val heightFeet = userDetails.heightFeet!!.toInt()
val heightInches = userDetails.heightInches!!.toInt()
val age = userDetails.age!!.toInt()
val gender = userDetails.gender
val activity = userDetails.activity
val goal = userDetails.goal
// Convert height to cm
val heightCm = (heightFeet * 30.48) + (heightInches * 2.54)
// Convert weight to kg if necessary
val weightKg = if (userDetails.weightUnit == "lbs") weight * 0.453592 else weight
// Calculate BMR using Harris-Benedict Equation
val bmr = when (gender) {
"Male" -> 88.362 + (13.397 * weightKg.toInt()) + (4.799 * heightCm) - (5.677 * age)
"Female" -> 447.593 + (9.247 * weightKg.toInt()) + (3.098 * heightCm) - (4.330 * age)
else -> return null
}
// Adjust BMR based on activity level
val totalCalories = when (activity) {
"Sedentary" -> bmr * 1.2
"Low active" -> bmr * 1.375
"Active" -> bmr * 1.55
"Very active" -> bmr * 1.825
else -> bmr
}
// Define PFC ratios based on goal
val (proteinRatio, fatRatio, carbRatio) = when (goal) {
"Lose weight" -> Triple(0.4, 0.3, 0.3)
"Keep weight" -> Triple(0.3, 0.3, 0.4)
"Gain weight" -> Triple(0.45, 0.2, 0.35)
else -> Triple(0.3, 0.3, 0.4) // Default: maintenance
}
// Calculate grams of Protein, Fat, and Carbs
val proteinGrams = (totalCalories * proteinRatio / 4).toInt()
val fatGrams = (totalCalories * fatRatio / 9).toInt()
val carbGrams = (totalCalories * carbRatio / 4).toInt()
val waterGrams = if (gender == "Male") 3.7 else 2.5
_onboardingState.value = _onboardingState.value.copy(recommendedProteins = proteinGrams)
_onboardingState.value = _onboardingState.value.copy(recommendedFats = fatGrams)
_onboardingState.value = _onboardingState.value.copy(recommendedCarbs = carbGrams)
_onboardingState.value = _onboardingState.value.copy(recommendedCalories = totalCalories.toInt())
_onboardingState.value = _onboardingState.value.copy(recommendedWaterIntake = waterGrams.toFloat())
// Return PFC distribution and calories
return mapOf(
"protein" to proteinGrams,
"fat" to fatGrams,
"carbs" to carbGrams,
"calories" to totalCalories.toInt()
)
}
}
data class OnboardingUiState(
val goal: String? = null,
val gender: String? = null,
val activity: String? = null,
val heightFeet: Int? = null,
val heightInches: Int? = null,
val weight: Int? = null,
val weightUnit: String? = null,
val age: String? = null,
val recommendedProteins: Int? = null,
val recommendedFats: Int? = null,
val recommendedCarbs: Int? = null,
val recommendedCalories: Int? = null,
val recommendedWaterIntake: Float? = null
)
Подробнее здесь: https://stackoverflow.com/questions/793 ... -closed-an
Пользовательский интерфейс не обновляется данными при первом запуске приложения, но загружается, когда приложение закрыв ⇐ Android
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение