Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What are the problem?

Jetpack Compose でログインフォームを作る

はじめに

Jetpack Compose で入力フォームを作成する方法を記載します。画面に配置する要素は3つで、メールアドレス入力欄、パスワード入力欄、ボタンです。

Empty Compose Activity から作り始めた場合、おそらく追加のパッケージは必要ありません。

実装

まずはメールアドレス入力欄の実装です。

@Composable
fun EmailTextField(
  value: String,
  modifier: Modifier = Modifier,
  onValueChange: (String) -> Unit,
) {
  val focusManager = LocalFocusManager.current
  OutlinedTextField(
    value = value,
    onValueChange = onValueChange,
    modifier = modifier,
    keyboardOptions = KeyboardOptions(
      keyboardType = KeyboardType.Email,
      imeAction = ImeAction.Next,
    ),
    keyboardActions = KeyboardActions(
      onNext = {
        focusManager.moveFocus(FocusDirection.Down)
      }
    ),
    label = { Text("メールアドレス") },
  )
}

keyboardType = KeyboardType.Email とすることで、キーボードがメールアドレスの入力用になります。

ImeAction.Next はキーボードの右下にあるボタンの形式を表します。keyboardOptionsimeAction = ImeAction.XXXkeyboardActionsonXXX は密接に関係しており、たとえば imeAction = ImeAction.Next と設定すると onNext で渡した関数が呼ばれます。

focusManager.moveFocus(FocusDirection.Down) により次のフォーカス先にフォーカスを移すことができます。

次はパスワード入力欄の実装です。

@Composable
fun PasswordTextField(
  value: String,
  modifier: Modifier = Modifier,
  onValueChange: (String) -> Unit,
  submit: () -> Unit,
) {
  val focusManager = LocalFocusManager.current
  OutlinedTextField(
    value = value,
    onValueChange = onValueChange,
    modifier = modifier,
    keyboardOptions = KeyboardOptions(
      keyboardType = KeyboardType.Password,
      imeAction = ImeAction.Done,
    ),
    keyboardActions = KeyboardActions(
      onDone = {
        focusManager.clearFocus()
        submit()
      }
    ),
    visualTransformation = PasswordVisualTransformation(),
    label = { Text("パスワード") },
  )
}

今回はキーボードで決定を押すと submit 処理が走ってほしいため、submit 関数を受け取るようにしています。

メールアドレス入力欄とほぼ同じですが、visualTransformation = PasswordVisualTransformation() という見慣れない記述があります。これは、パスワードを非表示にする変換処理を登録しています。

最後にボタンの実装です。

@Composable
fun LoginButton(
  ajax: Boolean,
  modifier: Modifier = Modifier,
  submit: () -> Unit,
) {
  Button(onClick = submit, enabled = !ajax, modifier = modifier) {
    if (ajax) {
      CircularProgressIndicator(
        strokeWidth = 2.dp,
        modifier = Modifier.size(24.dp)
      )
    } else {
      Text("ログイン")
    }
  }
}

処理中はボタンイベントを無効にしたいため enabled = !ajax としています。

これらの要素を取り入れたフォームの実装は次のようになります。

@Composable
fun LoginForm() {
  var ajax by remember { mutableStateOf(false) }
  var email by remember { mutableStateOf("") }
  var password by remember { mutableStateOf("") }
  val scope = rememberCoroutineScope()
  val submit = {
    scope.launch(Dispatchers.IO) {
      ajax = true
      // ここにログイン処理を記述する
      ajax = false
    }
    Unit
  }
  Column(
    modifier = Modifier
      .fillMaxSize()
      .padding(16.dp)
  ) {
    EmailTextField(email, Modifier.fillMaxWidth()) { email = it }
    Spacer(Modifier.height(4.dp))
    PasswordTextField(password, Modifier.fillMaxWidth(), { password = it }, submit)
    Spacer(Modifier.height(12.dp))
    LoginButton(ajax, Modifier.fillMaxWidth().height(56.dp), submit)
  }
}

入力文字列などの情報はフォームに持たせています。こうすることで、ログイン処理を行うときに入力文字列を利用することができます。

3つの各要素では modifier を引数として受け取っていました。そのように実装することで、呼び出し側で細かなスタイルの調節ができるようになります。

終わりに

メールアドレス入力欄からパスワード入力欄にフォーカスを移動させるとき、はじめは方法が思い浮かばずに悩んでいましたが、LocalFocusManager.current を使うことでフォーカスを次に移す処理が簡単に実装できます。参考にしていただけると嬉しいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
1
Help us understand the problem. What are the problem?