👶

【Flutter】 新規登録における入力フォームの実装で考慮すべき点とその対応

2023/12/08に公開

2023 Flutter大学アドベントカレンダー5日目の記事になります。

ご覧いただきありがとうございます。ganです。今回は 【Flutter】 認証周りの実装時に考慮すべき点とその対応 ということで、Flutter を使用してモバイルアプリを開発する際の認証周りの実装時、特に入力フォーム周辺で、これは気をつけた方がいい思ったことをまとめました。実装後の確認としてもこの記事が役に立てば幸いです。

内容

新規登録の実装時に考慮すべき点とその対応

  1. フォーカスが当たった時のフォーム挙動
  2. フォーカスを外す処理の追加
  3. キーボードの指定

新規登録の実装時に考慮すべき点とその対応

新規登録はそのアプリの入り口にあたる部分であり、その体験を向上させることはユーザを確保するための重要な要素の一つです。ここで、ユーザの登録には一般的に、メールアドレスとパスワードを使用するものとソーシャルログイン (SNS アカウントや GitHub アカウントを使用したログイン方法) を使用するものの2つがあります。
ここでは、メールアドレスとパスワードを入力するフォームを実装することを想定して進めていきます。

1. フォーカスが当たった時のフォーム挙動

ユーザは、入力フォームを押した際、画面上にはキーボードが表示され入力待機状態になったことを確認できます。しかし、いくらカーソルがあるとはいえ、複数ある入力フォームの内のどこにフォーカスが当たっているのかはわかりません。そのため、フォーカスが当たった時に、その入力フォームの枠の色を変更するなどの視覚的な変化を与えることで、ユーザがどの入力フォームにフォーカスが当たっているのかをわかりやすくすることができます。

一般に、

TextFormField(
  // ...
  decoration: InputDecoration(
    focusedBorder: // ...,
    errorBorder: // ...,
  ),
  // ...
)

と各入力フォームで定義されることが多いです。この実装で上に記述したような懸念点の対応は十分できています。しかしながら、このような書き方の場合、各入力フォームで同じようなコードを書くことになります。これは、コードの重複を招くことになり、メンテナンス性が低下する原因となります。そこで、このようなケースでは、MaterialAppThemeDataInputDecorationTheme を追加することで、コードの重複を防ぐことができます。

return MaterialApp(
      theme: ThemeData(
        // ...,
        inputDecorationTheme: const InputDecorationTheme(
          contentPadding: EdgeInsets.all(16),
          focusedBorder: TextFormBorders.textFormFocusedBorder,
          enabledBorder: TextFormBorders.textFormEnabledBorder,
          focusedErrorBorder: TextFormBorders.textFormErrorBorder,
          errorBorder: TextFormBorders.textFormErrorBorder,
        ),
      ),
  );
OutlineInputBorder を定義した TextFormBorders クラス
class TextFormBorders {
  // キーボード表示時のフォームの枠線。
  static const textFormFocusedBorder = OutlineInputBorder(
    borderRadius: BorderRadius.all(Radius.circular(8)),
    borderSide: BorderSide(
      color: Colors.deepPurple,
      width: 2,
    ),
  );

  // 平常時のフォームの枠線。
  static const textFormEnabledBorder = OutlineInputBorder(
    borderRadius: BorderRadius.all(Radius.circular(8)),
    borderSide: BorderSide(
      color: Colors.grey,
    ),
  );

  // 入力中のエラー時の枠線。
  static const textFormErrorBorder = OutlineInputBorder(
    borderRadius: BorderRadius.all(Radius.circular(8)),
    borderSide: BorderSide(
      color: Colors.red,
      width: 2,
    ),
  );
}

これにより、各入力フォームで重複したコードを書くことなく、フォーカスが当たった時のフォームの枠の色を変更することができます。

通常時 フォーカス時
normal focusing

2. フォーカスを外す処理の追加

ユーザは、フォームの入力完了後にフォーカスが外れること、つまり、キーボードが閉じられることを期待しています。そのため、フォーカスが外れた時に、前述したような入力フォームの枠の色を変更するなどの視覚的な変化を与えることで、ユーザがフォームの入力が完了したことをわかりやすくすることができます。この時、特に、入力フォーム以外をタップすることでフォーカスが外れる実装をすべきです。そのためには、各入力フォームにおいて、

