Статьи

Учебник по аутентификации Flutter Firebase

В этом посте по аутентификации Flutter Firebase мы рассмотрим, как мы можем аутентифицировать пользователей в нашем приложении Firebase с помощью плагина Flutter Firebase.

Если вы не знакомы с флаттером, я бы рекомендовал прочитать это, прежде чем идти дальше. Это даст вам основную идею о флаттере и поможет вам вместе с этим уроком.

https://ayusch.com/getting-started-with-flutter-app-development/

Мы будем создавать простое приложение с экраном входа и домашней страницей. Он будет иметь возможность разрешить пользователю вход и выход из приложения. У нас также будет функциональность, позволяющая пользователю зарегистрироваться в нашем приложении Firebase.

Вот основной поток приложения:

Итак, начнем!

Создать приложение флаттера

Перейдите в Android Studio и создайте приложение Flutter, нажав New -> Flutter Project и следуя указаниям мастера.

Удалите код для счетчика по умолчанию и добавьте следующие строки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
import 'package:flutter/material.dart';
import 'package:flutter_firebase_auth/root_page.dart';
import 'LoginSignupPage.dart';
import 'authentication.dart';
 
void main() => runApp(MyApp());
 
class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Authentication AndroidVille',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: RootPage(
        auth: new Auth(),
      ),
    );
  }
}

Мы скоро создадим RootPage.

Примечание . Не забудьте использовать артефакты AndroidX. Плагин Firebase для флаттера содержит некоторые аннотации, которые не поддерживаются в AndroidX. Пока они не будут обновлены, давайте не будем использовать AndroidX.

Добавление зависимостей

Нам нужно добавить некоторые зависимости в android / flutter для того, чтобы flutter использовал аутентификацию firebase.

Сначала добавьте это в файл build.gradle уровня вашего проекта . Для проекта флаттера это можно найти в Android / build.gradle

1
classpath 'com.google.gms:google-services:4.3.2'

Далее нам нужно применить плагин google-services на уровне приложения build.gradle . Это можно найти в android / app / build.gradle. Добавьте эту строку в конец файла.

1
apply plugin: 'com.google.gms.google-services'

Наконец, нам нужно добавить плагин Firebase для флаттера. Откройте pubspec.yaml и добавьте следующие строки в зависимости:

1
firebase_auth: ^0.6.6

Создание службы аутентификации Flutter Firebase

Далее нам нужно создать сервис аутентификации для системы входа в систему firebase от flutter. Это будет использоваться всеми страницами (или действиями в Android) для связи с Firebase.

Создайте новый файл дартс с именем: authentication.dart

Сначала мы добавим абстрактный класс BaseAuth, который будет реализован нашим классом Auth. Он содержит основные методы входа в систему, регистрации, получения информации о пользователе и выхода из системы.

1
2
3
4
5
6
7
8
9
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
 
abstract class BaseAuth {
  Future<String> signIn(String email, String password);
  Future<String> signUp(String email, String password);
  Future<FirebaseUser> getCurrentUser();
  Future<void> signOut();
}

В этом же файле создайте класс с именем: Auth и реализуйте класс BaseAuth. Переопределите все методы, как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
 
abstract class BaseAuth {
  Future<String> signIn(String email, String password);
  Future<String> signUp(String email, String password);
  Future<FirebaseUser> getCurrentUser();
  Future<void> signOut();
}
 
class Auth implements BaseAuth {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
 
  Future<String> signIn(String email, String password) async {
    FirebaseUser user = await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
    return user.uid;
  }
 
  Future<String> signUp(String email, String password) async {
    FirebaseUser user = await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
    return user.uid;
  }
 
  Future<FirebaseUser> getCurrentUser() async {
    FirebaseUser user = await _firebaseAuth.currentUser();
    return user;
  }
 
  Future<void> signOut() async {
    return _firebaseAuth.signOut();
  }
}

Здесь мы создаем глобальный экземпляр firebase и используем его для входа / выхода пользователя.

Создание корневой страницы

Нам нужно создать рут, чтобы определить, вошел ли пользователь в систему или нет. Если пользователь вошел в систему , мы направим его на домашнюю страницу, иначе мы покажем ему экран входа / регистрации.

Создайте root_page.dart, как показано ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import 'package:flutter/material.dart';
import 'authentication.dart';
import 'LoginSignupPage.dart';
import 'home_page.dart';
 
class RootPage extends StatefulWidget {
  RootPage({this.auth});
 
  final BaseAuth auth;
 
  @override
  State<StatefulWidget> createState() => new _RootPageState();
}
 
enum AuthStatus {
  NOT_DETERMINED,
  LOGGED_OUT,
  LOGGED_IN,
}
 
class _RootPageState extends State<RootPage> {
  AuthStatus authStatus = AuthStatus.NOT_DETERMINED;
  String _userId = "";
 
  @override
  void initState() {
    super.initState();
    widget.auth.getCurrentUser().then((user) {
      setState(() {
        if (user != null) {
          _userId = user?.uid;
        }
        authStatus =
            user?.uid == null ? AuthStatus.LOGGED_OUT : AuthStatus.LOGGED_IN;
      });
    });
  }
 
