当我将RegistrationFrame2()作为直接内容运行时,它可以正常工作
@Composable
fun RegistrationFrame2() {
val context = LocalContext.current
val focusRequester = remember { FocusRequester() }
val tabViewModel = viewModel<TabRowViewModel>()
val loginViewModel = viewModel<LoginViewModel>()
val signupViewModel = viewModel<SignupViewModel>()
val initialText = tabViewModel.getRegistrationInitialText(context)
val focusManager = LocalFocusManager.current
Scaffold(
scaffoldState = tabViewModel.scaffoldState,
snackbarHost = { SnackbarHost(hostState = tabViewModel.scaffoldState.snackbarHostState) },
modifier = Modifier.navigationBarsPadding()
) {
BoxWithConstraints(
contentAlignment = Alignment.TopStart,
modifier = Modifier
.padding(it)
.focusRequester(focusRequester)
.clickable(
indication = null,
interactionSource = remember {
MutableInteractionSource()
}
) {
focusManager.clearFocus()
}
) {
val boxScope = this
RegistrationHeaderWidget(screenHeight = boxScope.maxHeight)
Column(
modifier = Modifier.padding(
top = boxScope.maxHeight.times(0.217f),
start = 17.dp,
end = 17.dp
)
) {
Text(
text = initialText,
modifier = Modifier
.padding(start = 24.dp, end = 24.dp, top = 10.dp)
.animateContentSize(
animationSpec = tween(durationMillis = 350)
),
style = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.Medium,
color = MaterialTheme.digitalColors.textBlackColor
)
)
Box(
modifier = Modifier
.fillMaxWidth()
.animateContentSize(
animationSpec = tween(durationMillis = 250)
)
) {
Crossfade(
targetState = tabViewModel.currentIndex(),
animationSpec = tween(150),
) { currentIndex ->
if (currentIndex == 0) {
LoginPage()
} else {
RegistrationPage()
}
}
}
Spacer(modifier = Modifier.height(15.dp))
if (loginViewModel.state().isLoading || signupViewModel.state().isLoading) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
color = colorResource(id = R.color.primaryColor)
)
}
} else {
val onRegistrationSubmit = remember<(Context, ScaffoldState) -> Unit> {
{ context, scaffoldState ->
if (tabViewModel.currentIndex() == 0) {
loginViewModel.login(context, scaffoldState)
} else {
signupViewModel.validateAndSignup(context, scaffoldState)
}
}
}
val regBtnText = if (tabViewModel.currentIndex() == 0) R.string.loginSubmitButton else R.string.signupSubmitButton
RegistrationSubmitButton(
textResourceId = regBtnText,
onClick = onRegistrationSubmit
)
}
Spacer(modifier = Modifier.height(8.dp))
RegistrationFooterText(
enabled = !loginViewModel.state().isLoading && !signupViewModel.state().isLoading
)
}
}
}
}
但是当我在navHost中作为子对象运行它并将其设置为起始目的地时,
我得到这个错误信息
Process: com.example.application_digital, PID: 5298
java.lang.RuntimeException: Cannot create an instance of class
com.example.application_digital.features.registration.presentation.
login.view_models.LoginViewModel
Caused by: java.lang.InstantiationException:
java.lang.Class<com.example.application_digital.features.
registration.presentation.login.view_models.LoginViewModel>
has no zero argument constructor
这是可组合的navhost
@Composable
fun RootNavigator(navController: NavHostController) {
NavHost(
navController = navController,
startDestination = RootNavigatorRoutes.RegistrationPage.route,
)
{
composable(
route = RootNavigatorRoutes.RegistrationPage.route,
) {
RegistrationFrame2()
}
composable(route = RootNavigatorRoutes.HomePage.route) {
HomePageFrame()
}
composable(route = RootNavigatorRoutes.ForgotPasswordPage.route) {
throw Exception("Unimplemented Route yet")
}
composable(route = RootNavigatorRoutes.OTPVerificationPage.route) {
throw Exception("Unimplemented Route yet")
}
}
}
LoginViewModel代码
@HiltViewModel
class LoginViewModel @Inject constructor(
private val phoneValidator: PhoneValidator,
private val passwordValidator: PasswordValidator,
private val loginUseCase: LoginUseCase,
) :
BaseViewModel() {
private var loginState by mutableStateOf(LoginState())
fun state(): LoginState = loginState
fun dispatch(event: SignupEvent) {
when (event) {
is SignupEvent.PhoneNumberChanged -> {
loginState = loginState.copy(
phoneNumber = event.phoneNumber,
phoneNumberErrorMessage = null,
)
}
is SignupEvent.PasswordChanged -> {
loginState = loginState.copy(
password = event.password,
passwordErrorMessage = null,
)
}
is KeepMeLoggedInEvent -> {
loginState = loginState.copy(
keepMeLoggedIn = event.keepMeLoggedIn
)
}
is SignupEvent.ShowPassword -> {
loginState = loginState.copy(
showPassword = event.visible
)
}
else -> {}
}
}
fun onPhoneNumberChanged(update: String) {
val event = SignupEvent.PhoneNumberChanged(update)
dispatch(event)
}
fun onPasswordChanged(update: String) {
val event = SignupEvent.PasswordChanged(update)
dispatch(event)
}
private fun setIsLoadingTo(update: Boolean) {
if (loginState.isLoading != update) {
loginState = loginState.copy(isLoading = update)
}
}
private fun getProperPasswordErrorMessage(errorMessages:
List<Int>): Int? {
if (errorMessages.isEmpty()) {
return null
}
if (loginState.password.isEmpty()) {
return R.string.passwordIsRequired
}
return R.string.invalidPassword
}
private fun validate(): Boolean {
val phoneErrorMessage =
phoneValidator.validate(loginState.phoneNumber)
val passwordErrorMessage =
passwordValidator.validate(loginState.password)
loginState = loginState.copy(
phoneNumberErrorMessage = if (phoneErrorMessage.isEmpty())
null else phoneErrorMessage.first(),
passwordErrorMessage =
getProperPasswordErrorMessage(passwordErrorMessage)
)
val noPasswordError = loginState.passwordErrorMessage == null
val noPhoneNumberError = loginState.phoneNumberErrorMessage ==
null
return noPasswordError && noPhoneNumberError
}
fun login(context: Context, scaffoldState: ScaffoldState) {
val valid = validate()
Log.d("TT", "login: ")
if (!valid) {
Log.d("TT", "inside if: ")
return
}
Log.d("TT", "beyond if: ")
val payload = LoginPayload(
phoneNumber = loginState.phoneNumber,
password = loginState.password,
)
viewModelScope.launch {
setIsLoadingTo(true)
val response = loginUseCase.execute(payload)
setIsLoadingTo(false)
if (response.hasError()) {
scaffoldState.snackbarHostState.showSnackbar(message =
context.getString(response.errorMessage!!))
return@launch
}
scaffoldState.snackbarHostState.showSnackbar(message =
"process done successfully")
}
}
}
为ViewModel提供factory:
Documentation: https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-factories
viewModel: LoginViewModel= viewModel(
factory = LoginViewModelFactory(param1, param2)
)
柄:
viewModel: LoginViewModel = hiltViewModel<LoginViewModel>()