TextFormField(
  // ...
  onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
  // ...
)

とするのが望ましいです。

また、TextFormFieldonEditingComplete にも同様の対応をすることで、キーボードの完了ボタンを押した際にもフォーカスが外れるようにすることができます。

TextFormField(
  // ...
  onEditingComplete: () => FocusManager.instance.primaryFocus?.unfocus(),
  // ...
)

3. 入力値のバリデーションとキーボードの指定

メールアドレスやパスワードの入力フォームにおいて、入力値のバリデーションキーボードの指定を行うことは非常に重要です。これは、入力フォームにおいて、正しい形式でない文字列が入力された場合、その入力フォームの下にエラーメッセージを表示することで、ユーザに入力値が正しい形式でないことを伝えることができます。また、キーボードの指定については、入力内容に応じたキーボードの種類を指定することで、ユーザが入力しやすいようにすることができます。事前にメールアドレス、パスワードの正規表現によるバリデーションを用意しておきます。(正規表現についてはこの記事では詳しく説明しません。各自で適切な正規表現を使用してください。)

メールアドレスの場合 パスワードの場合
mail password
extension StringEx on String {
  /// メールアドレスの正規表現バリデーション
  bool isValidEmail() {
    return RegExp(
      r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$',
    ).hasMatch(this);
  }

  /// パスワードの正規表現バリデーション
  /// 8文字以上、大文字・小文字・数字をそれぞれ1文字以上含む必要がある。
  bool isValidPassword() {
    return RegExp(
      r'^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?[0-9]).{8,100}$',
    ).hasMatch(this);
  }
}

メールアドレスの場合

こちらはメールアドレスの場合の例です。inputFormatters を使用して入力できる文字を制限しています。また、keyboardType を使用してキーボードの種類を指定しています。errorText には、入力値が正しい形式でない場合に表示するエラーメッセージを設定しています。

TextFormField(
  // ...
  keyboardType: TextInputType.emailAddress,
  inputFormatters: [
    FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9@.+_-]')),
  ],
  errorText: _errorText,
  // ...
)
String? get _errorText {
  // 入力されたテキストを取得する。
  final text = controller.value.text;
  if (text.isNotEmpty && !text.isValidEmail()) {
    return '正しい形式で入力してください。';
  }
  // エラーがない場合は null を返す。
  return null;
}
keyboardType 指定無し keyboardType 指定有り
normal specific_keyboard_type

パスワードの場合

こちらはパスワードの例です。メールアドレスの場合に対して、keyboardTypeTextInputType.visiblePassword を指定し、inputFormatters で同様に入力できる文字を制限しています。こちらは、大文字、小文字、数字、記号を入力できるようにしています。errorText には、入力値が正しい形式でない場合に表示するエラーメッセージを設定しています。

TextFormField(
  // ...
  keyboardType: TextInputType.visiblePassword,
  inputFormatters: [
    FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9!-/:-@[-`{-~]')),
  ],
  errorText: _errorText,
  // ...
)
String? get _errorText {
  // 入力されたテキストを取得する。
  final text = controller.value.text;
  if (text.isNotEmpty && !text.isValidPassword()) {
    return '大文字、小文字、数字を含む8文字以上';
  }
  // エラーがない場合は null を返す。
  return null;
}

ここで一つ面白いのが、keyboardType: TextInputType.visiblePassword, としていますが実は、キーボードのタイプはこれを指定するかどうかに寄りません。キーボードのタイプは TextFormFieldisObscure の指定よって決まります。isObscure は、入力された文字を隠すかどうかを決めるものです。true にすると、入力された文字が隠され、false にすると、入力された文字は隠されません。この isObscure は、パスワードの入力フォームにおいて、入力された文字を隠すために使用されます。そのため、keyboardTypeTextInputType.visiblePassword を指定しているかどうかに関わらず、isObscuretrue になっている場合は、キーボードのタイプは TextInputType.visiblePassword になります。逆に、isObscurefalse になっている場合は、キーボードのタイプは TextInputType.text になります。

isObscure が false isObscure が true
isObscure_is_false isObscure_is_true

まとめ

いかがだったでしょうか。ユーザ登録はアプリのユーザ数を増やす上で最初の大きな壁です。ぜひこの記事を参考に、またさらに細部までこだわることでユーザ登録の体験を向上させていきましょう!

GitHubで編集を提案

Discussion