sorry for the English, I'm using a translator.i'm just a beginner, please consider this when replying.i know the code may not be the best. this is just a start. I'm using the viewmodel to create a template and then use this template in several instances,the problem is that when you enter a value in the field for item 1, the other field is also filled at the same time. I'd like to know the error, and why the code isn't working even though I've created different instances.
main file:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MercadoTheme {
Aplicacao()
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Aplicacao(modifier: Modifier = Modifier) {
val item1VM: ItemInputViewModel = viewModel()
val item2VM: ItemInputViewModel = viewModel()
val item1Layout = ItemInput()
val item1State by item1VM.itemState.collectAsState()
val item2Layout = ItemInput()
val item2State by item2VM.itemState.collectAsState()
Scaffold(modifier.fillMaxSize(),
topBar = {
TopAppBar(
modifier = modifier.fillMaxWidth(),
title = {
Text(
modifier = modifier.fillMaxWidth(),
text = "Comparador de Preços",
color = CompareTopAppBarTextColor,
fontWeight = FontWeight.Bold,
fontSize = 25.sp,
textAlign = TextAlign.Center,
)
},
colors = TopAppBarDefaults.topAppBarColors(CompareTopAppBarBackgroundColor)
)
},
content = { innerPadding ->
Column(modifier = Modifier.padding(innerPadding)) {
item1Layout.Insert(
itemData = item1State,
onValorChange = { item1State.onValorChange(it) },
onQuantidadeChange = { item1State.onQuantidadeChange(it) },
onUnidadeChange = { item1State.onUnidadeChange(it) },
onExpandedChange = { item1State.onExpandedChange(it) }
)
//---------------
Spacer(modifier = Modifier.size(20.dp))
item2Layout.Insert(
itemData = item2State,
onValorChange = { item2State.onValorChange(it) },
onQuantidadeChange = { item2State.onQuantidadeChange(it) },
onUnidadeChange = { item2State.onUnidadeChange(it) },
onExpandedChange = { item2State.onExpandedChange(it) }
)
}
})
}
UI file:
class ItemInput {
@Composable
fun Insert(
modifier: Modifier = Modifier,
itemData: ItemData,
onValorChange: (String) -> Unit = {},
onQuantidadeChange: (String) -> Unit = {},
onUnidadeChange: (String) -> Unit = {},
onExpandedChange: (Boolean) -> Unit = {}
) {
var textFieldSize by remember { mutableStateOf(Size.Zero) }
val unidadesOptions = listOf("-", "Kg", "g", "L", "mL", "m", "Cm", "mm")
Column {
Text(
modifier = modifier
.padding(10.dp)
.align(Alignment.CenterHorizontally),
text = itemData.name,
style = MaterialTheme.typography.titleLarge
)
Row(
modifier = modifier.padding(10.dp),
content = {
// Valor
OutlinedTextField(
modifier = Modifier.weight(3f),
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
label = { Text("Valor") },
value = itemData.valor,
onValueChange = onValorChange
)
Spacer(modifier = Modifier.width(8.dp))
// Quantidade
OutlinedTextField(
modifier = Modifier.weight(3f),
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
label = { Text("Quantidade") },
value = itemData.quantidade,
onValueChange = onQuantidadeChange
)
Spacer(modifier = Modifier.width(8.dp))
// Unidade
OutlinedTextField(
readOnly = true,
value = itemData.unidade,
onValueChange = {},
label = { Text("Unidade") },
trailingIcon = {
Icon(
imageVector = Icons.Default.ArrowDropDown,
contentDescription = "Open dropdown",
modifier = Modifier.size(24.dp)
)
},
modifier = Modifier
.weight(2f)
.onGloballyPositioned { coordinates ->
textFieldSize = coordinates.size.toSize()
}
.clickable { onExpandedChange(true) }
)
// Menu
DropdownMenu(
expanded = itemData.expanded,
onDismissRequest = { onExpandedChange(false) },
modifier = Modifier
.width(textFieldSize.width.dp)
.padding(top = 8.dp)
) {
unidadesOptions.forEach { item ->
DropdownMenuItem(
text = { Text(item) },
onClick = {
onUnidadeChange(item)
onExpandedChange(false)
}
)
}
}
}
)
}
}
}
VM file:
data class ItemData(
val name: String = "Item",
val valor: String = "",
val quantidade: String = "",
val unidade: String = "-",
val expanded: Boolean = false,
val onNameChange: (String) -> Unit = {},
val onValorChange: (String) -> Unit = {},
val onQuantidadeChange: (String) -> Unit = {},
val onUnidadeChange: (String) -> Unit = {},
val onExpandedChange: (Boolean) -> Unit = {}
)
class ItemInputViewModel : ViewModel() {
private val _itemState = MutableStateFlow(ItemData())
val itemState = _itemState.asStateFlow()
init {
_itemState.update { currentState ->
currentState.copy(
onNameChange = { newValue ->
_itemState.value = _itemState.value.copy(name = newValue)
},
onValorChange = { newValue ->
_itemState.value = _itemState.value.copy(valor = newValue)
},
onQuantidadeChange = { newValue ->
_itemState.value = _itemState.value.copy(quantidade = newValue)
},
onUnidadeChange = { newValue ->
_itemState.value = _itemState.value.copy(unidade = newValue)
},
onExpandedChange = { newValue ->
_itemState.value = _itemState.value.copy(expanded = newValue)
}
)
}
}
}
I've tried passing the parameters like this, but the same error still occurs
fun Aplicacao(modifier: Modifier = Modifier,
item1VM: ItemInputViewModel = viewModel(),
item2VM: ItemInputViewModel = viewModel()
)
When you use viewModel()
, that is putting a ViewModel instance in a ViewModelStoreOwner - in your case, your activity.
Every time you call viewModel()
for a particular class, you'll get the same instance back - that's how you get the same instance back after a configuration change, etc.
That means that when you write
val item1VM: ItemInputViewModel = viewModel()
val item2VM: ItemInputViewModel = viewModel()
item1VM
and item2VM
are the exact same instance - the first call creates an instance of ItemInputViewModel
and the second just retrieves that single instance.
If you want to create multiple instances, then you should use the key
parameter of viewModel
, as explained in the documentation:
The key to use to identify the ViewModel.
val item1VM: ItemInputViewModel = viewModel("item1")
val item2VM: ItemInputViewModel = viewModel("item2")