Die Benutzeroberfläche wird beim ersten App-Start nicht mit Daten aktualisiert, sondern geladen, wenn die App geschlosseAndroid

Forum für diejenigen, die für Android programmieren
Guest
 Die Benutzeroberfläche wird beim ersten App-Start nicht mit Daten aktualisiert, sondern geladen, wenn die App geschlosse

Post by Guest »

Ich habe ein Problem, bei dem beim ersten Öffnen der App, wenn der Benutzer das Onboarding durchläuft und dann zum Startbildschirm wechselt, die Benutzeroberfläche die Daten nicht aus der Datenbank lädt und auf unbestimmte Zeit auf einem leeren weißen Bildschirm hängen bleibt. Aber nachdem die App geschlossen und dann erneut geöffnet wurde, werden die Daten wie gewohnt geladen.
Ich erhalte dabei auch diese Fehler im Protokoll:
kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelling}@6cd224a
Ein Teil des Codes ist unten:
Bildschirm, der beim ersten Start nicht geladen wird:

Code: Select all

@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)
})
}
}
Ansichtsmodell des Bildschirms:

Code: Select all

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
)
Das Ansichtsmodell des Bildschirms vor dem Tagebuchbildschirm:

Code: Select all

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
)

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post