  void _onLoggedIn() {
    widget.auth.getCurrentUser().then((user) {
      setState(() {
        _userId = user.uid.toString();
      });
    });
    setState(() {
      authStatus = AuthStatus.LOGGED_IN;
    });
  }
 
  void _onSignedOut() {
    setState(() {
      authStatus = AuthStatus.LOGGED_OUT;
      _userId = "";
    });
  }
 
  Widget progressScreenWidget() {
    return Scaffold(
      body: Container(
        alignment: Alignment.center,
        child: CircularProgressIndicator(),
      ),
    );
  }
 
  @override
  Widget build(BuildContext context) {
    switch (authStatus) {
      case AuthStatus.NOT_DETERMINED:
        return progressScreenWidget();
        break;
      case AuthStatus.LOGGED_OUT:
        return new LoginSignupPage(
          auth: widget.auth,
          onSignedIn: _onLoggedIn,
        );
        break;
      case AuthStatus.LOGGED_IN:
        if (_userId.length > 0 && _userId != null) {
          return new HomePage(
            userId: _userId,
            auth: widget.auth,
            onSignedOut: _onSignedOut,
          );
        } else
          return progressScreenWidget();
        break;
      default:
        return progressScreenWidget();
    }
  }
}

Здесь главное отметить метод сборки. Для разных состояний пользователя мы возвращаем разные виджеты. Мы скоро создадим LoginSignupPage и HomePage .

Для отображения индикатора выполнения мы используем CircularProgressIndicator по умолчанию, предоставленный Flutter .

Также обратите внимание, что мы передаем объект auth в конструктор. Это передается из main.dart

Создание страницы входа в систему

Это самая важная часть, мы будем создавать форму входа / регистрации. Мы будем различать эти формы на основе formMode .

Всего у нас будет 6 виджетов:

  1. Поле для электронной почты.
  2. Поле для пароля.
  3. Кнопка входа
  4. Кнопка для переключения между логином и формой регистрации.
  5. Индикатор
  6. Виджет сообщения об ошибке.

Вот код для LoginSignupPage:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'authentication.dart';
 
class LoginSignupPage extends StatefulWidget {
  LoginSignupPage({this.auth, this.onSignedIn});
 
  final BaseAuth auth;
  final VoidCallback onSignedIn;
 
  @override
  State<StatefulWidget> createState() => new _LoginSignupPageState();
}
 
enum FormMode { LOGIN, SIGNUP }
 
class _LoginSignupPageState extends State<LoginSignupPage> {
  final _formKey = new GlobalKey<FormState>();
 
  String _email;
  String _password;
  String _errorMessage = "";
 
  // this will be used to identify the form to show
  FormMode _formMode = FormMode.LOGIN;
  bool _isIos = false;
  bool _isLoading = false;
 
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Flutter login demo"),
      ),
      body: Column(
        children: <Widget>[
          formWidget(),
          loginButtonWidget(),
          secondaryButton(),
          errorWidget(),
          progressWidget()
        ],
      ),
    );
  }
 
  Widget progressWidget() {
    if (_isLoading) {
      return Center(child: CircularProgressIndicator());
    }
    return Container(
      height: 0.0,
      width: 0.0,
    );
  }
 
  Widget formWidget() {
    return Form(
      key: _formKey,
      child: Column(
        children: <Widget>[
          _emailWidget(),
          _passwordWidget(),
        ],
      ),
    );
  }
 
  Widget _emailWidget() {
    return Padding(
      padding: const EdgeInsets.fromLTRB(0.0, 100.0, 0.0, 0.0),
      child: TextFormField(
        maxLines: 1,
        keyboardType: TextInputType.emailAddress,
        autofocus: false,
        decoration: new InputDecoration(
            hintText: 'Enter Email',
            icon: new Icon(
              Icons.mail,
              color: Colors.grey,
            )),
        validator: (value) => value.isEmpty ? 'Email cannot be empty' : null,
        onSaved: (value) => _email = value.trim(),
      ),
    );
  }
 
  Widget _passwordWidget() {
    return Padding(
      padding: const EdgeInsets.fromLTRB(0.0, 15.0, 0.0, 0.0),
      child: new TextFormField(
        maxLines: 1,
        obscureText: true,
        autofocus: false,
        decoration: new InputDecoration(
            hintText: 'Password',
            icon: new Icon(
              Icons.lock,
              color: Colors.grey,
            )),
        validator: (value) => value.isEmpty ? 'Password cannot be empty' : null,
        onSaved: (value) => _password = value.trim(),
      ),
    );
  }
 
  Widget loginButtonWidget() {
    return new Padding(
        padding: EdgeInsets.fromLTRB(0.0, 45.0, 0.0, 0.0),
        child: new MaterialButton(
          elevation: 5.0,
          minWidth: 200.0,
          height: 42.0,
          color: Colors.blue,
          child: _formMode == FormMode.LOGIN
              ? new Text('Login',
                  style: new TextStyle(fontSize: 20.0, color: Colors.white))
              : new Text('Create account',
                  style: new TextStyle(fontSize: 20.0, color: Colors.white)),
          onPressed: _validateAndSubmit,
        ));
  }
 
  Widget secondaryButton() {
    return new FlatButton(
      child: _formMode == FormMode.LOGIN
          ? new Text('Create an account',
              style: new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300))
          : new Text('Have an account? Sign in',
              style:
                  new TextStyle(fontSize: 18.0, fontWeight: FontWeight.w300)),
      onPressed: _formMode == FormMode.LOGIN ? showSignupForm : showLoginForm,
    );
  }
 
  void showSignupForm() {
    _formKey.currentState.reset();
    _errorMessage = "";
    setState(() {
      _formMode = FormMode.SIGNUP;
    });
  }
 
  void showLoginForm() {
    _formKey.currentState.reset();
    _errorMessage = "";
    setState(() {
      _formMode = FormMode.LOGIN;
    });
  }
 
  Widget errorWidget() {
    if (_errorMessage.length > 0 && _errorMessage != null) {
      return new Text(
        _errorMessage,
        style: TextStyle(
            fontSize: 13.0,
            color: Colors.red,
            height: 1.0,
            fontWeight: FontWeight.w300),
      );
    } else {
      return new Container(
        height: 0.0,
      );
    }
  }
 
  bool _validateAndSave() {
    final form = _formKey.currentState;
    if (form.validate()) {
      form.save();
      return true;
    }
    return false;
  }
 
  _validateAndSubmit() async {
    setState(() {
      _errorMessage = "";
      _isLoading = true;
    });
    if (_validateAndSave()) {
      String userId = "";
      try {
        if (_formMode == FormMode.LOGIN) {
          userId = await widget.auth.signIn(_email, _password);
        } else {
          userId = await widget.auth.signUp(_email, _password);
        }
        setState(() {
          _isLoading = false;
        });
 
        if (userId.length > 0 && userId != null) {
          widget.onSignedIn();
        }
      } catch (e) {
        setState(() {
          _isLoading = false;
          if (_isIos) {
            _errorMessage = e.details;
          } else
            _errorMessage = e.message;
        });
      }
    } else {
      setState(() {
        _isLoading = false;
      });
    }
  }
}

