Help us understand the problem. What is going on with this article?

【Flutter】FirebaseのAuthでEmail&パスワード認証を実装する(前半)

先週初めてFirebaseを使ってみたので備忘録的に残しておきます。

フォームでEmailとパスワードを入力し、Firebaseとの接続を確認できたところをゴールとします。

参考になると思う方

  • Flutterの環境構築ができてる

とりあえずHello World

main.dart
import 'package:flutter/material.dart';
import 'login_page.dart';

void main() {
  runApp(new MyApp());
}

class MyApp extends StatelessWidget {

  @override
    Widget build(BuildContext context) {

      return new MaterialApp(
        title: 'Flutter auth',
        theme: new ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: new LoginPage(),
      );
    }
}

login_page.dartファイルを作成し、こちらにAuthの具体的なコードを書いていきます。

login_page.dart
import 'package:flutter/material.dart';

class LoginPage extends StatefulWidget {

  @override
  State<StatefulWidget> createState() => new _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Flutter auth'),
      ),
      body: new Container(
        child: new Text('Hello World')
      ),
    );
  }
}

home: new LoginPage() homeにlogin_page.dartファイルのLoginPageクラスを返してるということですね。

とりあえずこんな感じ。

スクリーンショット 2020-02-26 11.27.38.png

フォームの作成

bodyのContainerに変更を加えていきます。

login_page.dart
class _LoginPageState extends State<LoginPage> {

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Flutter auth'),
      ),
      body: new Container(
        //追加
        child: new Form(
          child: new Column(
            children: <Widget>[
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Email'),
              ),
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Password'),
              ),
            ],
          )
        )
      ),
    );
  }

ここでchildとchildrenの違いについて説明しておきます。

child takes a single widget
child: Text('foo')

children takes a list of widgets
children: [Text('foo'), Text('bar')]

参考: https://stackoverflow.com/questions/54053909/what-is-the-difference-between-child-and-children-property-in-flutter

childは一つのWidgetのみをとり、childrenはWidgetをリストとしていくつもとることができると。

今回の例でもForm Widgetの中のchildにColumn Widgetを入れ、そのプロパティにchildrenを使用することでTextFormFieldとして二つのFormをリストとして表示しています。

スクリーンショット 2020-02-26 12.17.03.png

ボタンの作成

login_page.dart
class _LoginPageState extends State<LoginPage> {

  //追加
  void validateAndSave() {

  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Flutter auth'),
      ),
      body: new Container(
     //追加
        padding: EdgeInsets.all(16.0),
        child: new Form(
          child: new Column(
            //追加
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Email'),
              ),
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Password'),
              ),
              //追加
              new RaisedButton(
                child: new Text('Login', style: new TextStyle(fontSize: 20.0)),
                onPressed: validateAndSave,
              ),
            ],
          )
        )
      ),
    );
  }
}

とりあえずボタンを作ります。

new RaisedButton(
                child: new Text('Login', style: new TextStyle(fontSize: 20.0)),
                onPressed: validateAndSave,

onPressedでボタンを押した時の処理を実行します。

処理を行うvalidateAndSave関数はあとで記述します。

あとはイケてないところを少し修正します。

padding: EdgeInsets.all(16.0),

これでスペースを整え、

 crossAxisAlignment: CrossAxisAlignment.stretch,

こちらでボタンを画面いっぱいに引き延ばします。

種別 内容
baseline テキストのベースラインを揃えるように配置される
center 中央寄せになる
start Columnなら左寄せ(厳密にはTextDirectionによって開始位置が決まる)
Rowなら上寄せになる(厳密にはVerticalDirectionによって開始位置が決まる)
end Columnなら右寄せ(厳密にはTextDirectionによって開始位置が決まる)
Rowなら上寄せになる(厳密にはVerticalDirectionによって開始位置が決まる)
streach 子要素の幅また高さを埋めるように配置する

参考:https://qiita.com/kaleidot725/items/35f6940e594bdf073eb8#crossaxisalignment

crossAxisAlignmentについてはこちらに詳しく書いてあるので参考にしてみてください。

スクリーンショット 2020-02-26 12.42.36.png

UIはいい感じになりましたね。

loginフォームにvalidationをかけ、saveする

login_page.dart
body: new Container(
        padding: EdgeInsets.all(16.0),
        child: new Form(
          //追加
          key: formKey,
          child: new Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Email'),
                //追加
                validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null,
                onSaved: (value) => _email = value,
              ),
              new TextFormField(
                decoration: new InputDecoration(labelText: 'Password'),
                //追加
                obscureText: true,
                validator: (value) => value.isEmpty ? 'Password can\'t be empty' : null,
                onSaved: (value) => _password = value,
              ),
              new RaisedButton(
                child: new Text('Login', style: new TextStyle(fontSize: 20.0)),
                onPressed: validateAndSave,
              ),
            ],
          )
        )
      ),

