Flutterアプリケーション開発概論

【新規登録画面】名前・所属・メール・パスワードを入力する画面を作る

14LINE風チームタスク管理アプリを作りながら、ログイン・データベース・権限管理を学ぶ
FlutteriOSAndroidMacOSWindows基礎から学ぶ開発アプリ開発

このページでやること

このページでは、新規登録画面を作ります。

新規登録画面とは、まだアカウントを持っていない人が、名前・所属・メールアドレス・パスワードを入力して、アプリを使い始めるための画面です。

今回作る画面は、次のようなイメージです。

新規登録

プロフィール作成

名前
所属
メールアドレス
パスワード

[新規登録]

このページでは、まず画面を作るところまで進めます。

Firebase Authenticationにユーザーを作る処理や、Firestoreにプロフィールを保存する処理は、次のページで詳しく見ていきます。


今日のゴール

RegisterPage を作って、ログイン画面の「新規登録はこちら」から移動できるようにします。

ログイン画面
↓
新規登録はこちら
↓
新規登録画面

このページでは、次の4つの入力欄を作ります。

入力欄一言説明
名前アプリ内で表示する名前
所属学校名、部署名、チーム名など
メールアドレスログインに使うメール
パスワードログインに使う秘密の文字

このページで出てくる単語

単語一言説明
RegisterPage新規登録画面を作るWidget
StatefulWidget画面の中で変わる値を持てるWidget
TextEditingController入力欄の文字を管理する道具
AppTextFieldこの教材で作った共通入力欄
ScaffoldFlutter画面の基本の土台
AppBar画面上部のバー
SingleChildScrollView画面が小さいときにスクロールできる部品
FilledButton塗りつぶし型のボタン
dispose使い終わった道具を片付ける処理

Widget とは、Flutterの画面部品のことです。


npmや環境変数はこのページで必要?

このページでは、npmは使いません。

npmとは、Node.jsのパッケージ管理ツールです。

環境変数も設定しません。

環境変数とは、パソコン全体で使う設定値のことです。

このページでやることは、これだけです。

main.dartを開く
↓
RegisterPageを書く
↓
保存する
↓
flutter runで確認する

Step 1:main.dartを開く

Flutterプロジェクトの中で、次のファイルを開きます。

lib/main.dart

VS Codeを使っている場合は、ターミナルで次を実行してもOKです。

code lib/main.dart

code が使えない場合は、VS Codeの左側から lib/main.dart を開いてください。


Step 2:古い仮のRegisterPageがある場合は消す

前のページで、仮の RegisterPage を作った人は、そのコードを消してください。

仮のコードは、だいたい次のような形です。

class RegisterPage extends StatelessWidget {
  const RegisterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('新規登録'),
      ),
      body: const Center(
        child: Text('新規登録画面は次のページで作ります'),
      ),
    );
  }
}

この仮コードを、今回作る本物の RegisterPage に差し替えます。


Step 3:RegisterPageを追加する

LoginPage の下あたりに、次のコードを追加します。

class RegisterPage extends StatefulWidget {
  const RegisterPage({super.key});

  @override
  State<RegisterPage> createState() => _RegisterPageState();
}

RegisterPage は、新規登録画面です。

ここでは StatefulWidget を使います。

StatefulWidget とは、画面の中で変わる値を持てるWidgetです。

新規登録画面では、入力された名前、所属、メールアドレス、パスワード、読み込み中かどうか、エラー文などが変わります。

そのため、StatefulWidget を使います。


Step 4:入力欄用のControllerを作る

続けて、次のコードを追加します。

class _RegisterPageState extends State<RegisterPage> {
  final displayNameController = TextEditingController();
  final departmentController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  bool isLoading = false;
  String? errorText;

TextEditingController は、入力欄に入力された文字を取り出すための道具です。

今回は4つの入力欄があるので、Controllerも4つ作ります。

Controller入力する内容
displayNameController名前
departmentController所属
emailControllerメールアドレス
passwordControllerパスワード

isLoading は、新規登録中かどうかを表します。

errorText は、登録に失敗したときのエラー文を入れます。

null は、値がない状態のことです。


Step 5:disposeを書く

続けて、次のコードを追加します。

