Repository: flutter
Issue: [google-sign-in] Canceled sign-in attempt not reflected by `onCurrentUserChanged` `Stream`
Labels: p: google_sign_in,package,team-ecosystem,has reproducible steps,P2,triaged-ecosystem,found in release: 3.27,found in release: 3.28

### What package does this bug report belong to?

google_sign_in

### What target platforms are you seeing this bug on?

iOS, Android

### Have you already upgraded your packages?

Yes

### Dependency versions

<details><summary>pubspec.lock</summary>

```lock
  google_identity_services_web:
    dependency: transitive
    description:
      name: google_identity_services_web
      sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
      url: "https://pub.dev"
    source: hosted
    version: "0.3.1+4"
  google_sign_in:
    dependency: "direct main"
    description:
      name: google_sign_in
      sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f"
      url: "https://pub.dev"
    source: hosted
    version: "6.2.1"
  google_sign_in_android:
    dependency: transitive
    description:
      name: google_sign_in_android
      sha256: "0928059d2f0840f63c7b07a30cf73b593ae872cdd0dbd46d1b9ba878d2599c01"
      url: "https://pub.dev"
    source: hosted
    version: "6.1.33"
  google_sign_in_ios:
    dependency: transitive
    description:
      name: google_sign_in_ios
      sha256: "83f015169102df1ab2905cf8abd8934e28f87db9ace7a5fa676998842fed228a"
      url: "https://pub.dev"
    source: hosted
    version: "5.7.8"
  google_sign_in_platform_interface:
    dependency: transitive
    description:
      name: google_sign_in_platform_interface
      sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971"
      url: "https://pub.dev"
    source: hosted
    version: "2.4.5"
```

</details>


### Steps to reproduce

1. Make a sign-in request via `GoogleSignIn#signIn()`
2. As the app user, cancel the sign-in without choosing a Google account via the UI

### Expected results

The stream returned by `GoogleSignIn#onCurrentUserChanged()` should have a `null` element emitted for implementations that rely on the stream to handle auth events

### Actual results

While the `Future` returned by `GoogleSignIn#signIn()` completes with `null`, the user changed stream does not emit a null value.

### Code sample

<details open><summary>Code sample</summary>

```dart
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';

void main() {
  runApp(const MyApp());
}

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Google Sign-in bug demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Google Sign-in bug demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static final _clientId = 'google client id';

  final _googleSignIn = GoogleSignIn(clientId: _clientId);
  bool loading = false;
  bool signedIn = false;

  @override
  void initState() {
    super.initState();

    // relying on changes in the stream to update auth status
    _googleSignIn.onCurrentUserChanged.listen((acc) {
      setState(() {
        loading = false;
        signedIn = acc != null;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (loading)
              ...[
                Text('loading auth state'),
                CircularProgressIndicator(),
              ]
            else if (signedIn)
              ...[
                Text('you\'re signed in!'),
                ElevatedButton(onPressed: _doGoogleSignOut, child: Text('sign out')),
              ]
            else
              ElevatedButton(onPressed: _doGoogleSignIn, child: Text('sign in with google')),
          ],
        ),
      ),
    );
  }

  void _doGoogleSignIn() {
    setState(() {
      loading = true;

      // when a user cancels the auth process, the future returned by the following request will complete with `null`,
      // however, the `_googleSignIn.onCurrentUserChanged` `Stream` does not emit a new `null` value,
      // nor is an error thrown. this means that we can't use a consistent approach (i.e., relying solely on the stream)
      // to handle the canceled auth flow scenario, and have to also await this future and see if the result is null
      _googleSignIn.signIn();
    });
  }

  void _doGoogleSignOut() {
    setState(() {
      loading = true;
      _googleSignIn.signOut();
    });
  }
}

```

</details>


### Screenshots or Videos

<details open>
<summary>Screenshots / Video demonstration</summary>

https://drive.google.com/file/d/1YoO4xrlAKxRPhnxa7tnkleyRSaxoekmJ/view?usp=sharing

The video first shows that the happy path flows of sign-in and sign-out work when relying on the stream for auth updates. But then, for the Cancel flow, the stream is never updated, so the app remains in the loading state indefinitely.

</details>


### Logs

<details open><summary>Logs</summary>

```console
[Paste your logs here]
```

</details>


### Flutter Doctor output

<details open><summary>Doctor output</summary>

```console
[Paste your output here]
```

</details>