What You'll Learn
State Preservation/Restoration(rememberSaveable、SavedStateHandle、プロセス死Support、Custom Saver、Navigation引数)を解説します。
remember vs rememberSaveable
@Composable
fun StateComparison() {
// ❌ remember: Lost on screen rotation
var count1 by remember { mutableIntStateOf(0) }
// ✅ rememberSaveable: Persists through screen rotation and process death
var count2 by rememberSaveable { mutableIntStateOf(0) }
Column(Modifier.padding(16.dp)) {
Text("remember: $count1")
Text("rememberSaveable: $count2")
Button(onClick = { count1++; count2++ }) { Text("Increment") }
}
}
Custom Saver
data class EditState(
val title: String,
val content: String,
val isDraft: Boolean
)
val EditStateSaver = run {
val titleKey = "title"
val contentKey = "content"
val isDraftKey = "isDraft"
mapSaver(
save = { mapOf(titleKey to it.title, contentKey to it.content, isDraftKey to it.isDraft) },
restore = { EditState(it[titleKey] as String, it[contentKey] as String, it[isDraftKey] as Boolean) }
)
}
@Composable
fun EditScreen() {
var editState by rememberSaveable(stateSaver = EditStateSaver) {
mutableStateOf(EditState("", "", true))
}
Column(Modifier.padding(16.dp)) {
OutlinedTextField(
value = editState.title,
onValueChange = { editState = editState.copy(title = it) },
label = { Text("タイトル") }
)
OutlinedTextField(
value = editState.content,
onValueChange = { editState = editState.copy(content = it) },
label = { Text("内容") },
modifier = Modifier.height(200.dp)
)
}
}
SavedStateHandle (ViewModel)
@HiltViewModel
class EditViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: ArticleRepository
) : ViewModel() {
// SavedStateHandle で自動保存/復元
val title = savedStateHandle.getStateFlow("title", "")
val content = savedStateHandle.getStateFlow("content", "")
fun updateTitle(value: String) {
savedStateHandle["title"] = value
}
fun updateContent(value: String) {
savedStateHandle["content"] = value
}
fun save() {
viewModelScope.launch {
repository.save(Article(title = title.value, content = content.value))
}
}
}
Saving List State
@Composable
fun ListWithSavedScroll() {
val listState = rememberLazyListState() // 自動でスクロール位置保存
LazyColumn(state = listState) {
items(100) { index ->
Text("Item $index", Modifier.padding(16.dp))
}
}
}
// Saving Multi-Select State
@Composable
fun MultiSelectList(items: List<Item>) {
var selectedIds by rememberSaveable {
mutableStateOf(setOf<String>())
}
LazyColumn {
items(items, key = { it.id }) { item ->
ListItem(
headlineContent = { Text(item.title) },
leadingContent = {
Checkbox(
checked = item.id in selectedIds,
onCheckedChange = { checked ->
selectedIds = if (checked) selectedIds + item.id
else selectedIds - item.id
}
)
}
)
}
}
}
Summary
| 手法 | スコープ | 用途 |
|---|---|---|
remember |
Recomposition | 一時的な状態 |
rememberSaveable |
Configuration変更 | UI状態 |
SavedStateHandle |
プロセス死 | ViewModel状態 |
DataStore |
永続 | ユーザー設定 |
-
rememberSaveableで画面回転・プロセス死を生き延びる -
mapSaver/listSaverでカスタム型を保存 -
SavedStateHandleでViewModelの状態を自動保存 -
LazyListStateはスクロール位置を自動保持
8種類のAndroidAppTemplates(状態管理Pre-designed)を公開しています。
Template List → Gumroad
Related Articles:
Ready-Made Android App Templates
8 production-ready Android app templates with Jetpack Compose, MVVM, Hilt, and Material 3.
Browse templates → Gumroad
Top comments (0)