Hello, Form
Form の基本的な使い方を身につけましょう。 セットアップ がまだ完了していない場合は事前に実施してください。 このチュートリアルでは、form パッケージを使います。
Step 1 - Form
はじめに、rememberForm
APIを利用し、Form値と各入力フィールドの状態を管理する Form
インスタンスを取得します。
@Composable
fun App() {
MaterialTheme {
val form = rememberForm(
initialValue = "",
onSubmit = { value ->
println("onSubmit: $value")
}
)
}
}
Form
は initialValue
から型を推論します。 この例では、1つのテキスト入力フィールドが含まれる簡易的なフォームを作るため、initialValue
は文字列型にします。
WARNING
Androidプラットフォーム向けの状態復元を期待している場合は、initialValue
に指定する型が復元可能かどうか確認してください。 Formの内部では、フォーム値の管理に rememberSaveable
を利用しているため、未対応な型だと実行時エラーが投げられます。
Step 2 - FormField
Form の initialValue
に指定した型と各フィールドの関連付けは FormField を生成する Form<T>.Field
または Form<T>.rememberField
API で定義します。
@Composable
fun App() {
MaterialTheme {
val form = rememberForm(/* .. */)
Column {
form.Field(
selector = { it },
updater = { it },
validator = FieldValidator {
notBlank { "入力は必須です" }
},
render = { field -> // field: FormField<String>
TextField(
value = field.value,
onValueChange = field::onValueChange,
modifier = Modifier.onFocusChanged { state ->
field.handleFocus(state.isFocused || state.hasFocus)
},
isError = field.hasError,
supportingText = {
if (field.hasError) {
Text(text = field.error.messages.first(), color = MaterialTheme.colorScheme.error)
}
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = {
// フォーカスが次へ移動しないケースでは、手動でトリガーを呼び出す必要があります
field.trigger(FieldValidationMode.Blur)
defaultKeyboardAction(ImeAction.Done)
})
)
}
)
}
}
}
この例では、ヘッドレスコンポーネントとして提供する Form<T>.Field
を利用し、実際の入力コンポーネントと FormField
のI/F を繋ぎます。 selector
はフォームデータからフィールド値を抽出し、updater
はフィールドが変更されたときにフォームデータを更新する方法を指定します。
form パッケージでは、状態と振る舞いを制御しますが、UIは提供しません。これにより、入力コンポーネントのデザインに最大限の柔軟性を持たせることができます。
TIP
FieldValidator
ブロック内で使用可能なビルトインのバリデーションルールは form.rule に定義されています。 すべてのバリデーションルールは拡張関数のため、プロジェクト内で独自のバリデーションルールを定義してバリデーターブロック内で使用することができます。
Step 3 - Submit
フォームの送信は、Form<T>.handleSubmit
を利用し、任意のコンポーネントと組み合わせて制御します。
@Composable
fun App() {
MaterialTheme {
val form = rememberForm(
initialValue = "",
onSubmit = { value ->
// バリデーションが通った場合のみ呼ばれます
println("onSubmit: $value")
}
)
Column {
// ..
Button(
onClick = form::handleSubmit,
enabled = form.meta.canSubmit
) {
Text("送信")
}
}
}
}
handleSubmit
関数は、自動的にすべてのフィールドをバリデーションし、バリデーションが通った場合のみ onSubmit
コールバックを呼び出します。 送信ボタンの制御には、meta.canSubmit
が役立ちます。バリデーションルールとフォーム状態に基づいて、フォームが送信準備ができているかどうかを示します。
INFO
onSubmit
呼び出し後の送信中などのボタン制御は、form パッケージの範囲外です。 query パッケージの Mutation 機能に備わっている処理状態などを有効的に活用してください。
Step 4 - FormPolicy
バリデーションがいつ、どのように実行されるかを FormPolicy
で細かく調整できます。
@Composable
fun App() {
MaterialTheme {
val form = rememberForm(
initialValue = "",
policy = FormPolicy(
formOptions = FormOptions(
preValidationDelayOnChange = 300.milliseconds
),
fieldOptions = FieldOptions(
validationStrategy = FieldValidationStrategy(
initial = FieldValidationMode.Mount,
next = { current, isValid ->
if (isValid) FieldValidationMode.Blur else FieldValidationMode.Change
}
),
validationDelayOnChange = 300.milliseconds
)
),
onSubmit = { value -> println("onSubmit: $value") }
)
}
}
異なるUX要件に対応するためのプリセットポリシーを提供しています。
- FormPolicy() (デフォルト) - フィールドがフォーカスを失ったときに初回バリデーションを実行し、以降はフィールド値の変更都度バリデーションを遅延実行します
- FormPolicy.Minimal - 送信ボタンが押されたときに初回バリデーションを実行します(
meta.canSubmit
はつねにtrue
を返します)
Step 5 - FormState
Step 1 では、rememberForm
に初期値を指定していました。 FormState<T>
を活用すると、より高度なシナリオや外部状態管理との連携時に役立ちます。
@Composable
fun App() {
MaterialTheme {
val formState = rememberFormState(
initialValue = ""
)
val formWithState = rememberForm(
state = formState,
onSubmit = { value ->
println("onSubmit: $value")
formState.reset("")
}
)
// ミュータブルな状態タイプ(TextFieldState など)の場合
val nameState = rememberTextFieldState()
val emailState = rememberTextFieldState()
val formMeta = rememberFormMetaState()
val customFormState = remember(formMeta.key) {
FormState(
value = MutableFormData(name = nameState, email = emailState),
meta = formMeta
)
}
val customForm = rememberForm(
state = customFormState,
onSubmit = { value -> println("onSubmit: $value") }
)
Column {
// 上記で作成したいずれかのformインスタンスを使用
// ... フィールドと送信ボタン
}
}
}
FormState 経由でのみ呼び出せるAPIが存在します。
- reset - Form内の状態をすべて初期状態へ戻す
- setError - Form外のバリデーションエラーを設定できます(例えば、APIへの送信結果のバリデーションエラーの反映など)
一般的なシナリオでは、rememberForm
のみのアプローチで十分です。要件に応じて部分的に使い分けられるといいでしょう。
Finish 🏁
Form の基本的な使い方は理解できましたか? これでチュートリアルは完了です 🎊
学習を続けたい場合は、sample コード内の FormScreen
を動かしてみるのがよいでしょう。 ぜひ、試して気になるところがあれば Github discussions にフィードバックをお寄せください。
Soil プロジェクトに興味がありますか?
GitHub で ⭐ を付けてもらえると今後の励みになります!