10

I am trying to implement Google Sign In in my android flutter app, but I have this problem:

When user cancel Google sign in (tap on back button) this exception is throw.

PlatformException (PlatformException(sign_in_canceled, com.google.android.gms.common.api.ApiException: 12501: , null))

I found that from some newer version this should be fixed and it should return null instead of an exception. Currently I am using google_sign_in: ^4.1.1

I tried to wrap my code inside try-catch block or using .catchError() on the method, but nothing help.

My code looks like this:

  Future googleSign(BuildContext context) async {
    final GoogleSignInAccount googleSignInAccount =
        await googleSignIn.signIn().catchError((onError) => print(onError));

    final GoogleSignInAuthentication googleSignInAuthentication =
        await googleSignInAccount.authentication;

    final AuthCredential credential = GoogleAuthProvider.getCredential(
      accessToken: googleSignInAuthentication.accessToken,
      idToken: googleSignInAuthentication.idToken,
    );

    final AuthResult authResult = await _auth.signInWithCredential(credential);

    return authResult.user.uid;
  }

Do you have any idea, how to handle this exception? Thanks.

Petr Jelínek
  • 1,259
  • 1
  • 18
  • 36

5 Answers5

9

The issue seems to be caused by an inability of the Dart VM to correctly detect exceptions that are caught with catchError() as explained in this StackOverflow question

There doesn't seem to be a perfect fix to this bug. However, I came across a somewhat good workaround on Github.

It requires you to edit package:flutter/src/services/platform_channel.dart.

You would have to wrap this

final Map<dynamic, dynamic> result = await invokeMethod<Map<dynamic, dynamic>>(method, arguments);
return result?.cast<K, V>();

with a try/catch block as follows (found at the beginning of invokeMapMethod)

try {
    final Map<dynamic, dynamic> result = await invokeMethod<Map<dynamic, dynamic>>(method, arguments);
    return result?.cast<K, V>();
} on PlatformException catch (err) { // Checks for type PlatformException
    if (err.code == 'sign_in_canceled') { // Checks for sign_in_canceled exception
        print(err.toString());
    } else {
        throw err; // Throws PlatformException again because it wasn't the one we wanted
    }
}

You might also want to check if googleSignInAccount is null in which case, you should return return null to prevent further exceptions like NoSuchMethodError: The getter 'authentication' was called on null

So, your code can be re-written as

...

final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn().catchError((onError) => print(onError));

// Return null to prevent further exceptions if googleSignInAccount is null
if (googleSignInAccount == null) return null;

final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;

...

I hope this is easy to follow and it actually works for you.

Zamorite
  • 686
  • 9
  • 13
  • Hey, how can I find platform_channel? I don't see any package folder in my directory. – chichi Dec 06 '20 at 16:05
  • 1
    It can be found in your Flutter installation directory. \packages\flutter\lib\src\services https://ibb.co/j62dTmm By the way, I think the issue should have been fixed properly by now. – Zamorite Dec 07 '20 at 18:03
  • could you put a more complete example of how you do that? – Andres Cuello Jan 06 '21 at 15:30
9

This issue only resides in debug mode. If the user cancels the Google Sign In then the app won't freeze or crash in the release mode. So, no need to worry as this won't create any problem in the release mode.

Ahmad Khan
  • 757
  • 9
  • 22
  • Still happens in 2023. App froze when going back (without selecting an account to sign in with) in debug mode. Otherwise works fine. – l1qu1d Jul 20 '23 at 06:02
1

This seems to fix the issue.

static Future<User?> signInWithGoogle() async {
    final FirebaseAuth auth = FirebaseAuth.instance;
    final GoogleSignIn googleSignIn = GoogleSignIn();

    GoogleSignInAccount? googleSignInAccount;

    try {
      googleSignInAccount =
          await googleSignIn.signIn().catchError((onError) => null);
    } catch (e) {
      // print(e);
    }

    if (googleSignInAccount != null) {
      final GoogleSignInAuthentication googleAuth =
          await googleSignInAccount.authentication;
      final credential = GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );

      try {
        final UserCredential authResult =
            await auth.signInWithCredential(credential);
        final User? user = authResult.user;

        return user;
      } on FirebaseAuthException catch (e) {
        if (e.code == 'account-exists-with-different-credential') {
          // ...
        } else if (e.code == 'invalid-credential') {
          // ...
        }
      } catch (e) {
        // ...
      }
    }
    return null;
  }
orb
  • 43
  • 1
  • 10
0

I think u run with debugging mode. So, flutter show every single error, it is ok if u fix or not. So, try to run with without debugging mode and the error will fix, this will not throw the error anymore.

0

When you cancel the sign-in flow, await googleSignIn.signIn() returns null. The crash occurs because you go ahead to create AuthCredential with empty values and then pass that along to signInWithCredential.

To fix the problem, make sure to return early if the result of calling googeSignIn.sign() is null. E.g:

final GoogleSignInAccount googleSignInAccount =
    await googleSignIn.signIn().catchError((onError) => print(onError));

if (googleSignAccount == null) {
  return;
}
Taiwosam
  • 469
  • 7
  • 13