我在KotlinJetpack中使用状态流,但是当我向API发布更改时,我的UI没有更新,视图模型没有观察到新数据

5gfr0r5j  于 2023-05-18  发布在  Kotlin
关注(0)|答案(1)|浏览(107)

我的视图模型在这里没有观察到我发布的新数据,除非我再次初始化它,这是我的视图模型:

@HiltViewModel
class AttendanceViewModel@Inject constructor(
    private val attendanceHomeUseCase: AttendanceHomeUseCase,
    private val sharedPreferences: SharedPreferences,
) : ViewModel() {

    fun sharedUserId(): String {
        return sharedPreferences.getString("user_id", "0") ?: "0"
    }

    fun sharedRoleId(): String {
        return sharedPreferences.getString("role_id", "0") ?: "0"
    }

    fun updateSharedPreference() {
        sharedPreferences.edit().putString("role_id", "0").apply()
        sharedPreferences.edit().putString("user_id", "0").apply()
        sendUiEvent(UiEvent.Navigate(Screen.SplashScreen.route))
    }

    private val _state = MutableStateFlow(HomeState())
    val state:  MutableStateFlow<HomeState> = _state

    init {
        getHome()
    }
    
    private fun getHome() {
        attendanceHomeUseCase(sharedUserId().toInt()).onEach { result ->
            when (result) {
                is Resource.Success -> {
                    _state.value = HomeState(home = result.data)
                    Log.d("shared+Tag", result.data!!.toString())
                }
                is Resource.Loading -> {
                    _state.value = HomeState(isLoading = true)
                }
                is Resource.Error -> {
                    _state.value = HomeState(error = result.message ?: "Unexpected error!")
                }
            }
        }.launchIn(viewModelScope)
    }
}

以下是HomeState数据类:

data class HomeState (
    val isLoading: Boolean = false,
    val home: AttendanceHome? =  null,
    val error: String = ""
)

下面是我的用例,我在调用我的API后将结果发送到Resource类:

class AttendanceHomeUseCase@Inject constructor(
    val repository: LeadRepository,
) {
    operator fun invoke(
        userId: Int
    ) : Flow<Resource<AttendanceHome?>> = flow {
        try {
            emit(Resource.Loading())
            val attendance = repository.getHome(userId)
            emit(Resource.Success(attendance.body()))
        } catch (e: HttpException) {
            emit((Resource.Error(e.localizedMessage ?: "Unexpected error!")))
        } catch (e: IOException) {
            emit(Resource.Error(e.localizedMessage ?: "Can't reach server, check internet connection!"))
        }
    }
}

