Peter
Peter Software Developer | Technical Writer | Actively helping users with their questions on Stack Overflow. Occasionally I post here and on other platforms.

Using Twitter Authentication With Firebase In Flutter

Using Twitter Authentication With Firebase In Flutter
Source Code Follow me on

In this article, we will add Firebase Authentication and Twitter login to a Flutter application, which will authenticate a user by logging in using the twitter account and then we will retrieve user information in the home page.

Get Started With Twitter Authentication

This is the seventh article related to Firebase in Flutter, you can check the previous articles in the below links:

To know how to download the google-service.json file, you can check the first article in the above list. In the other two articles, I created a form using Flutter performed queries for the realtime database and authenticated users with Firebase, in the cloud firestore article, it was different code snippet related to Firestore and explaining each one. In the last article we integrated google sign in to the flutter application. This article I will demonstrate how to use twitter_login and firebase_auth. This article is aimed for the android phones.

Adding Firebase Auth And Twitter login To Flutter

As I said before, to check how to create a flutter project and add the google-service.json file which is used for android, then please check this article Get Started With Firebase in Flutter. Next, you need to add the following dependency to the pubspec.yaml file:

1
2
3
4
5
6
7
8
9
dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  firebase_auth: ^1.2.0
  firebase_core: ^1.2.0
  font_awesome_flutter: ^9.0.0
  google_sign_in: ^5.0.4
  twitter_login: ^3.0.8

Click CTRL + S to save, and you have successfully added the above dependencies to your Flutter application!

Note:

  • I’m using latest Flutter version 2.0 with null safety enabled, you can enable null safety by executing:
1
dart migrate --apply-changes

Generate Twitter API Key and Secret Key

To use twitter authentication in your application, first you need to create an account at Developer Platform. After getting approved, you need to navigate to Developer Apps, then click on Create App:

firebase auth twitter flutter

After that, give your application any name you want:

firebase auth twitter flutter

Then copy both the api key and api secret key:

firebase auth twitter flutter

Then click on App Settings, enable the 3-legged OAuth and enable Request enable address from users:

firebase auth twitter flutter

Also you need to add your own callback url, since the plugin twitter_login does not work with the firebase url, therefore in the callback url field just add something like this twitter-firebase-auth:// for example.

Enable Twitter Authentication in Firebase

Now before working on the application, you need to navigate to the Firebase console and enable twitter authentication. Therefore navigate to the Authentication tab and then click Sign-in method. You will find multiple providers, but for this tutorial you only need to enable the Twitter provider:

firebase auth twitter flutter

When enabling, you will also be asked to provide both the API key and the secret key that you generated in the previous section. If you want to authenticate using email/password then check this tutorial: Using Firebase Auth In Flutter.

Creating the Twitter Sign in button

The design of this application is based on this dribbble design by Ashlee Mckay. Since it was explained in the previous tutorial, their is no need to explain it again. So, now since we have both Google and Twitter Sign in button, create a component folder and add inside that folder create a file called sign_in_button.dart. Now create a class called SignInButton:

1
2
3
4
5
6
7
8
9
10
class SignInButton extends StatefulWidget {
  final FaIcon faIcon;
  final LoginType loginType;

  SignInButton({Key? key, required this.faIcon, required this.loginType})
      : super(key: key);

  @override
  _SignInButtonState createState() => _SignInButtonState();
}

Since this button will be used for different sign in providers, therefore we create two variables, the faIcon representing the icon and the loginType to know which provider the button will be. The LoginType will be an enum:

1
2
3
4
enum LoginType {
  Google,
  Twitter,
}

Now inside the _SignInButtonState class, do the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class _SignInButtonState extends State<SignInButton> {
  bool isLoading = false;
  FirebaseService service = new FirebaseService();
  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return !isLoading
        ? SizedBox(
            width: size.width * 0.8,
            child: OutlinedButton.icon(
              icon: this.widget.faIcon,
              onPressed: () async {
                setState(() {
                  isLoading = true;
                });
                await this.loginWithProviders();
                setState(() {
                  isLoading = false;
                });
              },
              label: Text(
                this.widget.loginType == LoginType.Google
                    ? Constants.textSignInGoogle
                    : Constants.textSignInTwitter,
                style: TextStyle(
                    color: Constants.kBlackColor, fontWeight: FontWeight.bold),
              ),
              style: ButtonStyle(
                  backgroundColor:
                      MaterialStateProperty.all<Color>(Constants.kGreyColor),
                  side: MaterialStateProperty.all<BorderSide>(BorderSide.none)),
            ),
          )
        : CircularProgressIndicator();
  }

Here we assign the facIcon instance variable to the icon property, and for the Text widget we check if the loginType is LoginType.Google then show Sign In With Google else show Sign In With Twitter.


Now inside the sign_in_page.dart, all you have to do is add the following widgets to the Column widget:

1
2
SignInButton(loginType: LoginType.Google,faIcon: FaIcon(FontAwesomeIcons.google)),
SignInButton(loginType: LoginType.Twitter,faIcon: FaIcon(FontAwesomeIcons.twitter)),

The above code will give you the following screen:

firebase auth twitter flutter

