個人開発者が勉強がてらまとめているだけのサイトです

OTP 入力(コード入力) OTP / PIN Code Input

4〜6桁のコードを1文字ずつの箱に入れる入力欄。打つと次の箱へ進み、全部埋まると自動で確認が始まる。

別名
認証コード入力ワンタイムパスワード確認コードPIN入力6桁の入力欄暗証番号入力Verification CodePin Code Fields
動き
1文字打つと次の箱へ全部埋まると自動で確認
作り方
自作 標準ウィジェットの組み合わせで自分で作ります

本物の Flutter ウィジェットが動きます(初回は数秒かかります)

別タブで大きく開く ↗

どんなもの?

SMS やメールで届く確認コードを入れてもらう形です。

箱ごとに TextField を並べると、フォーカスの移動や消すときの戻りが複雑になります。

見えない TextField を1つだけ置いて、打った文字を箱に振り分けて描くと、貼り付けや消去も普通に動きます。

Opacity で隠すときは alwaysIncludeSemantics: true を付けます。付けないと、読み上げ機能が有効な Web では入力欄ごと消えて文字を受け取れませんでした。

keyboardType: TextInputType.number と maxLength で数字だけ・桁数までに絞り、

autofillHints: [AutofillHints.oneTimeCode] を付けると、iOS では届いた SMS のコードを候補に出せます。

次に打つ箱の枠を目立たせ、埋まったら onChanged で桁数を見て確認処理を始めます。

パッケージなら pinput や pin_code_fields で、見た目の種類やアニメーションを含めて作れます。

こんなときに

  • SMS・メールで届いた確認コード、アプリのロック解除用の暗証番号

避けたい場面

  • 桁数が決まっていない入力(普通のテキスト入力にする)

最小コード

Stack(children: [
  // 見えない入力欄が1つだけ(貼り付け・消去も普通に効く)
  Opacity(
    opacity: 0,
    alwaysIncludeSemantics: true, // 読み上げ有効の Web で入力欄が消えないように
    child: TextField(
      controller: code,
      focusNode: focus,
      maxLength: 6,
      keyboardType: TextInputType.number,
      autofillHints: const [AutofillHints.oneTimeCode],
      onChanged: (v) { if (v.length == 6) verify(v); },
    ),
  ),
  // 打った文字を6つの箱に描く
  GestureDetector(
    onTap: focus.requestFocus,
    child: Row(children: [
      for (var i = 0; i < 6; i++)
        _Box(
          char: i < code.text.length ? code.text[i] : '',
          active: i == code.text.length,
        ),
    ]),
  ),
])

使うウィジェット

TextField(maxLength)StackAnimatedContainer

パッケージ: pinput ↗pin_code_fields ↗

似ているもの・一緒に見たいもの

触ってみる ↗
入力・フォーム パスワード表示切替Password Visibility Toggle

パスワード欄の右にある目のアイコンを押すと、●●● で隠していた文字が見えるようになる。もう一度押すと隠れる。

目のアイコンを押す
触ってみる ↗
入力・フォーム ログイン画面Login Screen

メールとパスワードを入れてログインする画面。新規登録との切り替えや、Google などでのログインボタンも並ぶ。

ログインと新規登録を切り替え