这是我观察状态的组合:

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun PunchItem(
    viewModel: AttendanceViewModel,
    context: Context,
    rosterId: String,
    navController: NavController
) {
    val state = viewModel.state.collectAsState().value
    val checkInState = viewModel.stateCheck.collectAsState().value
    val checkOutState = viewModel.stateCheckOut.collectAsState().value

    val currentDateTime = LocalDateTime.now()
    val formatter = DateTimeFormatter.ofPattern("d'${getDayOfMonthSuffix(currentDateTime.dayOfMonth)}' MMM")
    val formattedDate = currentDateTime.format(formatter)

    var time1 by rememberSaveable { mutableStateOf("") }

    var time2 by rememberSaveable { mutableStateOf("") }

    var time3 by rememberSaveable { mutableStateOf("") }

    var isPunchIn by rememberSaveable { mutableStateOf(false) }

    var isPunchOut by rememberSaveable { mutableStateOf(false) }

    var isWorkingHrs by remember { mutableStateOf(false) }

    val currentDate = LocalDate.now()
    val formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd")
    val formattedDate1 = currentDate.format(formatter1)

    Box(
        modifier = Modifier
            .padding(start = 16.dp, end = 16.dp, top = 16.dp)
            .fillMaxWidth()
            .clip(RoundedCornerShape(2.dp))
            .background(Color(0xFFFFF7F7))
    ) {
        AnimatedVisibility(visible = checkInState.isLoading || checkOutState.isLoading, enter = fadeIn(), exit = fadeOut()) {
            CircularProgressIndicator(modifier = Modifier.align(Alignment.TopStart).padding(8.dp).size(12.dp), color = RedBColor, strokeWidth = 2.dp)
        }
        Row(
            modifier = Modifier.padding(top = 16.dp, bottom = 16.dp).fillMaxWidth(1f).align(Alignment.CenterStart),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Text(
                text = formattedDate,
                fontSize = 16.sp,
                color = BackColor,
                fontWeight = FontWeight.SemiBold,
                modifier = Modifier.padding(16.dp)
            )

            state.home?.employee_attendance_roster?.first()?.check_in_time?.let {
                Log.d("ROSTER_TAG", "inTime: $it")
                PunchIn(text = it)
                isPunchIn = true
            }

            Spacer(modifier = Modifier.width(16.dp))

            state.home?.employee_attendance_roster?.first()?.check_out_time?.let {
                if (it != "00:00:00") {
                    Log.d("ROSTER_TAG", "outTime: $it")
                    PunchOut(text = it)
                    isWorkingHrs = true
                }
            }

            Spacer(modifier = Modifier.width(16.dp))

            state.home?.employee_attendance_roster?.let { rosterList ->
                if (rosterList.first().working.trim() != "00:00:00" && rosterList.first().working.trim() != "00:00") {
                    WorkingHours(text = state.home.employee_attendance_roster.first().working)
                    isWorkingHrs = true
                }
            }

            Spacer(modifier = Modifier.width(16.dp))

        }

        AnimatedVisibility(
            visible = !isWorkingHrs,
            modifier = Modifier.align(Alignment.CenterEnd),
        ) {
            Row(
                verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier
                    .clickable {
                        if (!isPunchIn) {
                            time1 = getPunchInTime()
                            getLocation(context) {
                                viewModel.checkIn(
                                    CheckInRequest(
                                        viewModel.sharedUserId(),
                                        rosterId,
                                        it.third,
                                        it.second,
                                        it.first,
                                        "",
                                        "",
                                        "",
                                        "",
                                        "",
                                        "",
                                        context.packageName,
                                    )
                                )
                            }
                            isPunchIn = true
                            navController.navigate(
                                if (viewModel.sharedRoleId() == "2") {
                                    Screen.AttendanceScreen.route
                                } else {
                                    Screen.DashboardScreen.route
                                }
                            )
                        } else {
                            time2 = getPunchOutTime()
                            getLocation(context) {
                                viewModel.checkOut(
                                    CheckOutRequest(
                                        viewModel.sharedUserId(),
                                        rosterId,
                                        it.third,
                                        it.second,
                                        it.first,
                                        "",
                                        "",
                                        "",
                                        "",
                                        "",
                                        "",
                                        context.packageName,
                                    )
                                )
                            }
                            isPunchOut = true
                            navController.navigate(
                                if (viewModel.sharedRoleId() == "2") {
                                    Screen.AttendanceScreen.route
                                } else {
                                    Screen.DashboardScreen.route
                                }
                            )
                        }
                    }
            ) {
                Text(
                    text = if (!isPunchIn) "Punch In" else "Punch Out",
                    fontSize = 12.sp,
                    color = RedColor,
                    fontWeight = FontWeight.Normal,
                    modifier = Modifier
                        .padding(top = 16.dp, end = 4.dp, bottom = 16.dp, start = 16.dp)

                )
                Icon(
                    imageVector = Icons.Outlined.ArrowForward,
                    contentDescription = "",
                    tint = RedColor,
                    modifier = Modifier
                        .padding(top = 16.dp, end = 16.dp, bottom = 16.dp, start = 0.dp)
                        .size(16.dp)
                )
            }
        }
    }
}

因此,在单击按钮后,我将新数据发布到API,但它没有触发视图模型状态,并且API调用成功,我如何使UI异步更新?

我尝试使用实时数据而不是流,但没有工作,问题是我的视图模型不知道新数据发布到API,所以我如何通知它?

aiazj4mn

aiazj4mn1#

尝试使用hiltViewModel()而不是AttendanceViewModel,如下所示

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun PunchItem(
    context: Context,
    rosterId: String,
    navController: NavController,
    viewModel: AttendanceViewModel = hiltViewModel()
) {
    val state by viewModel.state.collectAsState()
    val checkInState by viewModel.stateCheck.collectAsState()
    val checkOutState by viewModel.stateCheckOut.collectAsState()

    // rest of code
}

相关问题