fun Conversation(mainViewModel: MainViewModel) {
var stat = mainViewModel.getTweet().observeAsState()
val listState = rememberScrollState()
stat?.let {
if( stat.value == null){
Log.d("DAD","DASSA")
// null 값이면 로딩 에니메이션 뜨게 만들면 좋을듯>????
}
else{
Log.d("DAD","됐다")
var author = it.value!!.first
var messages = it.value!!.second
LazyColumn(modifier = Modifier.verticalScroll(listState).height(100.dp)) {
items(messages) { message ->
MessageCard(author, message)
}
}
}
}
}
In this composable, it seems like the composable "DID NOT" update value of mutableLivedata( mainViewModel.getTweet()). but I confuse about below code
fun InfoCard(mainViewModel: MainViewModel){
val Name = mainViewModel.getData().observeAsState()
Name.value?.let { it ->
Row(modifier = Modifier
.fillMaxWidth()
.background(shape = RoundedCornerShape(6.dp), color = Color.LightGray)
.padding(30.dp)
.height(40.dp),
verticalAlignment = Alignment.CenterVertically
) {
Spacer(modifier = Modifier.height(15.dp))
Text(it.get(0), modifier = Modifier.width(200.dp), textAlign = TextAlign.Center)
Spacer(modifier = Modifier.width(30.dp))
Text(text = it.get(1))
}
}
}
it also composable and use value of mutableLivedata( mainViewModel.getData())
But IT UPDATE VERY WELL..
what is a difference and How can I update first composable..
Self solution for someone like me.
Change
val Name = mainViewModel.getData().observeAsState()
to
val Name by mainViewModel.getData().observeAsState()
Related
I have a list with 10 items one of them have this elements "rankingCurrentPlace", "rankingPastPlace" and "isUser:true".
What i need to do its an animation on the lazycolumn if the api esponse is like this
"isUser:true", "rankingPastPlace:3" , "rankingCurrentPlace:7"
i need to show an animation in the list where the row starts in the third place and descend to the seventh place
is there a way to do this?
this is what I actually have
LazyColumn(
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
) {
items(
items = leaderboard,
key = { leaderBoard ->
leaderBoard.rankingPlace
}
) { leaderBoard ->
RowComposable( modifier = Modifier
.fillMaxWidth(),
topicsItem = leaderBoard,)
}
This answer works except when swapping first item with any item even with basic swap function without animation. I think it would be better to ask a new question about why swapping first item doesn't work or if it is bug. Other than that works as expected. If you need to move to items that are not in screen you can lazyListState.layoutInfo.visibleItemsInfo and compare with initial item and scroll to it before animation
1.Have a SnapshotStateList of data to trigger recomposition when we swap 2 items
class MyData(val uuid: String, val value: String)
val items: SnapshotStateList<MyData> = remember {
mutableStateListOf<MyData>().apply {
repeat(20) {
add(MyData( uuid = UUID.randomUUID().toString(), "Row $it"))
}
}
}
2.Function to swap items
private fun swap(list: SnapshotStateList<MyData>, from: Int, to: Int) {
val size = list.size
if (from in 0 until size && to in 0 until size) {
val temp = list[from]
list[from] = list[to]
list[to] = temp
}
}
3.Function to swap items one by one. There is a bug with swapping first item. Even if it's with function above when swapping first item other one moves up without showing animation via Modififer.animateItemPlacement().
#Composable
private fun animatedSwap(
lazyListState: LazyListState,
items: SnapshotStateList<MyData>,
from: Int,
to: Int,
onFinish: () -> Unit
) {
LaunchedEffect(key1 = Unit) {
val difference = from - to
val increasing = difference < 0
var currentValue: Int = from
repeat(abs(difference)) {
val temp = currentValue
if (increasing) {
currentValue++
} else {
currentValue--
}
swap(items, temp, currentValue)
if (!increasing && currentValue == 0) {
delay(300)
lazyListState.scrollToItem(0)
}
delay(350)
}
onFinish()
}
}
4.List with items that have Modifier.animateItemPlacement()
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
state = lazyListState,
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(
items = items,
key = {
it.uuid
}
) {
Row(
modifier = Modifier
.animateItemPlacement(
tween(durationMillis = 200)
)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.size(50.dp),
painter = painterResource(id = R.drawable.landscape1),
contentScale = ContentScale.FillBounds,
contentDescription = null
)
Spacer(modifier = Modifier.width(10.dp))
Text(it.value, fontSize = 18.sp)
}
}
}
Demo
#OptIn(ExperimentalFoundationApi::class)
#Composable
private fun AnimatedList() {
Column(modifier = Modifier.fillMaxSize()) {
val items: SnapshotStateList<MyData> = remember {
mutableStateListOf<MyData>().apply {
repeat(20) {
add(MyData(uuid = UUID.randomUUID().toString(), "Row $it"))
}
}
}
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
state = lazyListState,
contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
items(
items = items,
key = {
it.uuid
}
) {
Row(
modifier = Modifier
.animateItemPlacement(
tween(durationMillis = 200)
)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
modifier = Modifier
.clip(RoundedCornerShape(10.dp))
.size(50.dp),
painter = painterResource(id = R.drawable.landscape1),
contentScale = ContentScale.FillBounds,
contentDescription = null
)
Spacer(modifier = Modifier.width(10.dp))
Text(it.value, fontSize = 18.sp)
}
}
}
var fromString by remember {
mutableStateOf("7")
}
var toString by remember {
mutableStateOf("3")
}
var animate by remember { mutableStateOf(false) }
if (animate) {
val from = try {
Integer.parseInt(fromString)
} catch (e: Exception) {
0
}
val to = try {
Integer.parseInt(toString)
} catch (e: Exception) {
0
}
animatedSwap(
lazyListState = lazyListState,
items = items,
from = from,
to = to
) {
animate = false
}
}
Row(modifier = Modifier.fillMaxWidth()) {
TextField(
value = fromString,
onValueChange = {
fromString = it
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
TextField(
value = toString,
onValueChange = {
toString = it
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
Button(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
onClick = {
animate = true
}
) {
Text("Swap")
}
}
}
Edit: Animating with Animatable
Another method for animating is using Animatable with Integer vector.
val IntToVector: TwoWayConverter<Int, AnimationVector1D> =
TwoWayConverter({ AnimationVector1D(it.toFloat()) }, { it.value.toInt() })
val coroutineScope = rememberCoroutineScope()
val animatable = remember { Animatable(0, IntToVector) }
And can be used as
private fun alternativeAnimate(
from: Int,
to: Int,
coroutineScope: CoroutineScope,
animatable: Animatable<Int, AnimationVector1D>,
items: SnapshotStateList<MyData>
) {
val difference = from - to
var currentValue: Int = from
coroutineScope.launch {
animatable.snapTo(from)
animatable.animateTo(to,
tween(350 * abs(difference), easing = LinearEasing),
block = {
val nextValue = this.value
if (abs(currentValue -nextValue) ==1) {
swap(items, currentValue, nextValue)
currentValue = nextValue
}
}
)
}
}
on button click, i'm getting values from TextField fo i convert from String
Button(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
onClick = {
val from = try {
Integer.parseInt(fromString)
} catch (e: Exception) {
0
}
val to = try {
Integer.parseInt(toString)
} catch (e: Exception) {
0
}
alternativeAnimate(from, to, coroutineScope, animatable, items)
}
) {
Text("Swap")
}
Result
I suggest you to get your items from a data class. If your other items does not contain the variables you mentioned you can make them nullable in data class and put a condition checker in your lazycolumn
Like this
data class Items(
val otherItems: Other,
val rankingCurrentPlace: Int?,
val rankingLastPlace: Int?,
val isUser: Boolean?
)
Then you can make a list from this data class and pass it to lazycolumn
LazyColumn{
items(list){
(elements with condition)
}
}
A quick preface, I'm probably overlooking something rather simple but I'm willing to admit I'm not 100% sure of what I'm doing! The project I'm working on is a just for fun project to tinker around with Kotlin and Jetpack Compose by making an Android application.
So to my problem, I'm trying to use a DatePicker dialog using the vanpra compose-material-dialogs. I can get the dialog datepicker to appear but I can't get my selected date to return to my textfield I made after selecting the date from the dialog. It seems I'm running in to some issues updating the viewmodel correctly. Any help at all is greatly appreciated! Thank you!
Below is my DatePicker composable:
#Composable
fun TradeDatePicker(
value: LocalDate,
onValueChanged: (LocalDate) -> Unit,
modifier:Modifier = Modifier,
) {
val dialogState = rememberMaterialDialogState()
MaterialDialog(
dialogState = dialogState,
buttons = {
positiveButton("OK")
negativeButton("CANCEL")
},
backgroundColor = MaterialTheme.colors.background
) {
this.datepicker(onDateChange = onValueChanged)
}
Box(
modifier = modifier
.border(
width = 1.dp,
color = MaterialTheme.colors.onPrimary,
shape = RoundedCornerShape(50),
)
.clip(ButtonShape) //clip keeps animation within box borders
.clickable {
dialogState.show()
}
){
Row (
modifier = Modifier
.padding(16.dp),
){
Text(
text = "Trade Date",
modifier = Modifier
.weight(1F),
)
Icon(
Icons.Default.DateRange,
contentDescription = "Select Date",
)
}
}
}
Here is my screen where you select the datepicker field to display the datepicker dialog, it seems I'm trying to pass a LocalDate to the viewmodel but it requires a long(that's how I thought the date got returned from the dialog, as a long):
#Composable
fun AddEditTradeScreen(
navController: NavController,
viewModel: AddEditTradeViewModel = hiltViewModel()
){
val tradesymbolState = viewModel.tradeSymbol.value
val tradequantState = viewModel.tradeQuantity.value
val tradeDateState = viewModel.tradeDate
val scaffoldState = rememberScaffoldState()
val value: LocalDate
LaunchedEffect(key1 = true){
viewModel.eventFlow.collectLatest { event ->
when(event){
is AddEditTradeViewModel.UiEvent.ShowSnackbar ->{
scaffoldState.snackbarHostState.showSnackbar(
message = event.message
)
}
is AddEditTradeViewModel.UiEvent.SaveTrade -> {
navController.navigateUp()
}
}
}
}
Scaffold(
floatingActionButton = {
FloatingActionButton(
onClick = {
viewModel.onEvent(AddEditTradeEvent.SaveTrade)
},
backgroundColor = MaterialTheme.colors.primary
) {
Icon(imageVector = Icons.Default.Save, contentDescription = "Save Trade")
}
},
scaffoldState = scaffoldState
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Enter Your Tradeeee",
style = MaterialTheme.typography.h4,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(20.dp))
TransparentHintTextField(
text = tradesymbolState.text,
hint = tradesymbolState.hint,
onValueChange = {
viewModel.onEvent(AddEditTradeEvent.EnteredSymbol(it))
},
onFocusChange = {
viewModel.onEvent(AddEditTradeEvent.ChangeSymbolFocus(it))
},
isHintVisible = tradesymbolState.isHintVisible,
singleLine = true,
textStyle = MaterialTheme.typography.h5
)
Spacer(modifier = Modifier.height(16.dp))
TransparentHintTextField(
text = tradequantState.text,
hint = tradequantState.hint,
onValueChange = {
viewModel.onEvent(AddEditTradeEvent.EnteredQuantity(it))
},
onFocusChange = {
viewModel.onEvent(AddEditTradeEvent.ChangeQuantityFocus(it))
},
isHintVisible = tradequantState.isHintVisible,
textStyle = MaterialTheme.typography.body1,
//modifier = Modifier.fillMaxHeight()
)
Spacer(modifier = Modifier.height(16.dp))
TradeDatePicker(
value = LocalDate.now(),
onValueChanged = {
viewModel.onEvent(AddEditTradeEvent.EnteredTradeDate(it))
},
modifier = Modifier
.fillMaxWidth()
)
}
}
}
This is my viewmodel that I'm trying to update:
#HiltViewModel
class AddEditTradeViewModel #Inject constructor(
private val tradeUseCases: TradeUseCases,
savedStateHandle: SavedStateHandle
) : ViewModel(){
private val _tradeSymbol = mutableStateOf(
TradeTextFieldState(
hint = "Enter Trade Symobol"
))
val tradeSymbol: State<TradeTextFieldState> = _tradeSymbol
private val _tradeQuantity = mutableStateOf(
TradeTextFieldState(
hint = "Enter Number of Shares"
))
val tradeQuantity: State<TradeTextFieldState> = _tradeQuantity
private var _tradeDate : String? by remember {
mutableStateOf(null)
}
val tradeDate = { date: Long? ->
_tradeDate = date?.toString()?:""
}
private val _eventFlow = MutableSharedFlow<UiEvent>()
val eventFlow = _eventFlow.asSharedFlow()
private var currentTradeId: Int? = null
init{
savedStateHandle.get<Int>("tradeId")?.let { tradeId ->
if (tradeId != -1){
viewModelScope.launch {
tradeUseCases.getTrade(tradeId)?.also { trade ->
currentTradeId = trade.id
_tradeSymbol.value = tradeSymbol.value.copy(
text = trade.tradeSymb,
isHintVisible = false
)
_tradeQuantity.value = tradeQuantity.value.copy(
text = trade.tradequant,
isHintVisible = false
)
}
}
}
}
}
fun onEvent(event: AddEditTradeEvent){
when(event){
is AddEditTradeEvent.EnteredSymbol -> {
_tradeSymbol.value = tradeSymbol.value.copy(
text = event.value
)
}
is AddEditTradeEvent.ChangeSymbolFocus -> {
_tradeSymbol.value = tradeSymbol.value.copy(
isHintVisible = !event.focusState.isFocused &&
tradeSymbol.value.text.isBlank()
)
}
is AddEditTradeEvent.EnteredQuantity -> {
_tradeQuantity.value = tradeQuantity.value.copy(
text = event.value
)
}
is AddEditTradeEvent.ChangeQuantityFocus -> {
_tradeQuantity.value = tradeQuantity.value.copy(
isHintVisible = !event.focusState.isFocused &&
_tradeQuantity.value.text.isBlank()
)
}
is AddEditTradeEvent.EnteredTradeDate -> {
_tradeDate.value = tradeDate.value.copy(
text = event.value
)
}
is AddEditTradeEvent.SaveTrade -> {
viewModelScope.launch{
try {
tradeUseCases.addTrade(
Trade(
tradeSymb = tradeSymbol.value.text,
tradequant= tradeQuantity.value.text,
timestamp = System.currentTimeMillis(),
//tradestatus = "Long",
//color = MaterialTheme.colors.primary.green
id = currentTradeId
)
)
_eventFlow.emit(UiEvent.SaveTrade)
} catch (e: InvalidTradeException){
_eventFlow.emit(
UiEvent.ShowSnackbar(
message = e.message ?: "Couldn't save trade"
)
)
}
}
}
}
}
}
Lastly this is the event sealed class, not sure I'm using this correctly but in here for the "EnteredTradeDate" I declare it as a long and I'm not sure this is correct since I'm having trouble passing it from the screen to the viewmodel.
sealed class AddEditTradeEvent {
data class EnteredSymbol(val value: String): AddEditTradeEvent()
data class ChangeSymbolFocus(val focusState: FocusState): AddEditTradeEvent()
data class EnteredQuantity(val value: String): AddEditTradeEvent()
data class ChangeQuantityFocus(val focusState: FocusState): AddEditTradeEvent()
data class EnteredTradeDate(val value: Long) : AddEditTradeEvent()
data class ChangeDateFocus(val focusState: FocusState): AddEditTradeEvent()
object SaveTrade: AddEditTradeEvent()
}
I try to create an quiz app with Jetpack Compose but I need some response to understand how Jetpack Compose recompose after some change into a mutableStateOf.
After choosing a response I call the function into my viewModel to change the state of response to change background color. But if I don't add this code
state = state.copy(
isLoading = true
)
The background color doesn't change, why ?
Here complete code of Composable question's screen :
#Composable
fun QuizzScreen(
viewModel: QuizzViewModel,
modifier: Modifier = Modifier
) {
val state = remember {
viewModel.state
}
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Center
) {
if (state.isLoading) {
CircularProgressIndicator()
} else if (state.error != null) {
Text(
text = state.error,
color = MaterialTheme.colors.error
)
} else {
Column(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = Html.fromHtml(state.question, Html.FROM_HTML_MODE_LEGACY).toString(),
color = MaterialTheme.colors.onSurface,
fontSize = 32.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
)
Spacer(modifier = Modifier.height(48.dp))
for (response: Response in state.responses) {
Response(response) {
viewModel.checkAnwser(response)
}
}
}
}
}
}
#Composable
fun Response(
response: Response,
onClick: () -> Unit
) {
Spacer(modifier = Modifier.height(24.dp))
Button(
colors = ButtonDefaults.buttonColors(backgroundColor = if (response.state == ResponseStatus.Init) MaterialTheme.colors.surface else if (response.state == ResponseStatus.Correct) Color.Green else Color.Red),
onClick = onClick
) {
Text(
text = Html.fromHtml(response.response, Html.FROM_HTML_MODE_LEGACY).toString(),
fontSize = 24.sp,
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
)
}
}
And now my ViewModel
class QuizzViewModel(
val repository: QuizzRepository
) : ViewModel() {
private val _isLoading = MutableStateFlow(true)
var isLoading = _isLoading.asStateFlow()
var state by mutableStateOf(QuizzState())
init {
viewModelScope.launch {
loadQuizz()
}
}
suspend fun loadQuizz() {
state = state.copy(isLoading = true)
when (val quizzLocal = repository.getLocalQuizz()) {
is Resource.Success -> {
val quizz: Quizz = quizzLocal.data!!
state = state.copy(
question = quizz.question,
responses = quizz.responses.map { response ->
Response(
response,
ResponseStatus.Init
)
},
correctAnswer = quizz.correctAnswer,
isLoading = false
)
}
is Resource.Error -> {
state = state.copy(
isLoading = false,
error = quizzLocal.message!!
)
}
else -> Unit
}
_isLoading.value = false
}
fun checkAnwser(response: Response) {
val correctAnwser = (response.response == state.correctAnswer)
state.responses.map {
if (!correctAnwser && it == response) {
it.state = ResponseStatus.Error
}
if (it.response == state.correctAnswer) {
it.state = ResponseStatus.Correct
}
}
// If i comment this,
// not recompose
state = state.copy(
isLoading = true
)
}
}
I think the problem is that you are caching ViewModel's state by using remember{viewModel.state}
#Composable
fun QuizzScreen(
viewModel: QuizzViewModel,
modifier: Modifier = Modifier
) {
val state = remember {
viewModel.state
}
Using remember{} caches the state to the default value which makes the state remain unchanged even with recomposition.
Instead use val state = viewModel.state
var noteListState by remember { mutableStateOf(listOf("Drink water", "Walk")) }
This is my list of items. Any leads would be appreciated
To Get a Main UI with list and searchview
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainScreen()
}
}
For Main()
#Composable
fun MainScreen() {
val textState = remember { mutableStateOf(TextFieldValue("")) }
Column {
SearchView(textState)
ItemList(state = textState)
}
}
For Serchview and list
#Composable
fun SearchView(state: MutableState<TextFieldValue>) {
TextField(
value = state.value,
onValueChange = { value ->
state.value = value
},
modifier = Modifier.fillMaxWidth(),
textStyle = TextStyle(color = Color.White, fontSize = 18.sp),
leadingIcon = {
Icon(
Icons.Default.Search,
contentDescription = "",
modifier = Modifier
.padding(15.dp)
.size(24.dp)
)
},
trailingIcon = {
if (state.value != TextFieldValue("")) {
IconButton(
onClick = {
state.value =
TextFieldValue("") // Remove text from TextField when you press the 'X' icon
}
) {
Icon(
Icons.Default.Close,
contentDescription = "",
modifier = Modifier
.padding(15.dp)
.size(24.dp)
)
}
}
},
singleLine = true,
shape = RectangleShape, // The TextFiled has rounded corners top left and right by default
colors = TextFieldDefaults.textFieldColors(
textColor = Color.White,
cursorColor = Color.White,
leadingIconColor = Color.White,
trailingIconColor = Color.White,
backgroundColor = MaterialTheme.colors.primary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
)
)
}
#Composable
fun ItemList(state: MutableState<TextFieldValue>) {
val items by remember { mutableStateOf(listOf("Drink water", "Walk")) }
var filteredItems: List<String>
LazyColumn(modifier = Modifier.fillMaxWidth()) {
val searchedText = state.value.text
filteredItems = if (searchedText.isEmpty()) {
items
} else {
val resultList = ArrayList<String>()
for (item in items) {
if (item.lowercase(Locale.getDefault())
.contains(searchedText.lowercase(Locale.getDefault()))
) {
resultList.add(item)
}
}
resultList
}
items(filteredItems) { filteredItem ->
ItemListItem(
ItemText = filteredItem,
onItemClick = { /*Click event code needs to be implement*/
}
)
}
}
}
#Composable
fun ItemListItem(ItemText: String, onItemClick: (String) -> Unit) {
Row(
modifier = Modifier
.clickable(onClick = { onItemClick(ItemText) })
.background(colorResource(id = R.color.purple_700))
.height(57.dp)
.fillMaxWidth()
.padding(PaddingValues(8.dp, 16.dp))
) {
Text(text = ItemText, fontSize = 18.sp, color = Color.White)
}
}
For More Detailed answer you can refer this link
Please could you explain more, this question is a bit short and unclear.
One way (i think you could do) is just to loop through the list and check if a element (of your list) contains that text.
val filterPattern = text.toString().lowercase(Locale.getDefault()) // text you are looking for
for (item in copyList) { // first make a copy of the list
if (item.name.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item) // if the item contains that text add it to the list
}
}
... // here you then override noteListState
After you have made the filteredList you can just override the noteListState. Make sure to make a copy of that list beforehand and set it back when you don't want to show the filtered results.
My composable is recomposing endlessly after flow collect and navigating to a new screen.
I can't understand why.
I'm using Firebase for Auth with Email and Password.
I had to put some Log.i to test my function and my composable, and yes, my Main composable (SignUp) is recomposing endlessly after navigating.
ViewModel
// Firebase auth
private val _signUpState = mutableStateOf<Resources<Any>>(Resources.success(false))
val signUpState: State<Resources<Any>> = _signUpState
fun firebaseSignUp(email: String, password: String) {
job = viewModelScope.launch(Dispatchers.IO) {
firebaseAuth.firebaseSignUp(email = email, password = password).collect {
_signUpState.value = it
Log.i("balito", "polipop")
}
}
}
fun stop() {
job?.cancel()
}
SignUp
#Composable
fun SignUp(
navController: NavController,
signUpViewModel: SignUpViewModel = hiltViewModel()
) {
val localFocusManager = LocalFocusManager.current
Log.i("salut", "salut toi")
Column(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding()
.padding(16.dp)
.background(color = PrimaryColor)
) {
BackButton(navController = navController)
Spacer(modifier = Modifier.height(30.dp))
Text(
text = stringResource(id = R.string.sinscrire),
fontFamily = visby,
fontWeight = FontWeight.SemiBold,
fontSize = 28.sp,
color = Color.White
)
Spacer(modifier = Modifier.height(2.dp))
Text(
text = stringResource(R.string.prenez_votre_sante_en_main),
fontFamily = visby,
fontWeight = FontWeight.SemiBold,
fontSize = 20.sp,
color = Grey
)
Spacer(modifier = Modifier.height(20.dp))
Email(signUpViewModel = signUpViewModel, localFocusManager = localFocusManager)
Spacer(modifier = Modifier.height(16.dp))
Password(signUpViewModel = signUpViewModel, localFocusManager = localFocusManager)
Spacer(modifier = Modifier.height(30.dp))
Button(value = stringResource(R.string.continuer), type = Type.Valid.name) {
localFocusManager.clearFocus()
signUpViewModel.firebaseSignUp(signUpViewModel.emailInput.value, signUpViewModel.passwordInput.value)
}
Spacer(modifier = Modifier.height(16.dp))
Button(value = stringResource(R.string.inscription_avec_google), type = Type.Other.name) {
}
Spacer(modifier = Modifier.weight(1f))
Box(
modifier = Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
ClickableTextInfo(stringResource(id = R.string.deja_un_compte_se_connecter), onClick = {})
}
}
Response(navController = navController, signUpViewModel = signUpViewModel)
DisposableEffect(key1 = signUpViewModel.signUpState.value == Resources.success(true)) {
onDispose {
signUpViewModel.stop()
Log.i("fin", "fin")
}
}
}
#Composable
private fun Response(
navController: NavController,
signUpViewModel: SignUpViewModel
) {
when (val response = signUpViewModel.signUpState.value) {
is Resources.Loading<*> -> {
//WaitingLoaderProgress(loading = true)
}
is Resources.Success<*> -> {
response.data.also {
Log.i("lolipop", "lolipopi")
if (it == true) {
navController.navigate(Screen.SignUpConfirmation.route)
}
}
}
is Resources.Failure<*> -> {
// response.throwable.also {
// Log.d(TAG, it)
// }
}
}
}
During navigation transition recomposition happens multiple times because of animations, and you call navController.navigate on each recomposition.
You should not cause side effects or change the state directly from the composable builder, because this will be performed on each recomposition, which is not expected in cases like animation.
Instead you should use side effects. In your case, LaunchedEffect should be used.
if (response.data) {
LaunchedEffect(Unit) {
Log.i("lolipop", "lolipopi")
navController.navigate(Screen.SignUpConfirmation.route)
}
}