FlutterとAmazon Cognitoでログイン機能を実装してみた
こんにちは、CX事業本部IoT事業部の高橋雄大です。
FlutterとAmazon Cognitoでログイン機能を実装してみたいと思います。AWS AmplifyがFlutterのサポートを開始したことで、FlutterでAmplifyの各パッケージが使えるようになりました。今回は認証に関するAmplifyのパッケージを利用して、ログイン機能を実装していきます。
本記事のゴール
ログインとアカウント登録の機能を実装します。
環境情報
項目 | 内容 |
---|---|
OS | macOS Monterey 12.3 (21E230) |
Visual Studio Code | 1.66.0 |
Xcode | 13.3 (13E113) |
Flutter | 2.10.4 |
Dart | 2.16.2 |
Amazon Cognitoを準備
Amazon Cognitoユーザープールとアプリケーションクライアントを作成します。アプリケーションクライアントはパブリッククライアントでクライアントのシークレットは生成しません。
Cognitoユーザープールを選択してユーザープールIDを取得します。また、アプリケーションクライアントを選択してアプリケーションクライアントIDを取得します。
ログイン機能を実装
FlutterのプロジェクトにAmplifyのパッケージを追加します。
flutter pub add amplify_auth_cognito flutter pub add amplify_authenticator flutter pub add amplify_flutter
Amplifyの設定ファイルamplifyconfiguration.dart
を作成します。amplify-cli
を利用すれば自動で設定ファイルを生成することも可能ですが、Amplifyのセットアップが必要になるので、今回は手動でAmplifyの設定ファイルを作成します。
Amplifyの設定ファイルには、先程取得したユーザープールIDとアプリケーションクライアントIDを入力します。また、Amazon Cognitoユーザープールの設定内容も入力します。
const amplifyconfig = '''{ "auth": { "plugins": { "awsCognitoAuthPlugin": { "CognitoUserPool": { "Default": { "PoolId": "[COGNITO USER POOL ID]", "AppClientId": "[COGNITO USER POOL APP CLIENT ID]", "Region": "[REGION]" } }, "Auth": { "Default": { "authenticationFlowType": "USER_SRP_AUTH", "socialProviders": [], "usernameAttributes": [], "signupAttributes": [ "email" ], "passwordProtectionSettings": { "passwordPolicyMinLength": 8, "passwordPolicyCharacters": [] }, "mfaConfiguration": "OFF", "mfaTypes": [ "SMS" ], "verificationMechanisms": [ "EMAIL" ] } } } } } }''';
main.dart
でログイン機能を実装します。ログイン画面やアカウント登録画面はAuthenticator
コンポーネントで自動生成されます。
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 '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> { @override void initState() { super.initState(); _configureAmplify(); } void _configureAmplify() async { try { await Amplify.addPlugin(AmplifyAuthCognito()); await Amplify.configure(amplifyconfig); print('Successfully configured'); } on Exception catch (e) { print('Error configuring Amplify: $e'); } } @override Widget build(BuildContext context) { return Authenticator( child: MaterialApp( builder: Authenticator.builder(), home: Scaffold( appBar: AppBar(title: const Text('ホーム')), body: const Center( child: Text('ログイン成功!', style: TextStyle(fontSize: 32.0)), ), ), ), ); } }
デバッグを実行してプレビューするとログイン画面が表示されます。
デバッグコンソールに次のようなエラーが出力される場合にはios/Podfile
でプラットフォームを設定する必要があります。AmplifyのパッケージはiOS11以上が要件になりますので、プラットフォームを指定する必要があります。エラー内容を見ると、プラットフォームが指定されていないので、iOS9が自動で選択されているようです。
o update [!] CocoaPods could not find compatible versions for pod "amplify_auth_cognito_ios":
Error output from CocoaPods: ↳ [!] Automatically assigning platform `iOS` with version `9.0` on target `Runner` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`. Error running pod install Error launching application on iPhone 13 Pro Max. Exited (sigterm)
ios/Podfile
2行目のコメントアウトを解除してプラットフォームにiOS11を指定します。
# Uncomment this line to define a global platform for your project platform :ios, '11.0'
Podのキャッシュを更新します。
pod repo update
ソーシャルログイン対応
amplifyconfiguration.dart
のsocialProviders
にソーシャルプロバイダーを追加します。実際にソーシャルログインを利用するには、Amazon Cognito側の設定も必要になります。
"Auth": { "Default": { "authenticationFlowType": "USER_SRP_AUTH", "socialProviders": [ "GOOGLE", "FACEBOOK", "AMAZON", "APPLE" ], "usernameAttributes": [],
追加したソーシャルプロバイダーのログインボタンが表示されました。
日本語化対応
既存のコンポーネントを上書きして文言を日本語に変更します。
import 'package:amplify_authenticator/amplify_authenticator.dart'; import 'package:flutter/material.dart'; class CustomButtonResolver extends ButtonResolver { const CustomButtonResolver(); @override String signIn(BuildContext context) { return 'ログイン'; } @override String signUp(BuildContext context) { return 'アカウント登録'; } @override String forgotPassword(BuildContext context) { return 'パスワードを忘れた場合'; } @override String confirm(BuildContext context) { return '登録'; } @override String sendCode(BuildContext context) { return '再送信'; } @override String lostCode(BuildContext context) { return '検証コードが届かない場合'; } @override String signInWith(BuildContext context, AuthProvider provider) { switch (provider) { case AuthProvider.apple: return 'Appleでログイン'; case AuthProvider.amazon: return 'Amazonでログイン'; case AuthProvider.facebook: return 'Facebookでログイン'; case AuthProvider.google: return 'Googleでログイン'; } } @override String backTo(BuildContext context, AuthenticatorStep previousStep) { switch (previousStep) { case AuthenticatorStep.signIn: return "ログイン画面へ戻る"; default: return "戻る"; } } } class CustomInputResolver extends InputResolver { const CustomInputResolver(); @override String title(BuildContext context, InputField field) { switch (field) { case InputField.username: return 'ユーザー名'; case InputField.email: return 'メールアドレス'; case InputField.password: return 'パスワード'; case InputField.passwordConfirmation: return 'パスワード(確認)'; case InputField.verificationCode: return '検証コード'; default: return super.title(context, field); } } @override String hint(BuildContext context, InputField field) { final fieldName = title(context, field); return '$fieldNameを入力してください'; } @override String confirmHint(BuildContext context, InputField field) { final fieldName = title(context, field); final replaceFieldName = fieldName.replaceFirst('(確認)', ''); return '$replaceFieldNameを再度入力してください'; } } class CustomMessagesResolver extends MessageResolver { const CustomMessagesResolver(); } class CustomTitleResolver extends TitleResolver { const CustomTitleResolver(); @override String signIn(BuildContext context) { return 'ログイン'; } @override String signUp(BuildContext context) { return 'アカウント登録'; } @override String confirmSignUp(BuildContext context) { return '確認'; } } const stringResolver = AuthStringResolver( buttons: CustomButtonResolver(), inputs: CustomInputResolver(), messages: CustomMessagesResolver(), titles: CustomTitleResolver(), );
Authenticator
コンポーネントでstringResolver
を指定します。
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 'amplifyconfiguration.dart'; import 'resolver.dart';
@override Widget build(BuildContext context) { return Authenticator( stringResolver: stringResolver, child: MaterialApp( builder: Authenticator.builder(), home: Scaffold( appBar: AppBar(title: const Text('ホーム')), body: const Center( child: Text('ログイン成功!', style: TextStyle(fontSize: 32.0)), ), ), ), ); } }
ログイン画面やアカウント登録画面を日本語で表示することができました。
ダークテーマ
MaterialApp
コンポーネントを編集してダークテーマを適応してみます。
@override Widget build(BuildContext context) { return Authenticator( stringResolver: stringResolver, child: MaterialApp( builder: Authenticator.builder(), theme: ThemeData.dark(), home: Scaffold( appBar: AppBar(title: const Text('ホーム')), body: const Center( child: Text('ログイン成功!', style: TextStyle(fontSize: 32.0)), ), ), ), ); } }
ダークテーマのログイン画面を確認することができました。
動作確認
最後にアカウント登録をやってみます。入力したメールアドレス宛に検証コードが届くので、検証コードを入力してアカウント登録を完了します。
まとめ
AWS Amplifyのパッケージを利用することで、Flutterで簡単にログイン機能を実装することができました。Amplify UIのドキュメントを確認してみると、テーマの変更やUIのカスタマイズ方法についても記載されています。ブランディングを意識してログイン画面にアプリのロゴを表示したりするのも良いかもしれません。