0Pricing
Android Academy · レッスン

フォーム状態の管理

stateでフィールドの値を保持・更新します。

「フォーム状態の管理」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。

1つのフィールドからフォームへ

テキストフィールドが1つだけなら簡単です。しかし実際のフォームには、名前、メールアドレス、パスワード、場合によってはチェックボックスなど、複数の項目があります。このレッスンでは、Jetpack Composeでフォーム全体の状態を保持・更新する、すっきりした方法を学びます。

これを正しく実装すれば、バリデーション、送信、プレビューがすべて簡単になります。

独立した複数の状態

最も簡単な方法は、フィールドごとにmutableStateOfを1つ用意することです。それぞれのフィールドが、自分に対応する状態を読み取り、更新します。

フィールドが2つか3つ程度の小さなフォームなら、この方法で十分です。

@Composable
fun LoginForm() {
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }

    Column {
        OutlinedTextField(email, { email = it }, label = { Text("Email") })
        OutlinedTextField(password, { password = it }, label = { Text("Password") })
    }
}

Data classで状態をまとめる

フォームが大きくなると、ばらばらの変数が増えて管理しにくくなります。より整理された方法は、フォーム全体を1つの不変なdata classとしてモデル化することです。

そのオブジェクトを保持する状態を1つだけ用意し、copy()で更新します。

data class SignUpState(
    val name: String = "",
    val email: String = "",
    val agreedToTerms: Boolean = false
)

copy()で更新する

状態オブジェクトは不変なので、その場で直接変更することはありません。代わりに、1つのフィールドだけを変更した新しいコピーを作成し、それを再び代入します。

これにより、Composeの状態モデルを予測しやすく保てます。新しいオブジェクトが作成されると新しいスナップショットになり、再コンポジションが発生します。

var form by remember { mutableStateOf(SignUpState()) }

OutlinedTextField(
    value = form.name,
    onValueChange = { form = form.copy(name = it) },
    label = { Text("Name") }
)

完全なデータクラス形式

ここでは、フォーム全体を1つのSignUpStateで管理します。各フィールドはcopy()を使って、1つのプロパティを更新します。

いつでもフォーム全体の値を簡単に読み取れることに注目してください。値はそのままformです。

var form by remember { mutableStateOf(SignUpState()) }

Column {
    OutlinedTextField(
        value = form.name,
        onValueChange = { form = form.copy(name = it) },
        label = { Text("Name") }
    )
    OutlinedTextField(
        value = form.email,
        onValueChange = { form = form.copy(email = it) },
        label = { Text("Email") }
    )
}

チェックボックスとスイッチ

入力はすべてテキストとは限りません。CheckboxやSwitchも同じように扱えます。つまり、ブール値とonCheckedChangeコールバックを使います。

文字列と同じように、ブール値をフォームの状態に保存します。

var form by remember { mutableStateOf(SignUpState()) }

Row(verticalAlignment = Alignment.CenterVertically) {
    Checkbox(
        checked = form.agreedToTerms,
        onCheckedChange = { form = form.copy(agreedToTerms = it) }
    )
    Text("I agree to the terms")
}

状態から値を導出する

状態を単一の情報源で管理する大きなメリットは、追加の変数を保存せずに、状態から値を計算できることです。たとえば、フォームを送信できる状態かどうかを計算できます。

derivedStateOf(または単純な式)を使うと、フォームが変化するたびに結果が自動的に更新されます。

var form by remember { mutableStateOf(SignUpState()) }

val canSubmit by remember {
    derivedStateOf {
        form.name.isNotBlank() &&
        form.email.isNotBlank() &&
        form.agreedToTerms
    }
}

Button(onClick = { /* submit */ }, enabled = canSubmit) {
    Text("Sign Up")
}

構成変更への対応

rememberは再コンポジションの間は状態を保持しますが、画面の回転などの構成変更が発生すると失われます。回転後も単純な値を保持するには、rememberSaveableを使います。

プリミティブ型とParcelable型には、標準で対応しています。

var email by rememberSaveable { mutableStateOf("") }

OutlinedTextField(
    value = email,
    onValueChange = { email = it },
    label = { Text("Email") }
)

フォームの状態をViewModelにホイスティングする

単純なフォームを超える場合は、状態をViewModelに保持します。ViewModelは構成変更後も状態を保持し、UIロジックをコンポーザブルから分離できます。

コンポーザブルは状態を読み取り、イベントを転送します。正しい状態の管理はViewModelが担います。

class SignUpViewModel : ViewModel() {
    var state by mutableStateOf(SignUpState())
        private set

    fun onNameChange(value: String) {
        state = state.copy(name = value)
    }
}

ViewModelを接続する

コンポーザブルでViewModelを取得し、各フィールドを状態とハンドラーにバインドします。UIを薄く保てるため、テストもしやすくなります。

これは本番環境のフォームに推奨される構成です。

@Composable
fun SignUpScreen(vm: SignUpViewModel = viewModel()) {
    OutlinedTextField(
        value = vm.state.name,
        onValueChange = vm::onNameChange,
        label = { Text("Name") }
    )
}

Pure Kotlin: copy()の動作

copy()を使った更新パターンは、標準的なKotlinのデータクラスの動作にすぎません。ここでは単独で実行して、更新するたびにまったく新しいオブジェクトが生成されることを確認できます。

data class SignUpState(
    val name: String = "",
    val email: String = ""
)

fun main() {
    var state = SignUpState()
    state = state.copy(name = "Ada")
    state = state.copy(email = "ada@dev.io")
    println(state)
}

確認問題

フォームを状態として保持する不変のdata classでモデル化しています。1つのフィールドを更新するには、どうすればよいでしょうか。

まとめ

フォーム全体の状態を管理する方法を学びました。

  • 小規模なフォームでは、フィールドごとに1つのmutableStateOfを使います。
  • 大規模なフォームでは、1つの不変なdata classをcopy()で更新します。
  • テキストフィールドと同じように、ブール値でCheckboxやSwitchを制御します。
  • 「送信できるかどうか」のような値は保存せず、導出します。
  • 回転にはrememberSaveableを使い、実際の画面ではViewModelを使います。

次は、入力を検証して、役立つエラーを表示します。

よくある質問

「フォーム状態の管理」レッスンは無料ですか?

はい。「フォーム状態の管理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。

「フォーム状態の管理」で何を学びますか?

stateでフィールドの値を保持・更新します。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Android Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「フォーム状態の管理」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAndroid Academyレッスンでコードを書いて実行できますか?

はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. TextFieldとユーザー入力
  2. フォーム状態の管理
  3. 入力値のバリデーション
  4. キーボードのオプションとアクション
← Android Academyに戻る