0
votes

In this app, I have a screen where you can enter a title and content for a Note.

The screen has two composables DetailScreen() and DetailScreenContent.

Detailscreen has the scaffold and appbars and calls DetailScreenContents() which has two TextFields and a button.

I'm expecting the user to enter text in these fields and then press the button which will package the text into a NOTE object. My question is, how to pass the NOTE to the upper composable which is DETAILSCREEN() with a callback like= onclick: -> Note or any other efficient way?

@Composable
fun DetailScreen(navCtl : NavController, mviewmodel: NoteViewModel){

Scaffold(bottomBar = { TidyBottomBar()},
         topBar = { TidyAppBarnavIcon(
             mtitle = "",
             onBackPressed = {navCtl.popBackStack()},

         )
         }) {
    DetailScreenContent()
   }
}



@Composable
fun DetailScreenContent() {
val titleValue = remember { mutableStateOf("")}
val contentValue = remember { mutableStateOf("")}
val endnote by remember{ mutableStateOf(Note(
    Title = titleValue.value,
    Content = contentValue.value))}


Column(modifier = Modifier.fillMaxSize()) {

    OutlinedTextField(value = titleValue.value,
        onValueChange = {titleValue.value = it},
        singleLine = true,
        label = {Text("")}
    ,modifier = Modifier
            .fillMaxWidth()
            .padding(start = 3.dp, end = 3.dp),
        shape = cardShapes.small
    )

    OutlinedTextField(value = contentValue.value, onValueChange = {
        contentValue.value = it
    },
        label = {Text("Content")}
        ,modifier = Modifier
            .fillMaxWidth()
            .padding(start = 3.dp, end = 3.dp, top = 3.dp)
            .height(200.dp),
        shape = cardShapes.small,

    )

  Row(horizontalArrangement = Arrangement.End,
      modifier = Modifier.fillMaxWidth()){
      Button(onClick = {
                       /**return the object to the upper composable**/
      }, shape = cardShapes.small) {
          Text(text = stringResource(R.string.Finish))
      }
      
  }

}
1

1 Answers

0
votes

You could use state hoisting. Using lambdas is the most common way of hoisting state here.

Ok so here's DetailScreenContent(), say

fun DetailScreenContent(
processNote: (Note) -> Unit
){
 Button( onClick = { processNote(/*Object to be "returned"*/) }
}

We are not literally returning anything, but we are hoisting the state up the hierarchy. Now, in DetailsScreen

fun DetailScreen(navCtl : NavController, mviewmodel: NoteViewModel){

Scaffold(bottomBar = { TidyBottomBar()},
         topBar = { TidyAppBarnavIcon(
             mtitle = "",
             onBackPressed = {navCtl.popBackStack()},

         )
         }) {
    DetailScreenContent(
        processNote = {note -> //This is the passed object
            /*Perform operations*/
        }
     )
   //You could also extract the processNote as a variable, like so
/*
 val processNote = (Note) {
 Reference the note as "it" here
 }
*/
   }
}

This assumes that there is a type Note (something like a data class or so, the object of which type is being passed up, get it?)

That's how we hoist our state and hoist it up to the viewmodel. Remember, compose renders state based on variables here, making it crucial to preserve the variables, making sure they are not modified willy nilly and read from random places. There should be, at a time, only one instance of the variables, which should be modified as and when necessary, and should be read from a common place. This is where viewmodels are helpful. You store all the variables (state) inside the viewmodel, and hoist the reads and modifications to there. It must act as a single source of truth for the app.