Обратите внимание на метод validateAndSubmit () , он вызывается при нажатии кнопки входа. Сначала мы устанавливаем начальное состояние для загрузки равным true и пустой ошибкой.

Опять же, я рекомендую проверить эту статью, чтобы узнать, что делает метод setState.

После этого мы проверяем форму, а затем на основе formMode мы либо регистрируем пользователя, либо регистрируем его. В любом из этих случаев мы получаем идентификатор пользователя.

Наконец, мы вызываем метод onSignedIn для виджета. Этот метод является VoidCallback, который предоставляется в конструкторе LoginSignupPage корневой страницей.

В конце концов, корневая страница вызывает метод onLoggedIn, который устанавливает userId и, в конце концов, вызывает setState () . Это вызывает перестройку, и мы переходим на домашний экран.

Это завершает нашу страницу LoginSignupPage . Теперь пришло время добавить функциональность для выхода из системы на нашей домашней странице.

Создание домашней страницы

Чтобы завершить этот урок по аутентификации на базе Flutter, нам нужно добавить возможность выхода из системы. Выход из системы просто означает установку пустого идентификатора пользователя и перенаправление на LoginSignupPage.

Вот код для HomePage:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import 'package:flutter/material.dart';
 
import 'authentication.dart';
 
class HomePage extends StatefulWidget {
  HomePage({Key key, this.auth, this.userId, this.onSignedOut})
      : super(key: key);
 
  final BaseAuth auth;
  final VoidCallback onSignedOut;
  final String userId;
 
  @override
  State<StatefulWidget> createState() => new _HomePageState();
}
 
class _HomePageState extends State<HomePage> {
 
  _signOut() async {
    try {
      await widget.auth.signOut();
      widget.onSignedOut();
    } catch (e) {
      print(e);
    }
  }
 
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('Flutter login demo'),
        actions: <Widget>[
          new FlatButton(
              child: new Text('Logout',
                  style: new TextStyle(fontSize: 17.0, color: Colors.white)),
              onPressed: _signOut)
        ],
      ),
      body: Center(
        child: Text("hello"),
      ),
    );
  }
}

Мы держим кнопку SignOut в панели приложения. Когда пользователь щелкает это, мы вызываем метод onSignedOut, предоставляемый root_page .

Корневая страница просто устанавливает для authState пользователя значение LoggedOut, а для userId — пустую строку.

Вот как выглядит конечный результат:

Вывод

Надеюсь, вы нашли эту статью полезной. Если вы сделали, дайте мне знать в разделе комментариев ниже, я буду рад написать больше таких концептуальных статей.

Вы можете найти весь код этой статьи по адресу: https://github.com/Ayusch/Flutter-Firebase-Authentication

Если у вас есть какие-либо вопросы, дайте мне знать в комментариях ниже, и я буду рад помочь вам!

Смотрите оригинальную статью здесь: Flutter Firebase Authentication Tutorial

Мнения, высказанные участниками Java Code Geeks, являются их собственными.