h-1.flutter.4/lib/services/google/google_sign_in_provider.dart

180 lines
No EOL
5.7 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 {
// キャンセルまたはエラーの場合は、アプリ終了またはホーム画面へ誘導
}
}
}