将 Material3 颜色应用于 Jetpack Compose 中的 OutlinedTextField

Applying Material3 colors to OutlinedTextField in Jetpack Compose

编辑:添加更多细节 - 使代码完整:

在转换为新的 M3 主题后,我在将主题颜色应用于 OutlinedTextFields 时遇到了挑战。

据我所知,在使用 OutlinedTextField.[=24 时,不直接支持使用新的设计标记颜色(primaryContainertertiary 等) =]

问题:有没有一种简单的方法可以通用地应用这些颜色?
问题:当我应用自定义颜色时,它们并没有全部应用,我不确定为什么?

我尝试了什么:

这是我用来创建自定义 TextFieldColors 的函数,以及我如何应用它们:

@Composable
fun customTextColors(): TextFieldColors =
    TextFieldDefaults.outlinedTextFieldColors(
        textColor = MaterialTheme.colorScheme.onBackground,
        disabledTextColor = MaterialTheme.colorScheme.onBackground,
        backgroundColor = MaterialTheme.colorScheme.background,
        cursorColor = MaterialTheme.colorScheme.onBackground,
        errorCursorColor = MaterialTheme.colorScheme.error,
        focusedBorderColor = MaterialTheme.colorScheme.primary,
        unfocusedBorderColor = MaterialTheme.colorScheme.onBackground,
        disabledBorderColor = MaterialTheme.colorScheme.onBackground,
        errorBorderColor = MaterialTheme.colorScheme.error,
        leadingIconColor = MaterialTheme.colorScheme.onBackground,
        disabledLeadingIconColor = MaterialTheme.colorScheme.onBackground,
        errorLeadingIconColor = MaterialTheme.colorScheme.error,
        trailingIconColor = MaterialTheme.colorScheme.onBackground,
        disabledTrailingIconColor = MaterialTheme.colorScheme.onBackground,
        errorTrailingIconColor = MaterialTheme.colorScheme.error,
        focusedLabelColor = MaterialTheme.colorScheme.primary,
        unfocusedLabelColor = MaterialTheme.colorScheme.onBackground,
        disabledLabelColor = MaterialTheme.colorScheme.onBackground,
        errorLabelColor = MaterialTheme.colorScheme.error,
        placeholderColor = MaterialTheme.colorScheme.onBackground,
        disabledPlaceholderColor = MaterialTheme.colorScheme.primary,
    )

然后以这种方式应用它们:

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.input.*
import com.myApp.demo.R
import com.myApp.demo.data.StaticData
import com.myApp.demo.ui.theme.card_corner_radius
import com.myApp.demo.ui.theme.grey_400
import com.myApp.demo.ui.theme.horizfull_verthalf

@Composable
fun NameTextInput(name: String, onNameInfoValid: (Boolean) -> Unit) {
    // Name
    val nameState = remember { mutableStateOf(TextFieldValue(name)) }
    val nameString = stringResource(R.string.name)
    val nameLabelState = remember { mutableStateOf(nameString) }
    val isNameValid = if (nameState.value.text.length >= 5) {
        nameLabelState.value = nameString
        onNameInfoValid(true)
        true
    } else {
        nameLabelState.value = stringResource(R.string.name_error)
        onNameInfoValid(false)
        false
    }
    OutlinedTextField(
        shape = RoundedCornerShape(card_corner_radius),
        value = nameState.value,
        singleLine = true,
        onValueChange = { if (it.text.length <= 30) nameState.value = it },
        isError = !isNameValid,
        label = { Text(nameLabelState.value) },
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Text,
            capitalization = KeyboardCapitalization.Words
        ),
        colors = customTextColors(),  // <-- Here is where colors are set!
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizfull_verthalf)
    )
}

没有全部被应用。

我看到 focusedBorderColor 设置正确,但我没有看到 focusedLabelColor 被正确应用(在这种情况下,红色是正确的 primary 颜色被应用,所以我希望看到标签也是红色的)

另外错误是 border 有效,但不适用于其他属性:

这是我的主题设置:

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable

private val DarkColorPalette = darkColorScheme(
    primary = grey_700,
    onPrimary = white_desat,
    tertiary = grey_600,
    secondary = amer_blue,  // TODO desat
    onSecondary = grey_500,
    surface = black_pure,
    onSurface = white_desat,
    surfaceVariant = grey_800,
    onSurfaceVariant = white_desat,
    error = orange_700,
    onError = orange_700
)

private val LightColorPalette = lightColorScheme(
    primary = amer_red,
    onPrimary = white_pure,
    tertiary = amer_blue_50,
    secondary = amer_blue,
    onSecondary = grey_500,
    surface = white_pure,
    onSurface = black_pure,
    surfaceVariant = white_pure,
    onSurfaceVariant = black_pure,
    error = orange_500,
    onError = orange_500,
    errorContainer = orange_500,
    onErrorContainer = orange_500

    /* Other default colors to override
    background = Color.White,
    onBackground = Color.Black,
    */
)

@Composable
fun ComposeTemplateTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    MaterialTheme(
        colorScheme = colors,
        typography = DemoTypography,
        content = content
    )
}

最后为了使这个示例完整,这里是我首先调用可组合项的方式:

NameTextInput(name = personalInfo.name, onNameInfoValid = { isNameValidState.value = it })

...以及我使用的版本

        appCompatVersion = '1.3.1'
        activityComposeVersion = '1.4.0'        
        composeVersion = '1.0.4'
        composeNavigationVersion = '2.4.0-alpha06'        
        kotlinVersion = '1.5.31'
        ktlint_version = "0.42.1"
        ktxVersion = '1.7.0'
        lifecycleVersion = '2.3.0'
        materialVersion = '1.5.0-alpha05'

这看起来很疯狂。但这就是发生的事情。

Material 2 和 Material 3 之间似乎存在一些风格冲突。 (我猜显然很明显)。

正在使用

import androidx.compose.material3.Text

但是当使用

import androidx.compose.material.Text

这一行更改解决了问题。

如果需要,我的完整 material 导入。

import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Clear
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme

其他有用的信息,

我正在维护完整的 2 套资源。
这可能有助于 M3 尚不支持的组件。
在可组合项中使用适当的导入。
如果特定可组合项同时具有 M2 和 M3 组件,我们可以使用完整导入。

private val Material2DarkColorPalette = darkColors(
    primary = Primary,
    ...
)

private val Material2LightColorPalette = lightColors(
    primary = Primary,
    ...
)

private val LightColorPalette = lightColorScheme(
    primary = Primary,
    ...
)

private val DarkColorPalette = darkColorScheme(
    primary = Primary,
    ...
)

@Composable
fun Material2AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    val colors = if (darkTheme) {
        Material2DarkColorPalette
    } else {
        Material2LightColorPalette
    }

    androidx.compose.material.MaterialTheme(
        colors = colors,
        typography = Material2Typography,
        shapes = Shapes,
        content = content
    )
}

@Composable
fun Material3AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }

    MaterialTheme(
        colorScheme = colors,
        typography = Typography,
        content = content
    )
}

@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    Material2AppTheme(
        darkTheme = darkTheme,
        content = content,
    )
}