Performing Twitter Sign-in

Now navigate to the FirebaseService class and add the following method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  Future<Resource?> signInWithTwitter() async {
    final twitterLogin = TwitterLogin(
      apiKey: "<api-key>",
      apiSecretKey: "<api-secret-key>",
      redirectURI: "twitter-firebase-auth://",
    );
    final authResult = await twitterLogin.login();

    switch (authResult.status) {
      case TwitterLoginStatus.loggedIn:
        final AuthCredential twitterAuthCredential =
            TwitterAuthProvider.credential(
                accessToken: authResult.authToken!,
                secret: authResult.authTokenSecret!);
        
        final userCredential =
            await _auth.signInWithCredential(twitterAuthCredential);
        return Resource(status: Status.Success);
      case TwitterLoginStatus.cancelledByUser:
           return Resource(status: Status.Success);
      case TwitterLoginStatus.error:
         return Resource(status: Status.Error);
      default:
        return null;
     }
   }

As you can see, here you need to add the api key, api secret key and the url, then when you call the method twitterLogin.login(), it will open the twitter authentication page. If the user clicks on Authorize, then they will be able to login:

firebase auth twitter flutter

The Resource that is returned in the above method is a custom class created to return if the status of the twitter authentication process:

1
2
3
4
5
6
7
8
9
10
11
class Resource{

   final Status status;
  Resource({required this.status});
}

enum Status {
  Success,
  Error,
  Cancelled
}

Note: Don’t forget to add the following to the AndroidManifest.xml file, so the plugin can work correctly:

1
2
3
4
5
6
7
8
<intent-filter>
<action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
        <data android:scheme="twitter-firebase-auth"/>
</intent-filter>

As you can see in the previous section, inside the _SignInButtonState class we have the onPressed property:

1
2
3
4
5
6
7
8
9
onPressed: () async {
setState(() {
    isLoading = true;
    });
await this.loginWithProviders();
setState(() {
    isLoading = false;
    });
},

The loginWithProviders() will have the following implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  Future<void> loginWithProviders() async {
    String? displayName;
    Resource? result;
    try {
      switch (this.widget.loginType) {
        case LoginType.Google:
          displayName = await service.signInwithGoogle();
          break;
        case LoginType.Twitter:
          result = await service.signInWithTwitter();
          break;
      }
      if (result!.status == Status.Success || displayName != null) {
        Navigator.pushNamedAndRemoveUntil(
            context, Constants.homeNavigate, (route) => false);
      }
    } catch (e) {
      if (e is FirebaseAuthException) {
        showMessage(e);
      }
    }
  }

According to the loginType provided, we call either the signInWithTwitter() or signInWithGoogle() and if the result is Status.Success then we navigate to the home page. Now if the user previously logged in using a different auth provider, then it will enter the catch and call the showMessage() method, which will show the following dialog:

firebase auth twitter flutter

Inside the showMessage(), we would have the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void showMessage(FirebaseAuthException e) {
    showDialog(
        context: context,
        builder: (BuildContext builderContext) {
          return AlertDialog(
            title: Text("Error"),
            content: Text(e.message!),
            actions: [
              TextButton(
                child: Text("Ok"),
                onPressed: () async {
                  Navigator.of(builderContext).pop();
                  print(e.code);
                  if (e.code == 'account-exists-with-different-credential') {
                    List<String> emailList = await FirebaseAuth.instance
                        .fetchSignInMethodsForEmail(e.email!);
                    if (emailList.first == "google.com") {
                      await this.service.signInwithGoogle(true, e.credential);
                              Navigator.pushNamedAndRemoveUntil(
            context, Constants.homeNavigate, (route) => false);
                    }
                  }
                },
              )
            ],
          );
        });
  }

So here if the error is account-exists-with-different-credential then we retrieve the sign in methods that the user is using, for example google.com and then we call signInWithGoogle(). Inside the signInWithGoogle, we do the normal flow as explained in previous article:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  Future<String?> signInwithGoogle(
      [bool link = false, AuthCredential? authCredential]) async {
    try {
      final GoogleSignInAccount? googleSignInAccount =
          await _googleSignIn.signIn();

      final GoogleSignInAuthentication googleSignInAuthentication =
          await googleSignInAccount!.authentication;
      final AuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleSignInAuthentication.accessToken,
        idToken: googleSignInAuthentication.idToken,
      );
      UserCredential userCredential =
          await _auth.signInWithCredential(credential);
      if (link) {
        await linkProviders(userCredential, authCredential!);
      }
      return userCredential.user!.displayName;
    } on FirebaseAuthException catch (e) {
      print(e.message);
      throw e;
    }
  }

The only additional thing, is that if the argument link is true, then call linkProviders():

1
2
3
  Future<UserCredential?> linkProviders(UserCredential userCredential, AuthCredential newCredential) async {
    return await userCredential.user!.linkWithCredential(newCredential);
  }

which will give you the following in the firebase console:

firebase auth twitter flutter

Regarding the home page, check Retrieve User Information

I hope you enjoyed reading this flutter tutorial, please feel free to leave any comments or feedback on this post!

 

Become a Patron!