  @override
  void dispose() {
    displayNameController.dispose();
    departmentController.dispose();
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

dispose は、使い終わった道具を片付ける処理です。

TextEditingController は画面が消えるときに片付ける必要があります。

難しく考えなくて大丈夫です。

入力欄のControllerを作ったら、最後に dispose() で片付ける、と覚えてください。


Step 6:register()を仮で作る

このページでは、まだ本格的な登録処理は作りません。

まずは、ボタンを押したときに動く仮の register() を作ります。

続けて、次のコードを追加します。

  Future<void> register() async {
    setState(() {
      errorText = null;
    });

    final displayName = displayNameController.text.trim();
    final department = departmentController.text.trim();
    final email = emailController.text.trim();
    final password = passwordController.text;

    if (displayName.isEmpty ||
        department.isEmpty ||
        email.isEmpty ||
        password.isEmpty) {
      setState(() {
        errorText = 'すべての項目を入力してください。';
      });
      return;
    }

    setState(() {
      errorText = '次のページでFirebase登録処理を追加します。';
    });
  }

trim() は、文字の前後にある余分な空白を消す処理です。

isEmpty は、文字が空かどうかを調べるものです。

return は、ここで処理を止めるという意味です。

この段階では、入力チェックだけ行います。

入力チェックとは、必要な項目が入っているか確認することです。


Step 7:画面部分を追加する

続けて、同じ _RegisterPageState の中に、次のコードを追加します。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.bg,
      appBar: AppBar(
        title: const Text('新規登録'),
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(20),
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 420),
              child: AppCard(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    const Text(
                      'プロフィール作成',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: AppColors.text,
                        fontSize: 24,
                        fontWeight: FontWeight.w900,
                      ),
                    ),
                    const SizedBox(height: 8),
                    const Text(
                      'チームで使う名前とログイン情報を入力します',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: AppColors.subText,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    const SizedBox(height: 24),
                    AppTextField(
                      controller: displayNameController,
                      label: '名前',
                    ),
                    const SizedBox(height: 12),
                    AppTextField(
                      controller: departmentController,
                      label: '所属',
                    ),
                    const SizedBox(height: 12),
                    AppTextField(
                      controller: emailController,
                      label: 'メールアドレス',
                      keyboardType: TextInputType.emailAddress,
                    ),
                    const SizedBox(height: 12),
                    AppTextField(
                      controller: passwordController,
                      label: 'パスワード',
                      obscureText: true,
                    ),
                    if (errorText != null) ...[
                      const SizedBox(height: 12),
                      ErrorBox(message: errorText!),
                    ],
                    const SizedBox(height: 20),
                    FilledButton(
                      onPressed: isLoading ? null : register,
                      child: Text(isLoading ? '登録中...' : '新規登録'),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

これで、新規登録画面のUIができます。

Scaffold は、画面の基本の土台です。

AppBar は、画面上部のバーです。

SafeArea は、スマホのノッチやステータスバーに画面が重ならないようにする部品です。

SingleChildScrollView は、画面が小さいときにスクロールできるようにする部品です。

ConstrainedBox は、横幅の最大サイズを決める部品です。


ここまでの完成コード

このページで追加する RegisterPage は、次の形です。

class RegisterPage extends StatefulWidget {
  const RegisterPage({super.key});

  @override
  State<RegisterPage> createState() => _RegisterPageState();
}

class _RegisterPageState extends State<RegisterPage> {
  final displayNameController = TextEditingController();
  final departmentController = TextEditingController();
  final emailController = TextEditingController();
  final passwordController = TextEditingController();

  bool isLoading = false;
  String? errorText;

  @override
  void dispose() {
    displayNameController.dispose();
    departmentController.dispose();
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  Future<void> register() async {
    setState(() {
      errorText = null;
    });

    final displayName = displayNameController.text.trim();
    final department = departmentController.text.trim();
    final email = emailController.text.trim();
    final password = passwordController.text;

    if (displayName.isEmpty ||
        department.isEmpty ||
        email.isEmpty ||
        password.isEmpty) {
      setState(() {
        errorText = 'すべての項目を入力してください。';
      });
      return;
    }

    setState(() {
      errorText = '次のページでFirebase登録処理を追加します。';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.bg,
      appBar: AppBar(
        title: const Text('新規登録'),
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(20),
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 420),
              child: AppCard(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.stretch,
                  children: [
                    const Text(
                      'プロフィール作成',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: AppColors.text,
                        fontSize: 24,
                        fontWeight: FontWeight.w900,
                      ),
                    ),
                    const SizedBox(height: 8),
                    const Text(
                      'チームで使う名前とログイン情報を入力します',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                        color: AppColors.subText,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    const SizedBox(height: 24),
                    AppTextField(
                      controller: displayNameController,
                      label: '名前',
                    ),
                    const SizedBox(height: 12),
                    AppTextField(
                      controller: departmentController,
                      label: '所属',
                    ),
                    const SizedBox(height: 12),
                    AppTextField(
                      controller: emailController,
                      label: 'メールアドレス',
                      keyboardType: TextInputType.emailAddress,
                    ),
                    const SizedBox(height: 12),
                    AppTextField(
                      controller: passwordController,
                      label: 'パスワード',
                      obscureText: true,
                    ),
                    if (errorText != null) ...[
                      const SizedBox(height: 12),
                      ErrorBox(message: errorText!),
                    ],
                    const SizedBox(height: 20),
                    FilledButton(
                      onPressed: isLoading ? null : register,
                      child: Text(isLoading ? '登録中...' : '新規登録'),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Step 8:ログイン画面から移動できるか確認する

LoginPage の中に、次のコードがあるか確認します。

TextButton(
  onPressed: isLoading
      ? null
      : () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (_) => const RegisterPage(),
            ),
          );
        },
  child: const Text('新規登録はこちら'),
),

Navigator は、画面を移動するための仕組みです。

push は、新しい画面を上に重ねて表示する命令です。

MaterialPageRoute は、Material Design風に画面移動するための部品です。

このコードがあると、ログイン画面から新規登録画面へ移動できます。


Step 9:保存する

main.dart を保存します。

Macの場合:

command + S

Windowsの場合:

Ctrl + S

Step 10:実行する

ターミナルで実行します。

flutter run

すでに起動している場合は、ターミナルで r を押します。

r

r はホットリロードです。

ホットリロードとは、アプリを起動したまま画面の変更を反映する機能です。


Step 11:画面を確認する

ログイン画面で、次を押します。

新規登録はこちら

次のような画面が表示されればOKです。

新規登録

プロフィール作成

名前
所属
メールアドレス
パスワード

[新規登録]

ここでは、まだ本当にユーザー登録できなくても大丈夫です。

このページのゴールは、新規登録画面のUIを作ることです。


Step 12:入力チェックを確認する

何も入力せずに「新規登録」ボタンを押してみます。

次のエラーが表示されればOKです。

すべての項目を入力してください。

次に、4つの入力欄をすべて入力してから「新規登録」を押します。

名前:山田太郎
所属:開発チーム
メールアドレス:test@example.com
パスワード:password123

次の表示が出ればOKです。

次のページでFirebase登録処理を追加します。

これは仮の表示です。

次のページで、実際にFirebase Authenticationへ登録する処理に変更します。


1か所だけ変更してみる

画面の説明文を変えてみます。

この部分を探してください。

'チームで使う名前とログイン情報を入力します',

次のように変えてみます。

'はじめて使う方は、ここから登録してください',

保存して、ホットリロードします。

r

画面の文字が変われば成功です。

確認できたら、好きな表現に戻してOKです。


よくあるエラーと直し方

エラー原因直し方
RegisterPage is already defined仮のRegisterPageが残っている古いRegisterPageを削除する
AppTextField isn't defined共通UIがないAppTextField を追加する
AppCard isn't defined共通UIがないAppCard を追加する
ErrorBox isn't defined共通UIがないErrorBox を追加する
setState isn't definedState クラスの外に書いている_RegisterPageState の中に書く
Navigator でエラーmaterial.dart のimport漏れimport 'package:flutter/material.dart'; を確認
画面が変わらない保存していないcommand + S
ホットリロードで直らない画面構成が大きく変わったアプリを再起動する

RegisterPage is already defined** が出たとき**

次のようなエラーが出た場合です。

The name 'RegisterPage' is already defined.

これは、RegisterPage が2つあるという意味です。

前に作った仮の RegisterPage が残っている可能性があります。

main.dart の中で class RegisterPage を検索してください。

VS Codeなら、

command + F

で検索できます。

class RegisterPage が2つあれば、古い仮の方を削除します。


setState isn't defined** が出たとき**

setState は、State クラスの中で使えます。

今回なら、次の中に書く必要があります。

class _RegisterPageState extends State<RegisterPage> {
  // ここに書く
}

RegisterPage クラスの外に register() を書くと、setState が使えません。


最短作業まとめ

読むのが大変な人は、ここだけ見てください。

1. lib/main.dartを開く
2. 古い仮のRegisterPageがあれば消す
3. 新しいRegisterPageを追加する
4. LoginPageの「新規登録はこちら」からRegisterPageへ移動できるか確認する
5. 保存する
6. flutter run

実行コマンドです。

flutter run

起動中なら、

r

チェックリスト

□ main.dartを開いた
□ 古い仮のRegisterPageを削除した
□ RegisterPageをStatefulWidgetで作った
□ 名前用Controllerを作った
□ 所属用Controllerを作った
□ メール用Controllerを作った
□ パスワード用Controllerを作った
□ disposeを書いた
□ register()を仮で作った
□ AppTextFieldを4つ置いた
□ 新規登録ボタンを作った
□ 入力チェックが動いた
□ 保存した
□ flutter runで確認した

ミニ確認問題

Q1. RegisterPageは何をする画面ですか?

回答

新しいユーザーが、名前・所属・メールアドレス・パスワードを入力して登録するための画面です。


Q2. なぜRegisterPageはStatefulWidgetで作りますか?

回答

入力内容、読み込み中かどうか、エラー文など、画面の中で変わる値があるからです。


Q3. TextEditingControllerは何のために使いますか?

回答

入力欄に入力された文字を取り出すために使います。


Q4. このページでnpmや環境変数は必要ですか?

回答

必要ありません。

このページでは、新規登録画面のUIを作るだけです。


このページのまとめ

  • このページでは、新規登録画面のUIを作った。
  • 入力欄は、名前・所属・メールアドレス・パスワードの4つ。
  • 入力された文字は TextEditingController で管理する。
  • RegisterPage は、変化する値があるので StatefulWidget で作る。
  • dispose() でControllerを片付ける。
  • まずは仮の register() で入力チェックだけ行う。
  • ログイン画面の「新規登録はこちら」から移動できるようにする。
  • このページではnpmや環境変数は不要。

次のページでやること

次のページでは、実際にFirebase Authenticationへユーザーを作成します。

createUserWithEmailAndPassword を使って、入力したメールアドレスとパスワードで新しいアカウントを作ります。

教材トップへ戻る