180 lines
No EOL
5.7 KiB
Dart
180 lines
No EOL
5.7 KiB
Dart
// Version: 1.0.0
|
||
import 'package:flutter/material.dart';
|
||
import 'package:google_sign_in/google_sign_in.dart';
|
||
|
||
/// GoogleSignIn のマルチインスタンス管理
|
||
class GoogleSignInProvider {
|
||
final List<GoogleSignIn> _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<String> get allAccounts {
|
||
return _accounts
|
||
.where((s) => s.currentUser?.email != null)
|
||
.map((s) => '${s.currentUser?.displayName ?? '未認証'} (${s.currentUser?.email})')
|
||
.toList();
|
||
}
|
||
|
||
/// アカウントを登録・選択(設定画面より)
|
||
Future<GoogleSignInAccount?> 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<void> logout() async {
|
||
final signIn = _accounts.firstWhere(
|
||
(s) => s.currentUser?.email == _currentAccountEmail,
|
||
orElse: () => _accounts.last,
|
||
);
|
||
await signOut();
|
||
_currentAccountEmail = null;
|
||
print('[GoogleSignIn] ログアウト');
|
||
}
|
||
|
||
/// アカウント切り替え(複数アカウントを持つ場合)
|
||
Future<GoogleSignInAccount?> 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<GoogleSignInAccount?> get onAccountChanged =>
|
||
_accounts.firstWhere((s) => s.currentUser != null).authStateChanges;
|
||
}
|
||
|
||
/// アカウント選択画面用ウィジェット
|
||
class GoogleAccountsSelectScreen extends StatelessWidget {
|
||
final String? title;
|
||
final FutureOr<String>? 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<void> _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 {
|
||
// キャンセルまたはエラーの場合は、アプリ終了またはホーム画面へ誘導
|
||
}
|
||
}
|
||
} |