keyについてなのですが、少し難しく、まだ浅い理解に留まっています:confounded:

Controls how one widget replaces another widget in the tree.

In addition, using a GlobalKey as the widget's key allows the element to be moved around the tree (changing parent) without losing state. When a new widget is found (its key and type do not match a previous widget in the same location), but there was a widget with that same global key elsewhere in the tree in the previous frame, then that widget's element is moved to the new location.

Flutter公式: https://api.flutter.dev/flutter/widgets/Widget/key.html

ツリー内にあるWidgetを他のWidgetに入れ替えるのをコントロールすると。

さらに、GlobalKeyをWidgetのkeyとして使用すると状態を失うことなく、Elementをツリー内で移動できると(親を変える)。

ああ〜なるほどね...(わからん)

一応良さげな記事を見つけたので置いておきます。これはFlutterというフレームワークそのものの理解が必要そうです。

動画をみてもわからなかった...

Keys! What are they good for?: https://medium.com/flutter/keys-what-are-they-good-for-13cb51742e7d

validator: (value) => value.isEmpty ? 'Email can\'t be empty' : null,
onSaved: (value) => _email = value,

formが空だった場合にはnullとEmail can't be emptyというテキストを返し、入力された場合はセーブします。

パスワードも同じように実装しますが、打った文字が隠れるように下記のコードを記述します。

obscureText: true,

次にボタンを押した時の処理を加えていきます。

login_page.dart
class _LoginPageState extends State<LoginPage> {

  final formKey = new GlobalKey<FormState>();

  String _email;
  String _password; 

  void validateAndSave() {
    final form = formKey.currentState;
    if(form.validate()) {
      form.save();
      print('Form is valid. Email: $_email, password: $_password');
    } else {
      print('Form is Invalid. Email: $_email, password: $_password');
    }
  }

上からformKeyと_email、_passwordを定義します。

定数の前に_(アンダースコア)がついていることに気づくと思うのですが、これはdartの言語使用で、そのライブラリ内だけ参照できるように制限をかけるものらしいです。

スクリーンショット 2020-02-26 13.18.10.png

空欄のままボタンを押すと上の写真のようにDEBUG CONSOLEではflutter: Form is Invalid. Email: null, password: nullと表示されたでしょうか?

そのあとEmailとPasswordに「test」と入力してボタンを押すとflutter: Form is valid. Email: test, password: test
Form is validしたことを確認できると思います:relaxed:

あとはFirebaseと接続してデータベースに反映させるだけです!!

Firebaseのライブラリを追加

このサイトに飛んで最新バージョンのfirebase_authをインストールします。

dart package: https://pub.dev/packages/firebase_auth#-installing-tab-

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  firebase_auth: ^0.15.4

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2

セーブすると自動的にflutter pub getが走ると思います。

main.dart
import 'package:firebase_auth/firebase_auth.dart';

ここからはFirebaseの設定に入るのですが、長くなってきたので二つの記事に分けます。

ではこちらの記事で:wave:

【Flutter】FirebaseのAuthでメアド&パスワード認証を実装する(後半)
https://qiita.com/iketeruhiyoko/items/7d0718bc6210ed545913

参考

Flutter & Firebase Auth 02 - Create, validate and save a login form + Firebase iOS auth integration
https://www.youtube.com/watch?v=BNOUtPSN-kA

Pogre
スタートアップに就職予定の大学4年です。
https://note.com/pogre
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