Flutter(iOS)アプリでAmzon Cognitoの認証状態に応じて画面を切り替える実装をしてみた

2023.03.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 Delivery部の若槻です。

前々回はFlutterアプリでMaterialAppを使って画面を切り替える方法を確認しました。

また前回は四苦八苦しながらもCognito user poolでHoeted UIを使えるようにする実装を行いました。

今回は、これらの合わせ技として、Flutter(iOS)アプリでAmzon Cognitoの認証状態に応じて画面を切り替える実装をしてみました。

実装

実装していきます。

ちなみにFlutter上でのHosted UIの実装部分は高橋雄大さんの次のエントリを大いに参考にさせて頂きました。

iOSのプロパティ設定

iOSのプロパティ設定にアプリのURL(User pool clientのコールバックURLに指定した<myapp>://という記述の<myapp>)を追加します。

ios/Runner/info.plist

	<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>myapp</string>
			</array>
		</dict>
	</array>

Amplifyパッケージのインストール

flutter pub add amplify_auth_cognito
flutter pub add amplify_flutter

コンフィグファイル作成

Amplifyの設定ファイルamplifyconfiguration.dartを作成します。User poolの情報を設定値として指定します。

lib/amplifyconfiguration.dart

const amplifyconfig = '''{
  "auth": {
    "plugins": {
      "awsCognitoAuthPlugin": {
        "CognitoUserPool": {
          "Default": {
            "PoolId": "<COGNITO USER POOL ID>",
            "AppClientId": "<COGNITO USER POOL APP CLIENT ID>",
            "Region": "<REGION>"
          }
        },
        "Auth": {
          "Default": {
            "OAuth": {
              "WebDomain": "<COGNITO WEB DOMAIN>",
              "AppClientId": "<COGNITO USER POOL APP CLIENT ID>",
              "SignInRedirectURI": "<myapp>://",
              "SignOutRedirectURI": "<myapp>://",
              "Scopes": [
                "email",
                "openid",
                "profile"
              ]
            }
          }
        }
      }
    }
  }
}''';

注意点として、WebDomainで指定する値は<myapp>.auth.<region>.amazoncognito.comとなります。(`https://`は含めないように注意)

認証間利用プロバイダー

認証状態の管理はproviderを使用しています。AmplifyのSDKを使用したサインイン/サインアウトのFutureもプロバイダー内で実装しています。

lib/auth_provider.dart

import 'package:flutter/material.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify_flutter.dart';

import 'amplifyconfiguration.dart';

class AuthProvider extends ChangeNotifier {
  bool signedIn = false;

  Future<void> _configure() async {
    if (!Amplify.isConfigured) {
      await Amplify.addPlugin(AmplifyAuthCognito());
      await Amplify.configure(amplifyconfig);
    }

    try {
      await Amplify.Auth.getCurrentUser();

      signedIn = true;
    } on AuthException {
      signedIn = false;
    }

    notifyListeners();
  }

  AuthProvider() {
    _configure();
  }

  Future<void> signIn() async {
    try {
      await Amplify.Auth.signInWithWebUI();

      signedIn = true;
    } on AuthException {
      signedIn = false;
    }

    notifyListeners();
  }

  Future<void> signOut() async {
    try {
      await Amplify.Auth.signOut();

      signedIn = false;
    } on AuthException {
      signedIn = true;
    }

    notifyListeners();
  }
}

サインイン画面

サインイン画面です。ログインボタンが表示されます。

lib/signin_screen.dart

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

import './auth_provider.dart';

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

  @override
  State<AuthScreen> createState() => _AuthScreenState();
}

class _AuthScreenState extends State<AuthScreen> {
  @override
  Widget build(BuildContext context) {
    final AuthProvider authProvider = context.watch<AuthProvider>();
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('My App')),
        body: Center(
          child: Center(
              child: ElevatedButton(
                  child: const Text('Login'),
                  onPressed: () {
                    authProvider.signIn();
                  })),
        ),
      ),
    );
  }
}

ホーム画面

サインイン後にアクセスできるホーム画面です。ログアウトボタンが表示されます。

lib/home_screen.dart

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

import './auth_provider.dart';

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

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    final AuthProvider authProvider = context.watch<AuthProvider>();
    return MaterialApp(
        home: Scaffold(
      appBar: AppBar(title: const Text('My App')),
      body: Center(
        child: Center(
            child: ElevatedButton(
                style: ElevatedButton.styleFrom(
                  backgroundColor: Colors.red,
                ),
                child: const Text('ログアウト'),
                onPressed: () {
                  authProvider.signOut();
                })),
      ),
    ));
  }
}

認証状態に応じた画面の切り替え

認証状態に応じて画面を切り替える実装はmain.dartでプロバイダーの値を元に行っています。

lib/main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './auth_provider.dart';
import './signin_screen.dart';
import './home_screen.dart';

void main() => runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => AuthProvider()),
        ],
        child: const MyApp(),
      ),
    );

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    final AuthProvider authProvider = context.watch<AuthProvider>();

    return MaterialApp(
        home: authProvider.signedIn ? const HomeScreen() : const AuthScreen());
  }
}

動作確認

iOS Simulatorでアプリを起動します。

flutter run -d 2CA618C6-E1DF-42E2-B555-541EE8A289B3

アプリが起動しました。未ログインなのでサインイン画面が表示されています。

LoginボタンをクリックするとCognitoのHosted UIが表示されます。サインアップまたはサインインを行います。

サインインに成功するとホーム画面が表示されました。

Logoutボタンをクリックすると、サインアウトが行われてサインイン画面に戻りました。

おわりに

Flutter(iOS)アプリでAmzon Cognitoの認証状態に応じて画面を切り替える実装をしてみました。

今回のアプリは2画面だけでしたが、実際のアプリはさらに柔軟な画面遷移を行うことになります。次回以降でまた実装を紹介します。

以上