I wanted to achieve something that was like a moda...
# codereview
t
I wanted to achieve something that was like a modal in place edit of a "name" field in my Settings page. I chose to (ab)use an AlertDialog to do this. Maybe there's a better way? I'll put code and some questions in thread...
The various "hacky" things I had to do make this work include: • use
onGloballyPositioned
modifier to capture where the original label is at • Populate the full contents of the dialog so I could use Column/Spacer layout to place the dialog text field in the same place • Then emulate the dismiss click (including suppress the ripple), since there was no scrim anymore • Use onGloballyPositioned on the TextField as a hook to trigger focus so the keyboard raises I would love feedback, redirection, on any of these. I'm curious if there's a easier general methodology for temporarily restricting a interaction to a subset of the screen so it can be temporarily modal-ish.
Copy code
private fun NameRow(mc: MCHost) {
   Box(
      modifier = Modifier
         .fillMaxWidth()
         .padding(vertical = 10.dp)
   ) {
      var isEditing by remember { mutableStateOf(false) }
      // value to retain where the name label ended up at
      var nameTop by remember { mutableStateOf(0.dp) }
      // capture density here in composition, because we can't run it in the globally positioned closure
      var density = LocalDensity.current
      SettingsLabel(text = "Name", modifier = Modifier.align(Alignment.CenterStart))
      Text(text = mc.name ?: "", modifier = Modifier
         .align(Alignment.Center)
         // capture the root position in nameTop
         .onGloballyPositioned { layoutCoordinates ->
            nameTop = with(density) { layoutCoordinates.positionInRoot().y.toDp() }
         })
      CircleButton(vectorID = R.drawable.screwdriver_in_circle_mask,
         modifier = Modifier.align(Alignment.CenterEnd),
         click = { isEditing = isEditing.not() })
      if (isEditing) {
         var editedName by remember {
            mutableStateOf(
               TextFieldValue(
                  // force the cursor to the end
                  text = mc.name!!, selection = TextRange(mc.name!!.length)
               )
            )
         }
         val focusRequester = remember { FocusRequester() }
         AlertDialog(onDismissRequest = { isEditing = false }) {
            // The dialog wants to minimially center things
            // Fill it with a column
            Column(
               modifier = Modifier
                  .fillMaxHeight()
                  // But now there's no dismiss area, so we have to emulate the dismiss click here
                  .clickable(
                     // use the version that allows us to suppress the ripple so it looks like a normal dialog dismiss
                     MutableInteractionSource(),
                     indication = null,
                     onClick = { isEditing = false })
            ) {
               // The full dialog column matches the root context we capture the nameTop in, add 4 for padding and insert a spacer
               Spacer(modifier = Modifier.height(nameTop - 4.dp))
               BasicTextField(
                  value = editedName,
                  onValueChange = { newValue ->
                     editedName = newValue
                  },
                  modifier = Modifier
                     .padding(horizontal = 40.dp)
                     .fillMaxWidth()
                     .background(Color.White, shape = RoundedCornerShape(25))
                     .padding(vertical = 4.dp)
                     .focusRequester(focusRequester)
                     // suggested on SO as a reliable way get this TextField focused when the dialog opens
                     .onGloballyPositioned { focusRequester.requestFocus() }, // IMPORTANT
                  textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
                  keyboardOptions = KeyboardOptions(
                     capitalization = KeyboardCapitalization.Words,
                     keyboardType = KeyboardType.Text,
                     imeAction = ImeAction.Default
                  ),
                  keyboardActions = KeyboardActions(onDone = {
                     mc.name = editedName.text
                     isEditing = false
                  }),
                  singleLine = true,
               )
            }
         }
      }
   }
}
j
Interesting, the out anim could be perceived a bit jumpy. Perhaps there is a better suitable way to return. Like instead of slide from bottom / to bottom you could scale it out in a bit.