【Flutter】Amplifyのログイン機能をAmplify UIを使って爆速で構築する 

# 概要
本記事では、Flutter x Amplify Cognito x Amplify UIの組み合わせでログイン機能を構築する方法を記します(参考:[Amplify UI公式](https://ui.docs.amplify.aws/components/authenticator?platform=flutter))。

ログイン機能を実装するには、ログイン機能そのものだけでなく、ログインフォームのUI実装も必要になるため、とても大変ですよね。[Amplify公式の認証のチュートリアル](https://aws.amazon.com/jp/getting-started/hands-on/build-flutter-app-amplify/module-three/)でも自前でのUI構築を前提としています。もし、UIに特に強いこだわりを持たないのであれば、Amplify UIそのまま、あるいはそれをベースにカスタマイズすることで実装速度に寄与すると考えられます。

出来上がりは以下のようになります。
※注：サインアップは【予め】本アプリのCreate Accountから作成済の状態でキャプチャしました
![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1424230/5f243120-1c9c-d066-af97-02fa7ba72725.gif)

# 環境
flutter, Amplify CLIのインストールは別記事を参照お願いします。

```shell
$ flutter --version
Flutter 2.10.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 7e9793dee1 (3 weeks ago) • 2022-03-02 11:23:12 -0600
Engine • revision bd539267b4
Tools • Dart 2.16.1 • DevTools 2.9.2

$ amplify -v
7.6.25
```

# 事前準備
## プロジェクト作成【作成済であれば不要】
プロジェクト名のflutter_amplify_authやオプションについては、適宜変更してください。
```shell
$ flutter create flutter_amplify_auth
```

## 関連パッケージのインストール
amplify_flutter、amplify_auth_cognito、amplify_authenticatorをインストールします。
pub addしてますが、pubspec.yamlを書き換えてpub getしても問題ありません。
```shell
$ cd flutter_amplify_auth
$ flutter pub add amplify_flutter
$ flutter pub add amplify_auth_cognito
$ flutter pub add amplify_authenticator
```

本記事記載のタイミング(2022/3/21)だと、以下のバージョンでした。

```yaml:pubspec.yaml
amplify_authenticator: ^0.1.0
amplify_auth_cognito: ^0.4.1
amplify_flutter: ^0.4.1
```


## プラットフォームのバージョン指定
### iOS
Podfileのplatformのコメントを外してiosのバージョンを13.0に指定します。
13.0の理由は、[Amplify公式](https://docs.amplify.aws/start/getting-started/setup/q/integration/flutter/#add-amplify-to-your-application)を参照しています。
```text:ios/Podfile
platform :ios, '13.0'
```

### Android
```gradle:android/app/build.gradle
minSdkVersion 21
```

以下は参考です。[公式](https://docs.amplify.aws/start/getting-started/installation/q/integration/flutter/)によると、【1.5.31】以上であることが必須らしいですが、Flutterのバージョン3.10.xは、何もしなくてもKotlinのバージョンが1.5.31以上で設定されているので気にしなくて良いです。
```gradle:android/build.grdale
buildscript {
    ext.kotlin_version = '1.6.10'
    ...
}
```

## Amplifyプロジェクト作成と認証サービス追加
①Amplifyプロジェクトの初期化
プロジェクト作成のオプションはご自身のユースケースで適宜変更ください。
```shell
$ amplify init
? Enter a name for the project flutteramplifyauth
? Initialize the project with the above configuration? Yes
? Select the authentication method you want to use: (Use arrow keys)
? Please choose the profile you want to use default
```

②Amplifyプロジェクトに認証サービスを追加
認証方法のオプションはご自身のユースケースで適宜変更ください。
ここでは、最小の構成であるユーザ名の認証としています。

```shell
$ amplify add auth
Do you want to use the default authentication and security configuration? Default configuration
How do you want users to be able to sign in? Username
Do you want to configure advanced settings? No, I am done.
```

③認証サービスをプロジェクトに反映させる

```shell
$ amplify push
? Are you sure you want to continue? Yes
```

# 実装
## MyAppをStateful Widgetに変更する
物理キーボードのショートカットキーで変更するのが簡単だと思います。
MyAppクラスのStatelessWidgetにキーボードカーソルを合わせた上で、
VSCodeであれば、【command + .(ドット)】
Android Studioであれば、【option + Enter】

にて【Convert to Stateful Widget】を選びます。

## _MyAppStateのinitStateでAmplifyのconfigを読み込む
【_MyAppState】のinitStateでAmplifyの設定を読み込みます。
Amplifyを使用する場合、他のサービス(ex. DataStore)も使用することが考えられるため、本記事ではaddPluginメソッドではなく、addPluginsメソッドを採用しました。
この辺りは適宜変更いただければと思います。

```dart
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter_amplify_auth/amplifyconfiguration.dart';

@override
void initState() {
  super.initState();
  _configureAmplify();
}

void _configureAmplify() async {
  try {
    await Amplify.addPlugins([AmplifyAuthCognito()]);
    await Amplify.configure(amplifyconfig);
    debugPrint('Successfully configured');
  } on Exception catch (e) {
    debugPrint('Error configuring Amplify: $e');
  }
}
```

## MaterialAppをAuthenticator Widgetでラップする
buildメソッドを2箇所だけ変更を加えます。変更部分はコメントアウト参考。
```dart:main.dart
@override
Widget build(BuildContext context) {
  // ✨✨MaterialAppにAuthenticatorをラップする
  return Authenticator(
    child: MaterialApp(
      // ✨✨MaterialAppのbuilderにAuthenticatorのbuilderメソッドを設定する
      builder: Authenticator.builder(),
      title: 'Flutter Demo',
      theme: ThemeData(
      primarySwatch: Colors.blue,
    ),
    home: const MyHomePage(title: 'Flutter Demo Home Page'),
    ),
  );
}
```

## コード全文【参考】
上記と記載している内容は同じのため、基本的には読み飛ばしていただければと。

```dart:dart.main
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_amplify_auth/amplifyconfiguration.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    // ✨✨MaterialAppにAuthenticatorをラップする
    return Authenticator(
      child: MaterialApp(
        // ✨✨MaterialAppのbuilderにAuthenticatorのbuilderメソッドを設定する
        builder: Authenticator.builder(),
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    _configureAmplify();
  }

  void _configureAmplify() async {
    try {
      await Amplify.addPlugins([AmplifyAuthCognito()]);
      await Amplify.configure(amplifyconfig);
      debugPrint('Successfully configured');
    } on Exception catch (e) {
      debugPrint('Error configuring Amplify: $e');
    }
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
```

# 所感
Amplify UIを使えば、ここまでサクッとログイン周り実装できるのは驚異的というか感動しました。
もしUIにこだわりなければ積極的に採用を検討しても良いのではないでしょうか。