// Version: 1.0.0 import 'package:flutter/material.dart'; import 'package:google_sign_in/google_sign_in.dart'; /// GoogleSignIn のマルチインスタンス管理 class GoogleSignInProvider { final List _accounts = []; String? _currentAccountEmail; /// 初期化(デフォルトアカウントを 1 つ生成) factory GoogleSignInProvider() { return GoogleSignInProvider._(); } GoogleSignInProvider._() { // デフォルトのサインインインスタンスを作成 final signIn = GoogleSignIn( scopes: [ 'email', 'https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/drive.readonly', 'https://www.googleapis.com/auth/spreadsheets.readonly', ], ); _accounts.add(signIn); } /// 現在選択中のアカウントのメールを取得 String? get currentAccountEmail => _currentAccountEmail; /// 全登録済みアカウント一覧 List get allAccounts { return _accounts .where((s) => s.currentUser?.email != null) .map((s) => '${s.currentUser?.displayName ?? '未認証'} (${s.currentUser?.email})') .toList(); } /// アカウントを登録・選択(設定画面より) Future login({String? accountEmail}) async { try { final signIn = _accounts.firstWhere( (s) => s.currentUser?.email == null, orElse: () => _accounts.last, ); await signIn.signIn(); if (signIn.currentUser != null) { _currentAccountEmail = signIn.currentUser!.email; print('[GoogleSignIn] 認証済み:${signIn.currentUser!.email}'); return signIn.currentUser; } else { throw Exception('キャンセルまたはエラー'); } } catch (e) { print('[GoogleSignIn] 認証失敗:$e'); rethrow; } } /// 現在のアカウントでログアウト Future logout() async { final signIn = _accounts.firstWhere( (s) => s.currentUser?.email == _currentAccountEmail, orElse: () => _accounts.last, ); await signOut(); _currentAccountEmail = null; print('[GoogleSignIn] ログアウト'); } /// アカウント切り替え(複数アカウントを持つ場合) Future switchAccount({required String email}) async { try { final signIn = GoogleSignIn( scopes: [ 'email', 'https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/drive.readonly', 'https://www.googleapis.com/auth/spreadsheets.readonly', ], ); final account = await signIn.signIn(); if (account?.email == email) { _currentAccountEmail = email; print('[GoogleSignIn] アカウント切り替え:$email'); return account; } else { throw Exception('メールアドレスが一致しません'); } } catch (e) { print('[GoogleSignIn] 切り替え失敗:$e'); rethrow; } } /// 認証状態の監視(Stream で使用) Stream get onAccountChanged => _accounts.firstWhere((s) => s.currentUser != null).authStateChanges; } /// アカウント選択画面用ウィジェット class GoogleAccountsSelectScreen extends StatelessWidget { final String? title; final FutureOr? selectedAccountEmail; const GoogleAccountsSelectScreen({ super.key, this.title, this.selectedAccountEmail, }); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(title ?? 'Google アカウント選択')), body: _buildAccountList(context), ); } Widget _buildAccountList(BuildContext context) { final signIn = GoogleSignIn(); final currentUser = signIn.currentUser; final accounts = currentUser == null ? [] : [currentUser]; return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'このアプリには複数の Google アカウントを登録できます。', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), const SizedBox(height: 16), if (currentUser != null) ...[ Card( child: ListTile( leading: Icon(Icons.person, size: 32), title: Text(currentUser.displayName ?? '未認証'), subtitle: Text(currentUser.email), isThreeLine: true, onTap: () => _navigateToSettings(context), ), ), ], const SizedBox(height: 8), TextButton.icon( onPressed: currentUser != null ? () => _navigateToSettings(context) : null, icon: Icon(Icons.add_account), label: Text('新規アカウントを追加'), ), ], ), ); } Future _navigateToSettings(BuildContext context) async { // GoogleSignin の signOut() を呼び出し、認証フローを再開始 final signIn = GoogleSignIn(); await signIn.signOut(); final auth = await GoogleSignInAuthentication( scopes: [ 'email', 'https://www.googleapis.com/auth/gmail.readonly', ], ); final account = await signIn.signIn(); if (account != null) { // 認証成功後の処理(この例では設定画面へ戻る) Navigator.of(context).pop(account?.email); } else { // キャンセルまたはエラーの場合は、アプリ終了またはホーム画面へ誘導 } } }