Mikolaj Leszczynski
02/21/2024, 11:02 AMMikolaj Leszczynski
02/21/2024, 11:02 AMGuilherme Delgado
02/26/2024, 10:51 PMGuilherme Delgado
02/26/2024, 10:51 PMvicky7230
03/14/2024, 12:17 PMvicky7230
03/15/2024, 7:09 AMreduce thread safe?vicky7230
03/16/2024, 10:45 AMGuilherme Delgado
05/09/2024, 3:14 PMGuilherme Delgado
05/09/2024, 3:15 PMappmattus
05/12/2024, 5:07 PMKshitij Patil
06/05/2024, 11:05 AMrunBlocking { } in it's intent { } extension function. runBlocking can throw InterruptedException when thread being used for executing the coroutine gets interrupted for some reason. This results in a runtime crash. Is there any way to get around this crash?twisterrob
07/06/2024, 9:41 PMstefanus ayudha
07/16/2024, 12:48 PMstefanus ayudha
07/16/2024, 12:49 PM@Composable
fun LoginScreen(
/** States **/
viewModel: LoginViewModel = viewModel(),
onSuccess: (LoginResult) -> Unit,
onNavigateToRegistration: () -> Unit,
) {
val context = LocalContext.current
viewModel.collectSideEffect {
when (it) {
is LoginScreenEffect.LoginSuccess -> onSuccess.invoke(LoginResult())
is LoginScreenEffect.ShowToastError -> Toast.makeText(
context,
it.message,
Toast.LENGTH_SHORT
).show()
}
}
Box {
Column(modifier = Modifier.fillMaxSize()) {
Text(text = "Login Screen")
val state by viewModel.collectAsState()
val showLoading by remember(state.showLoginLoading) { derivedStateOf { state.showLoginLoading } }
if (showLoading)
CircularProgressIndicator()
Spacer(modifier = Modifier.weight(1f))
val emailError by remember(state.emailError) { derivedStateOf { state.emailError } }
val passwordError by remember(state.passwordError) { derivedStateOf { state.passwordError } }
LoginForm(
emailError = emailError,
passwordError = passwordError,
onEmailInput = { viewModel.updateEmail(it) },
onPasswordInput = { viewModel.updatePassword(it) }
)
val enableButton by remember(state) { derivedStateOf { state.enableSubmitButton } }
Button(
onClick = { viewModel.submitLogin() },
enabled = enableButton
) {
Text(text = "Login")
}
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = { onNavigateToRegistration.invoke() },
colors = ButtonDefaults.filledTonalButtonColors()
) {
Text(text = "Go To Registration")
}
Spacer(modifier = Modifier.weight(1f))
}
}
}
@Composable
fun LoginForm(
emailError: String,
passwordError: String,
onEmailInput: (String) -> Unit,
onPasswordInput: (String) -> Unit,
) {
Column {
var emailBuffer by remember { mutableStateOf("") }
TextField(
value = emailBuffer,
onValueChange = {
emailBuffer = it
onEmailInput.invoke(it)
},
isError = emailError.isNotBlank(),
)
if (emailError.isNotBlank())
Text(text = emailError, color = MaterialTheme.colorScheme.error)
var passwordBuffer by remember { mutableStateOf("") }
TextField(
value = passwordBuffer,
onValueChange = {
passwordBuffer = it
onPasswordInput.invoke(it)
},
isError = passwordError.isNotBlank()
)
if (passwordError.isNotBlank())
Text(text = passwordError, color = MaterialTheme.colorScheme.error)
}
}
The State:
@Immutable
@optics
data class LoginScreenSate(
val email: String = "",
val password: String = "",
val submitLoginDataState: VmState<LoginCredential> = VmIdle()
) {
companion object
val showLoginLoading: Boolean = submitLoginDataState is VmProcessing
val emailError: String
get() = when {
!email.matches(EmailPattern.toRegex()) -> "Format Salah"
else -> ""
}
val passwordError: String = when {
password.length < 10 -> "Pasword kurang panjang"
else -> ""
}
val enableSubmitButton =
emailError.isBlank() && passwordError.isBlank() && submitLoginDataState.fold(ifProcessing = { false }) { true }
}
The ViewModel
class LoginViewModel : ContainerHost<LoginScreenSate, LoginScreenEffect>, ViewModel() {
override val container: Container<LoginScreenSate, LoginScreenEffect> =
container(LoginScreenSate())
fun updateEmail(email: String) = intent {
reduce {
val lens = LoginScreenSate.email::set
lens(state, email)
}
}
fun updatePassword(password: String) = intent {
reduce {
val lens = LoginScreenSate.password
lens.set(state, password)
}
}
fun submitLogin() = intent {
val lens = LoginScreenSate.submitLoginDataState
reduce { lens.set(state, VmProcessing()) }
// do login stuff
val loginResultState = runBlocking {
delay(3000)
VmSuccess(LoginCredential())
}
// assume success
reduce { lens.set(state, loginResultState) }
postSideEffect(LoginScreenEffect.LoginSuccess(loginResultState.data))
}
}stefanus ayudha
07/16/2024, 12:50 PMGuilherme Delgado
09/13/2024, 9:53 AMMikolaj Leszczynski
10/01/2024, 9:46 AMappmattus
10/02/2024, 11:39 AMappmattus
10/02/2024, 1:56 PMLaurence Muller
10/03/2024, 1:04 PMmacosX64 & macosArm64)? Are there specific dependencies that need to be made compatible?
I got a macos desktop app using compose that builds in kotlin native (not jvm) and would love to use orbit in itdave08
10/07/2024, 2:03 PMDaniel (danflo)
10/25/2024, 6:46 PMKrzysiek Skorcz
11/26/2024, 11:39 AMSonu Sourav
12/04/2024, 1:16 PMfun example() {
intent {
fun1()
}
Log.d("sonusourav","intent 1")
intent {
fun2()
}
Log.d("sonusourav","intent 2")
intent {
fun3()
}
Log.d("sonusourav","intent 3")
}
Will the three functions be launched in parallel like launch? Sorry if this is very naive question, couldn't found a clear answer for this written anywhereStew Boling
03/03/2025, 1:53 AMappmattus
05/15/2025, 9:35 PMappmattus
05/22/2025, 8:41 AMappmattus
05/23/2025, 4:48 PMJacob Rhoda
05/23/2025, 8:42 PMclass LoginViewModel(
private val authController: AuthenticationController,
) : ViewModelContainerHost<LoginState, LoginSideEffect>() {
override val container: Container<LoginState, LoginSideEffect> =
container(
initialState = LoginState.UNINITIALIZED,
) {
observeAuthState()
}
private fun observeAuthState() =
intent(registerIdling = false) {
repeatOnSubscription {
authController.authState
.collect { state ->
reduce {
when (state) {
AuthState.UNINITIALIZED -> LoginState.UNINITIALIZED
AuthState.UNAUTHORIZED -> LoginState.UNAUTHORIZED
AuthState.AUTHORIZED -> LoginState.AUTHORIZED
AuthState.ERROR -> LoginState.ERROR
}
}
}
}
}
}
LoginViewModelTests.kt
class LoginViewModelTests {
val authService =
mock<AuthService>(MockMode.strict) {
}
@Test
fun testConstructorAuthorized() =
runTest {
everySuspend { authService.isAuthorized() } returns true
val authController = AuthenticationController(Logger, authService)
LoginViewModel(authController).test(this, LoginState.UNINITIALIZED) {
runOnCreate()
expectState(LoginState.AUTHORIZED)
}
}
}
Result:
Info: Updating auth state to AUTHORIZED
Timed out waiting for remaining intents to complete for 1s
org.orbitmvi.orbit.test.OrbitTimeoutCancellationException: Timed out waiting for remaining intents to complete for 1s
...
The weird thing is that if I expectState(LoginState.UNAUTHORIZED) , then it fails because it gets AUTHORIZED.alexsullivan114
10/01/2025, 4:24 PMjava.lang.IllegalStateException: Method setCurrentState must be called on the main thread exception when trying to navigate because the block passed to collectSideEffect isn't being run on the main thread. Anyone faced this before or have any insights?