Android Compose의 TextFieldState
State-based TextField 에서는 기존의 Value-based TextField 와 달리, TextFieldState 클래스를 통해 입력값, 커서 위치 등 입력창의 상태를 통합적으로 저장하고 관리합니다. 입력 흐름 전체를 하나의 객체로 캡슐화하여 관리하기 때문에 기존의 Value-based TextField 보다 안정적이고, 일관되게 입력창을 구현할 수 있습니다.
// Value-based TextField
var text by remember { mutableStateOf("Hello") }
TextField(
value = text,
onValueChange = { text = it },
)
// State-based TextField
val state = rememberTextFieldState("Hello")
TextField(
state = state
)
TextFieldState
rememberTextFieldState() 함수를 사용하여 TextFieldState 인스턴스를 생성하면 입력창의 상태를 자동으로 저장하고, 복원해줍니다.
val state = rememberTextFieldState("Hello")
TextField(
state = state
)
만약 더 복잡하거나 커스텀한 방식으로 상태 저장이 필요하다면 rememberSaveable 함수에 TextFieldState.Saver를 전달해서 TextFieldState 인스턴스를 생성할 수 있습니다.
val state = rememberSaveable(saver = TextFieldState.Saver) {
TextFieldState()
}
TextField(
state = state
)
TextFieldState의 edit, setTextAndSelectAll, setTextAndPlaceCursorAtEnd, clearText 같은 함수를 호출하면 TextField 값을 변경할 수 있습니다.
edit
edit 함수는 TextFieldState의 현재 상태를 수정 가능한 형태인 TextFieldBuffer로 만들어,
val state = TextFieldState("hello world!")
state.edit {
// Insert a comma after "hello".
insert(5, ",") // = "hello, world!"
// Delete the exclamation mark.
delete(12, 13) // = "hello, world"
// Add a different name.
append("Compose") // = "hello, Compose"
// Say goodbye.
replace(0, 5, "goodbye") // "goodbye, Compose"
// Select the new name so the user can change it by just starting to type.
selection = TextRange(9, 16) // "goodbye, ̲C̲o̲m̲p̲o̲s̲e"
}
setTextAndSelectAll
TextField의 값을 지정한 텍스트로 변경하고, 그 텍스트 전체를 선택합니다. 아래 코드는 버튼 클릭 시, 입력창 값이 Hello -> Hello World! 로 변경하고, 변경한 값은 전체 선택됩니다.
val state = rememberTextFieldState("Hello")
Button(
onClick = { state.setTextAndSelectAll("Hello World!") }
) {
Text("변경&전체선택")
}
TextField(
state = state
)
setTextAndPlaceCursorAtEnd
TextField의 값을 지정한 텍스트로 변경하고, 그 텍스트의 가장 마지막으로 커서 위치를 이동합니다.
clearText
state에 저장된 모든 text 값을 지웁니다.
TextFieldState의 text, selection, composition, undoState
text
현재 입력창의 값을 나타냅니다. 사용자가 값을 입력할 때 자동으로 업데이트 되며 TextFieldState의 edit 함수 블록 안에서 TextFieldBuffer의 함수를 통해 값을 변경할 수 있습니다. snapshotFlow를 통해 입력창 값의 변화를 Compose recomposition 범위 밖에서 관찰할 수 있습니다.
val state = rememberTextFieldState()
// 입력창에 "!!!" 값 추가
state.edit {
append("!!!")
}
LaunchedEffect(state) {
snapshotFlow { state.text }
.distinctUntilChanged()
.collectLatest { text ->
// text 변화 감지
}
}
}
TextField(
state = state
)
selection
현재 입력창의 커서 위치나 텍스트 선택 범위를 나타냅니다. 사용자가 값을 입력하거나 커서를 변경할 때마다 자동으로 업데이트 되며 TextFieldState의 edit 함수 블록 안에서 selection 값을 변경할 수 있습니다. snapshotFlow를 통해 selection 값의 변화를 Compose recomposition 범위 밖에서 관찰할 수 있습니다.
val state = rememberTextFieldState()
// TextField의 커서 위치가 맨 앞으로 변경됨
state.edit {
selection = TextRange(0,0)
}
LaunchedEffect(state) {
snapshotFlow { state.selection }
.distinctUntilChanged()
.collectLatest { selection ->
// selection 변화 감지 : TextRange(x, x)
}
}
}
TextField(
state = state
)
composition
키보드(IME)가 아직 조합하고 있는 텍스트의 범위를 나타내는 값입니다. null일 경우, 조합이 완료되어 입력이 확정된 상태를 의미하고, 조합 중일 때는 해당 텍스트의 범위가 TextRange 타입으로 제공됩니다. 한국어, 일본어, 한자 등 조합해서 입력하는 언어에서 주로 사용됩니다.
undoState
undo 기록을 관리하는 State로 undoState의 history는 입력창의 입력, 삭제, 붙여넣기 등 편집 작업을 수행할 때마다 쌓입니다.
canUndo | 되돌릴 수 있는지 여부 |
canRedo | 다시 되돌릴 수 있는지 여부 |
undo() | 되돌리기 |
redo() | 되돌린 작업 복원 |
clearHistory() | 입력창의 편집 작업 히스토리 초기화 |
val state = rememberTextFieldState()
Column(Modifier.padding(8.dp)) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(
onClick = { state.undoState.undo() },
enabled = state.undoState.canUndo
) {
Text("Undo")
}
Button(
onClick = { state.undoState.redo() },
enabled = state.undoState.canRedo
) {
Text("Redo")
}
Button(
onClick = { state.undoState.clearHistory() },
enabled = state.undoState.canUndo || state.undoState.canRedo
) {
Text("Clear History")
}
}
}
TextField(
state = state
)