flutterfirebase-authenticationfirebaseuiapple-id

Firebase multiple authentication (Google and Apple) with Flutter app


I am having a hard time understanding on how I can design a flutter screen with firebase authentication with multiple login method on the same screen. The Authentication screen (Auth_gate.dart) in my case should support:

My Firebase project as been initialised properly with Flutter.

My flutter project is well configured with firebase as per documentation. The in-built "SignInScreen" from the firebase_auth package is doing the job well for Google sign-in And email. https://firebase.flutter.dev/docs/ui/auth/integrating-your-first-screen/

Since it is working fine for Email and Google methods, I was thinking I can just add a new AppleProvider in the providers list, it will render the 'Sign in with apple' button and handle the flow like it did with Google.

I have set up my Apple developer to support the Apple Sign in method this is fine. https://firebase.google.com/docs/auth/ios/apple?authuser=0#enable-apple-as-a-sign-in-provider

In the Documentation of firebase, in the flutter section I only can find these line of code, I am note sure where to put them.

https://firebase.google.com/docs/auth/flutter/federated-auth

import 'package:firebase_auth/firebase_auth.dart';

Future<UserCredential> signInWithApple() async {
  final appleProvider = AppleAuthProvider();
  if (kIsWeb) {
    await FirebaseAuth.instance.signInWithPopup(appleProvider);
  } else {
    await FirebaseAuth.instance.signInWithProvider(appleProvider);
  }
}

It seems the SignInScreen do not support AppleProvider() because I am getting the following error:

The element type 'AppleAuthProvider' can't be assigned to the list type 'AuthProvider<AuthListener, AuthCredential>'. (Documentation)

I have consulted the following documentation links with not much insigths: https://firebase.flutter.dev/docs/ui/auth/building-a-custom-ui/ https://firebase.google.com/docs/auth/flutter/account-linking

Can someone explain what I am doing wrong? and how to get me back on track? Much appreciated.

main.dart

import 'dart:async';
import 'package:EcoClick/screens/auth_gate.dart';
import 'package:EcoClick/screens/on_boarding.dart';
import 'package:EcoClick/screens/permission_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'languages/deleguates.dart';
import 'languages/localizations.dart';
import 'utils/database_helper.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

//Widget screen = const OnboardingScreen();
Widget screen = const AuthGate();

void main() {
  runZonedGuarded(() async {
    try {
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform,
      );
      final prefs = await SharedPreferences.getInstance();
      final bool? look = prefs.getBool('look');
      if (look == false || look == null) {
        //screen = const OnboardingScreen();
      } else {
        //screen = const PermissionWrapper();
      }
      runApp(
        Builder(
          builder: (context) =>
              Provider.value(
                  value: null,
                  builder: (context, child) => Greenprint(screen)),
        ),
      );
    }
    catch (error) {
      if (kDebugMode) {
        print('error in main');
      }
    }
  }, (Object e, StackTrace s) {
  }, zoneSpecification: ZoneSpecification(
    print: (Zone self, ZoneDelegate parent, Zone zone, String message) {
    },
  ));
}

class Greenprint extends StatelessWidget {
  final Widget screen;

  const Greenprint(this.screen, {super.key});

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [ChangeNotifierProvider<MyDatabaseHelper>(
          create: (_) => MyDatabaseHelper()),],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        onGenerateTitle: (BuildContext context) =>
        MyLocalizations
            .of(context)
            .homeTitle,
        localizationsDelegates: const [
          MyLocalizationsDelegate(),
          GlobalMaterialLocalizations.delegate,
          GlobalWidgetsLocalizations.delegate,
        ],
        supportedLocales: const [
          Locale('en', ''),
          Locale('fr', ''),
        ],
        title: 'GreenPrint',
        //theme: ThemeData.dark(),
        home: screen,
      ),
    );
  }
}

auth_gate.dart

import 'package:EcoClick/screens/camera_widget.dart';
import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart'; // new
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';


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

  AppleAuthProvider test(){
    var appleProvider = AppleAuthProvider();
    appleProvider.addScope('email');
    appleProvider.setCustomParameters({
      'locale': 'fr',
    });

    FirebaseAuth.instance.signInWithPopup(appleProvider);
    return appleProvider;
  }

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [
              EmailAuthProvider(),
              GoogleProvider(clientId: "0000000.apps.googleusercontent.com"),  //OK
              AppleAuthProvider() // Gives error: The element type 'AppleAuthProvider' can't be assigned to the list type 'AuthProvider<AuthListener, AuthCredential>'. (Documentation)

             ],
            headerBuilder: (context, constraints, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('images/icon/icon.png'),
                ),
              );
            },
            subtitleBuilder: (context, action) {
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: action == AuthAction.signIn
                    ? const Text('Welcome to FlutterFire, please sign in!')
                    : const Text('Welcome to Flutterfire, please sign up!'),
              );
            },

            footerBuilder: (context, action) {
              return const Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text(
                  'By signing in, you agree to our terms and conditions.',
                  style: TextStyle(color: Colors.grey),
                ),
              );
            },
            sideBuilder: (context, shrinkOffset) {
              return Padding(
                padding: const EdgeInsets.all(20),
                child: AspectRatio(
                  aspectRatio: 1,
                  child: Image.asset('flutterfire_300x.png'),
                ),
              );
            },

          );
        }

        return const CameraScreen();
      },
    );
  }
}

Solution

  • Well, kind of answering my own question! This helped a lot to understand. https://www.youtube.com/watch?v=dMS4TSFwml8&t=902s&ab_channel=ShivamGupta https://github.com/shivam22rkl/Google-Apple-SignIn/tree/main

    Hope it helps, Cheers