connect static method

Future<GoogleDriveProvider?> connect({
  1. bool forceInteractive = false,
  2. List<String>? scopes,
  3. String? serverClientId,
})

Connects to Google Drive, authenticating the user.

This method handles the Google Sign-In flow. It will attempt to sign in silently first, unless forceInteractive is true.

scopes a list of additional Google API scopes to request. The default scopes are drive.DriveApi.driveAppdataScope or drive.DriveApi.driveScope depending on MultiCloudStorage.cloudAccess. serverClientId The server client ID for requesting an ID token if you need to authenticate to a backend server.

Returns a connected GoogleDriveProvider instance on success, or null on failure/cancellation.

Implementation

static Future<GoogleDriveProvider?> connect(
    {bool forceInteractive = false,
    List<String>? scopes,
    String? serverClientId}) async {
  debugPrint("connect Google Drive,  forceInteractive: $forceInteractive");
  // Return existing instance if already connected and not forcing a new interactive session.
  if (_instance != null && _instance!._isAuthenticated && !forceInteractive) {
    return _instance;
  }
  if (scopes != null) {
    _scopes = scopes;
  }
  try {
    // Initialize GoogleSignIn with the correct scope based on the desired cloud access level.
    _googleSignIn ??=
        GoogleSignIn(scopes: _scopes, serverClientId: serverClientId);
    GoogleSignInAccount? account;
    // Attempt silent sign-in first to avoid unnecessary user interaction.
    if (!forceInteractive) {
      account = await _googleSignIn!.signInSilently();
    }
    // If silent sign-in fails or is skipped, start the interactive sign-in flow.
    account ??= await _googleSignIn!.signIn();
    if (account == null) {
      debugPrint('User cancelled Google Sign-In process.');
      return null;
    }
    // Ensure the user has granted the required permissions.
    final bool hasPermissions = await _googleSignIn!.requestScopes(_scopes);
    if (!hasPermissions) {
      debugPrint('User did not grant necessary Google Drive permissions.');
      await signOut();
      return null;
    }
    // Get the authenticated HTTP client.
    final client = await _googleSignIn!.authenticatedClient();
    if (client == null) {
      debugPrint(
          'Failed to get authenticated Google client after permissions were granted.');
      await signOut();
      return null;
    }
    // Wrap the client in a RetryClient to handle transient network errors (5xx).
    final retryClient = RetryClient(
      client,
      retries: 3,
      when: (response) => {500, 502, 503, 504}.contains(response.statusCode),
      onRetry: (request, response, retryCount) => debugPrint(
          'Retrying request to ${request.url} (Retry #$retryCount)'),
    );
    // Create or update the singleton instance with the authenticated client.
    final provider = _instance ?? GoogleDriveProvider._create();
    provider.driveApi = drive.DriveApi(retryClient);
    provider._isAuthenticated = true;
    _instance = provider;
    debugPrint(
        'Google Drive user signed in: ID=${account.id}, Email=${account.email}');
    return _instance;
  } on SocketException catch (e) {
    debugPrint('No internet connection during Google Drive sign-in.');
    throw NoConnectionException(e.message);
  } catch (error) {
    debugPrint(
      'Error occurred during the Google Drive connect process.',
    );
    if (error is PlatformException && error.code == 'network_error') {
      throw NoConnectionException(error.toString());
    }
    await signOut(); // Clean up on error.
    return null;
  }
}