flutter FirebaseAuthを用いたアカウント作成　MVVM ＋ repositoryアーキテクチャでのシンプルな実装例

#はじめに
引き続きFlutterを勉強中。アカウント作成機能とログイン機能などをつけるためにFirebaseを使用しています。
前回の投稿：
https://qiita.com/Oyama-Kohei/items/fd1204f63d3af2d906e6flutter
今回の投稿では前回の構成をよりPJに近くメンテナンスしやすいようにrepository層を追加して改めてリファクタリングをしたので、紹介したいと思います！（前回に引き続きまだまだ勉強中の身なのでおかしなところあったらすみません。。。）

#アカウント作成の概要
今回の作成する方式もメールアドレスとパスワードによるアカウント作成方法になります。
フォルダ構成は以下の通り

```
lib----components---register---register_page
    │             │          ├-register_view_model
    │             │
    │             ├-repository--auth_repository
    │             ├-service   --auth_service
    │
    ├--utility   ---validator---email_validator
                  │          ├--password_validator
                  │
                  ├-locator
```


#Widgetの作成
widget(register_page)の作成です。

```dart:register_page
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:taskmum_flutter/components/register/register_view_model.dart';
import 'package:taskmum_flutter/components/wiget/common_button.dart';
import 'package:taskmum_flutter/utility/validator/email_validator.dart';
import 'package:taskmum_flutter/utility/validator/nickname_validator.dart';
import 'package:taskmum_flutter/utility/validator/password_validator.dart';

class RegisterPage extends StatefulWidget {
  const RegisterPage({Key? key}) : super(key: key);

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

  late String _email, _password, _nickname;

  @override
  Widget build(BuildContext context){
    final _formKey = GlobalKey<FormState>();
    return ChangeNotifierProvider<RegisterViewModel>(
      create: (_) => RegisterViewModel(),
      child: Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.white,
            elevation: 0.0,
          ),
          body: Center(
              child: Consumer<RegisterViewModel>(builder: (context, viewModel, child){
                return Form(
                    key: _formKey,
                    child: Stack(
                        children: <Widget>[
                          Padding(padding: const EdgeInsets.all(24),
                              child: Column(
                                  children: [
                                    const Expanded(
                                      flex: 1,
                                      child: Image(
                                        image: AssetImage("images/boys.jpeg"),
                                      ),
                                    ),
                                    Expanded(
                                      flex: 1,
                                      child: Column(
                                        children: [
                                          TextFormField(
                                            validator: EmailValidator.validator(),
                                            autovalidateMode: AutovalidateMode.onUserInteraction,
                                            decoration: const InputDecoration(hintText: 'email'),
                                            onChanged: (value) => _email = value
                                          ),
                                          TextFormField(
                                            obscureText: true,
                                            validator: PasswordValidator.validator(),
                                            autovalidateMode: AutovalidateMode.onUserInteraction,
                                            decoration: const InputDecoration(hintText: 'password'),
                                            onChanged: (value) => _password = value
                                          ),
                                          Padding(padding: const EdgeInsets.fromLTRB(0, 30, 0, 0),
                                              child: CommonButton(
                                                text: '新規アカウント作成',
                                                padding: const EdgeInsets.fromLTRB(20, 10, 20, 10),
                                                useIcon: true,
                                                onPressed: () async {
                                                  if (_formKey.currentState!.validate()) {
                                                    bool result = await (viewModel.signUp(_email, _password));
                                                    if (result) {
                                                      //<ページ遷移処理など>
                                                      );
                                                    }
                                                  }
                                                },
                                              )
                                          )
                                        ],
                                      ),
                                    ),
                                  ]
                              )
                          ),
                        ]
                    )
                );
              }
              )
          )
      ),
    );
  }
}

```

#ViewModel（register_view_model）の作成
前回の記事ではModelとしていましたが、
今回はrepository層を追加してサーバー側のやりとりとアプリの処理を分割したため、ViewModelとしています

```dart:register_view_model
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/widgets.dart';
import 'package:taskmum_flutter/components/repository/auth_repository.dart';
import 'package:taskmum_flutter/utility/locator.dart';

class RegisterViewModel extends ChangeNotifier{

  final AuthRepository _repository = getIt<AuthRepository>();

  Future<bool> signUp(String email, String password) async {

    var result = await _repository.signUp(email, password);
    if (result is! User) {
      return false;
    }
    return true;
  }
}
```

#Repository層（auth_repository, auth_service, locator）の作成
こちらのRepository層ではサーバー側にemailとpasswordの情報を用いてアカウントを作成する機能を付与しています


```dart:auth_repository
import 'package:taskmum_flutter/components/service/auth_service.dart';
import 'package:taskmum_flutter/utility/locator.dart';

class AuthRepository {

  final AuthService _AuthService = getIt<AuthService>();

  Future signUp(String email, String password) async{
    var result = await _AuthService.signUp(email, password);
    return result;
  }
}

```

```dart:auth_service
import 'package:firebase_auth/firebase_auth.dart';

class AuthService {

  final firebaseAuthService = FirebaseAuth.instance;

  Future signUp(String email, String password) async{
    try{
      UserCredential _userCredential = await firebaseAuthService.createUserWithEmailAndPassword(email: email, password: password);

      if(_userCredential.user != null){
        return _userCredential.user;
      }
    }on FirebaseAuthException catch(e){
      print("AuthException ${e.toString()})");
    }
  }
}

```
locatorはget itパッケージを使用してデータベースなどのサービスオブジェクトにアクセスして、それらを簡単にモックできるようにします。
get itパッケージについては以下公式ドキュメントを参照
https://pub.dev/packages/get_it

```dart:locator
import 'package:get_it/get_it.dart';
import 'package:taskmum_flutter/components/repository/auth_repository.dart';
import 'package:taskmum_flutter/components/service/auth_service.dart';

GetIt getIt = GetIt.instance;

void setupLocator() {
  getIt.registerLazySingleton(() => AuthRepository());
  getIt.registerLazySingleton(() => AuthService());
}

```

#まとめ
徐々にPJに近いような構成にコーディングすることができるようになってきました。（まだまだですが。。。）
まだアカウント認証系しかできていないので、これから本格的にメインの機能を考えて実装できるようになれればいいなと思っています！早くPJで活躍できるような人材になりたいですねww