简体   繁体   中英

Showing a text field in the app bar in Jetpack Compose with Material3

Many Android apps feature a search box in the app bar / toolbar, that looks somewhat like this screenshot:

How do I recreate something similar in Jetpack Compose + Material3? I would like the text field to have these features:

  • support showing a hint when the content string is empty (eg "Type your search...")
  • auto-focus on the first composition, so that the keyboard is opened
  • put the cursor at the end of the content string on the first composition

I tried to reproduce the same behavior in Jetpack Compose + Material3 by putting a TextField in the title of a CenterAlignedTopAppBar but the result does not look good. The text field uses the whole height of the app bar and has a grey background, and both of these things look odd.

I came up with a AppBarTextField after some engineering, see the code below. I had to use the lower-level BasicTextField since the normal TextField is not customizable enough. The code having to do with theming and color was copied directly from TextField 's implementation, so that the theme's customizations apply normally to the components of the text field.

The parameters the AppBarTextField composable accepts are:

  • value : the content string to show in the text field
  • onValueChange : new values are passed here (remember to update value !)
  • hint : the hint to show when the text field is empty
  • modifier , keyboardOptions and keyboardActions : they are passed directly to BasicTextField and they behave the same as they would in a normal TextField . If you need to customize other TextField parameters just add them to the function signature and then pass them to BasicTextField .

The requested features are implemented:

  • the focus acquisition was achieved with a SideEffect , so that it would only happen on the first composition
  • putting the cursor at the end on the first composition required using a TextFieldValue
  • the strange-looking background is not present anymore, since no .background() modifier is present (while it is in the normal TextField )
  • the hint was added using by passing a placeholder to TextFieldDecorationBox in the decorationBox parameter (note that this was also possible with TextField )
  • TextFieldDecorationBox 's padding is also now only 4dp . Padding was added here (and not with a modifier on BasicTextField ) since otherwise the bottom line indicator (which is, instead, displayed using the .indicatorLine() modifier) would not be shown correctly.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppBarTextField(
    value: String,
    onValueChange: (String) -> Unit,
    hint: String,
    modifier: Modifier = Modifier,
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
    keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val textStyle = LocalTextStyle.current
    val colors = TextFieldDefaults.textFieldColors()

    // If color is not provided via the text style, use content color as a default
    val textColor = textStyle.color.takeOrElse {
        MaterialTheme.colorScheme.onSurface
    }
    val mergedTextStyle = textStyle.merge(TextStyle(color = textColor, lineHeight = 50.sp))

    // request focus when this composable is first initialized
    val focusRequester = FocusRequester()
    SideEffect {
        focusRequester.requestFocus()
    }

    // set the correct cursor position when this composable is first initialized
    var textFieldValue by remember {
        mutableStateOf(TextFieldValue(value, TextRange(value.length)))
    }
    textFieldValue = textFieldValue.copy(text = value) // make sure to keep the value updated

    CompositionLocalProvider(
        LocalTextSelectionColors provides LocalTextSelectionColors.current
    ) {
        BasicTextField(
            value = textFieldValue,
            onValueChange = {
                textFieldValue = it
                // remove newlines to avoid strange layout issues, and also because singleLine=true
                onValueChange(it.text.replace("\n", ""))
            },
            modifier = modifier
                .fillMaxWidth()
                .heightIn(32.dp)
                .indicatorLine(
                    enabled = true,
                    isError = false,
                    interactionSource = interactionSource,
                    colors = colors
                )
                .focusRequester(focusRequester),
            textStyle = mergedTextStyle,
            cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
            keyboardOptions = keyboardOptions,
            keyboardActions = keyboardActions,
            interactionSource = interactionSource,
            singleLine = true,
            decorationBox = { innerTextField ->
                // places text field with placeholder and appropriate bottom padding
                TextFieldDefaults.TextFieldDecorationBox(
                    value = value,
                    visualTransformation = VisualTransformation.None,
                    innerTextField = innerTextField,
                    placeholder = { Text(text = hint) },
                    singleLine = true,
                    enabled = true,
                    isError = false,
                    interactionSource = interactionSource,
                    colors = colors,
                    contentPadding = PaddingValues(bottom = 4.dp)
                )
            }
        )
    }
}

Here is an example usage:

var value by rememberSaveable { mutableStateOf("initial content") }
CenterAlignedTopAppBar(
    title = {
        AppBarTextField(
            value = value,
            onValueChange = { newValue -> value = newValue },
            hint = "A hint..."
        )
    },
    navigationIcon = /* the back icon */,
    actions = /* the search icon */
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM