-4

I am attempting to get one property to bind between a ViewModel and a @Composable.

I am getting the following error

Type mismatch. 
Required:String 
Found:MutableState<String.Companion>

I don't understand what I am doing wrong.

//Reusable input field
@Composable
fun MyTextField(
    value: String,
    onValueChange: (String) -> Unit,
    placeHolder: String
    ) {
    OutlinedTextField(
        value = value,
        onValueChange = onValueChange,
        placeholder = {Text(placeHolder)},
    )
}


// ViewModel
class MyViewModel : ViewModel () {
    var MyVariable = mutableStateOf(String)
}


// stateful comp
@Composable
fun MyScreen(
    viewModel: MyViewModel = MyViewModel()
) {
     MyContent(
        myVariable = vm.myVariable,
        setMyVariable = { vm.myVariable = it }
    )
}

// stateless Comp
@Composable
fun MyContent(
    myVariable: String,
    setMyVariable: (String) -> Unit
)
    {
        Column() {
    
            MyTextField(value = myVariable, onValueChange = setMyVariable, placeholder = "Input Something")

            Text("Your variable is $myVariable" )
    }


Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • Just for the future posts, always tell exactly which line the error emerges from, post only the necessary code, removing all the decorations and similar modifiers. While using the android-jetpack-compose tag, also add the android tag along with it. Also, they are called 'Composables', not 'Views'. You must always use the correct terminology or it can lead to no end of confusion. – Richard Onslow Roper May 20 '22 at 19:04

2 Answers2

1

You are initializing your variable as a MutableState<T> type data-holder.

What you've posted in the question won't even compile but this is what I assume you did in your actual code

var myVar = mutableStateOf ("")

Now, mutableStateOf("") returns a MutableState<String> type object, which you are passing around in your methods.

On the other hand, your methods expect a String type object, not MutableState<String>. Hence, you could either extract the value from your variable and pass that around,

myVar.value

Or do it the preferred way, and use kotlin property delegation.

Initialize the variable like this

var myVar by mutableStateOf ("")

The by keyword acts as a delegate for the initializer and returns a String value instead of a MutableState<T>. This updates and triggers recompositions just as it's other counterpart but is far cleaner in code and keeps the boilerplate to a minimum.

Also, you seem to be pretty new to compose and kotlin, so consider taking the Compose pathway to learn the basics. Just look it up and the first official Android developers link will take you there.

EDIT: FINAL ANSWER

ViewModel

ViewModel{
 var v by mutableStateOf ("")
 fun setV(v: String) {
   this.v = v
 }
}

Composable calling site

MyComposable(
 value = viewModel.v
 onValueChange = viewModel::setV
)

Composable declaration

fun MyComposable (
 value: String,
 onValueChange: (String) -> Unit
) {
 TextField (
  value = value
  onValueChange = onValueChange
 )
}

This is the proper state-hoisting linking where the state variable is updated properly and hence, read well. Also, sir, you'd know what state-hoisting is if you'd actually read through the docs and taken the codelabs. I'm serious, TAKE the codelabs (in the Compose pathway). That's the reason your question was downvoted so many times, because you use terms like state-hoisting as if you understand it, but then you don't have half the implementation that it promotes.

Richard Onslow Roper
  • 5,477
  • 2
  • 11
  • 42
  • State hoisting works through the getter/setter format. The getters and setters must be at the same level of hierarchy, i.e., your ViewModel. Check [this](https://stackoverflow.com/a/71817269/15880865) answer. – Richard Onslow Roper May 21 '22 at 04:30
  • Also, as I advised in my answer, do take the compose pathway first. If you think just going through stackoverflow will teach you everything, trust me it never will. – Richard Onslow Roper May 21 '22 at 04:31
0

Replace

MyContent(
    myVariable = vm.myVariable,
    setMyVariable = { vm.myVariable = it }
)

with

MyContent(
    myVariable = vm.myVariable.value,
    setMyVariable = { vm.myVariable.value = it }
)

and you need to define your variable like this

val MyVariable = mutableStateOf<String>("initial value here")
Francesc
  • 25,014
  • 10
  • 66
  • 84