Skip to content

Instantly share code, notes, and snippets.

@tmxdyf
Created March 27, 2026 02:54
Show Gist options
  • Select an option

  • Save tmxdyf/ff9df72cb64b74fae5a4b42e3c3e6e1e to your computer and use it in GitHub Desktop.

Select an option

Save tmxdyf/ff9df72cb64b74fae5a4b42e3c3e6e1e to your computer and use it in GitHub Desktop.
自定义TextField光标
@Composable
fun BasicTextFieldWidget(
modifier: Modifier = Modifier,
value: TextFieldValue,
hint: String,
maxLength: Int,
visible: Boolean = true,
requestFocus: Boolean = false,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
onValueChanged: (value: TextFieldValue) -> Unit,
) {
val visualTransformation = if (visible) {
VisualTransformation.None
} else {
remember { PasswordVisualTransformation() }
}
var focusable by remember { mutableStateOf(value = false) }
val focusRequester = remember { FocusRequester() }
LaunchedEffect(key1 = Unit) {
if (requestFocus) {
focusable = true
delay(timeMillis = 100)
focusRequester.requestFocus()
}
}
val focusManager = LocalFocusManager.current
var cursorRect by remember { mutableStateOf<Rect?>(null) }
var hasFocus by remember { mutableStateOf(false) }
BasicTextField(
modifier = modifier
//.padding(start = 8.dp, end = 50.dp)
.fillMaxWidth()
.focusable(enabled = focusable)
.focusRequester(focusRequester)
.onFocusChanged {
hasFocus = it.hasFocus
},
textStyle = TextStyle(color = Color.Black, fontSize = 17.sp),
singleLine = true,
keyboardOptions = keyboardOptions,
keyboardActions = KeyboardActions(
onNext = {
runCatching {
focusManager.moveFocus(FocusDirection.Down)
}
}),
visualTransformation = visualTransformation,
cursorBrush = SolidColor(Color.Transparent),
value = value,
onValueChange = { textValue ->
val length = textValue.text.length
if (length <= maxLength) {
//textFieldValue = textValue
onValueChanged(textValue)
}
},
onTextLayout = { layout ->
//val cursorOffset = value.length
//cursorRect = layout.getCursorRect(cursorOffset)
val selection = value.selection
// 只在“插入态”才画光标
cursorRect = if (selection.collapsed) {
layout.getCursorRect(selection.start)
} else {
null
}
}
) { innerTextField ->
Box(
modifier = Modifier
.padding(horizontal = 4.dp)
) {
innerTextField()
if (value.text.isBlank()) {
Text(
text = hint,
color = Color(0xCCCCCCCC),
fontSize = 17.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
SideEffect {
Logger.d("ComposeTextFieldWidget", "cursorRect=$cursorRect")
}
if (hasFocus && cursorRect != null) {
/*自绘制光标*/
Canvas(
modifier = Modifier
.matchParentSize()
) {
cursorRect?.let { r ->
val cursorWidth = 1f
// ✅ 当前可视区域宽度
val maxX = size.width - cursorWidth
// ✅ 将光标限制在可视范围内
val clampedLeft = r.left.coerceIn(0f, maxX)
drawRect(
color = Color.Black,
topLeft = Offset(clampedLeft, r.top),
size = Size(width = cursorWidth, r.height)
)
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment