I want to create an interactive column. For that, it's necessary to observeto state. But the lazy column doesn't display change.
State:
data class TournamentState(
var tournament:Tournament= Tournament(0,"",TounamentType.SWISS_ROUND, mutableStateListOf()),
var tournamentScreen: TournamentScreen=TournamentScreen.PLAYERS
)
View model:
class TournamentViewModel(
private val tournamentRepository: TournamentRepository,
private val playerRepository: PlayerRepository
) : ViewModel(){
private val _uiState = MutableStateFlow(TournamentState())
val uiState: StateFlow<TournamentState> = _uiState.asStateFlow()
fun initData(tournamentId:Int){
viewModelScope.launch {
_uiState.value.tournament=
tournamentRepository.getStream(tournamentId).first()?.toModel() ?: returnDataTest()
}
}
fun addUser(){
_uiState.value.tournament.players.add(
Player(
maxId()+1,
"Player"+(_uiState.value.tournament.players.size+1)
)
)
}
fun removeUser(player: Player){
_uiState.value.tournament.players.remove(player)
}
fun changePage(tournamentScreen: TournamentScreen){
_uiState.value=_uiState.value.copy(tournamentScreen = tournamentScreen)
}
1st activity:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TounamentActivity(
tournamentId:Int,
tournamentViewModel: TournamentViewModel = viewModel(factory = AppViewModelProvider.factory)
){
tournamentViewModel.initData(tournamentId)
val tournamentState by tournamentViewModel.uiState.collectAsState()
Scaffold (
topBar = {
TopAppBar(title = {
Row(horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxWidth()) {
IconButton(onClick = { tournamentViewModel.changePage(TournamentScreen.PLAYERS) }) {
Icon(imageVector = Icons.Filled.AccountCircle, contentDescription = "Players", tint = Color.Black)
}
IconButton(onClick = { tournamentViewModel.changePage(TournamentScreen.DRAFT) }) {
Icon(imageVector = Icons.Filled.List, contentDescription = "Draft", tint = Color.Black)
}
IconButton(onClick = { tournamentViewModel.changePage(TournamentScreen.RESULT) }) {
Icon(imageVector = Icons.Filled.Star, contentDescription = "Result", tint = Color.Black)
}
}
}, colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = Color.Cyan))
},
){ innerPadding->
Card(modifier = with(Modifier){
fillMaxSize()
.padding(innerPadding)
.paint(
painterResource(R.drawable.androidparty),
contentScale = ContentScale.Crop)
}
) {
tournamentState.tournamentScreen.let {
when(it){
TournamentScreen.PLAYERS-> PlayersActivity(tournamentViewModel = tournamentViewModel)
TournamentScreen.DRAFT-> Text(text = "draft Not Implement")
TournamentScreen.RESULT-> Text(text = "result Not Implement")
}
}
}
}
}
2nd activity:
fun PlayersActivity(
tournamentViewModel: TournamentViewModel
){
val tournamentUiState by tournamentViewModel.uiState.collectAsState()
Column (modifier = Modifier
.fillMaxSize()){
PlayersBase(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.9f), itemsList = tournamentUiState.tournament.players
) { player: Player -> tournamentViewModel.removeUser(player) }
Button(
onClick = {
tournamentViewModel.addUser()
},
modifier = Modifier
.fillMaxSize()
.padding(10.dp),
shape = RoundedCornerShape(10.dp)
) {
Image(painter = painterResource(R.drawable.plus), contentDescription = "Add player")
}
}
}
@Composable
fun PlayersBase(
modifier: Modifier, itemsList: List<Player>, remove: (Player) -> Unit
) {
LazyColumn(modifier = modifier
.fillMaxSize()
.padding(0.dp, 10.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)){
items(items = itemsList, key = {it.id}){
PlayerCard(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.08f),
player = it,
remove = {
remove(it)
}
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PlayerCard(modifier: Modifier = Modifier,player: Player, remove:() -> Unit){
var name by remember { mutableStateOf(player.name) }
Card(modifier = modifier
.fillMaxSize()
.padding(20.dp, 0.dp),) {
Row (modifier = Modifier.fillMaxSize()){
TextField(value = name, onValueChange = {name=it},
modifier= Modifier
.fillMaxHeight()
.fillMaxWidth(0.8f),
textStyle = TextStyle.Default.copy(fontSize = 20.sp)
)
IconButton(onClick = { remove() }, modifier = Modifier.fillMaxSize()) {
Icon(imageVector = Icons.Filled.Clear, contentDescription = "Remove player", tint = Color.Black)
}
}
}
}
What is solution to force observe state's data? I don't know what I'm doing wrong so I have no clue. If you have any questions don't hesitate to ask.
Your problems are in addUser
and removeUser
functions because
you modify the same instance of TournamentState so StateFlow still sees it as the same value
fun addUser() {
_uiState.value = _uiState.value.copy (
tournament = _uiState.value.tournament.copy (
players = _uiState.value.tournament.players.toMutableList().apply {
add(Player(maxId() + 1, "Player${_uiState.value.tournament.players.size + 1}"))
}
)
)
}
fun removeUser(player: Player) {
_uiState.value = _uiState.value.copy (
tournament = _uiState.value.tournament.copy (
players = _uiState.value.tournament.players.toMutableList().apply {
remove(player)
}
)
)
}