I am trying to create a single interface for Google and Facebook login, my SignInProvider
looks like:
abstract class SignInProvider with ChangeNotifier {
bool get isSigningIn;
set isSigningIn(bool isSigningIn);
void login();
void logout();
}
class FacebookSignInProvider with ChangeNotifier implements SignInProvider { ... }
class GoogleSignInProvider with ChangeNotifier implements SignInProvider { ... }
My Landing page:
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<SignInProvider>(create: (context) => GoogleSignInProvider()),
ChangeNotifierProvider<SignInProvider>(create: (context) => FacebookSignInProvider())
],
child: Scaffold(
body: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
final provider =
Provider.of<SignInProvider>(context, listen: true);
if (provider.isSigningIn) {
return // Show loader
} else if (snapshot.hasData) {
return // Show logged-in UI
} else {
// Give option to login via Google and Facebook
return Row(children: [
TextButton(
onPressed: () {
final provider = Provider.of<SignInProvider>(
context,
listen: false,
);
provider.login();
},
child: const Text('Login with Google'),
),
TextButton(
onPressed: () {
final provider = Provider.of<SignInProvider>(
context,
listen: false,
);
provider.login();
},
child: const Text('Login with Facebook'),
),
],
);
}
}),
),
);
}
}
In this UI, clicking on Login with Google
and Login with Facebook
both are referring to FacebookSignInProvider
, since that is provided last in the list of providers. How can I make it act on respective provider?
When I change the onPressed
definition to Provider.of<GoogleSignInProvider>
or Provider.of<FacebookSignInProvider>
it crashes.
Try this solution
Create a single generic SignInProvider with ChangeNotifier along with SignInType enum
enum SignInType {
none,
facebook,
google
}
class SignInProvider with ChangeNotifier {
bool _isSigningIn = false;
SignInType _signInType = SignInType.none;
set signInType(SignInType signType) {
_signInType = signType;
}
SignInType get signInType {
return _signInType;
}
set signingIn(bool signingIn) {
_isSigningIn = signingIn;
}
bool get signingIn {
return _isSigningIn;
}
void login() {
switch (signInType) {
case SignInType.none:
case SignInType.facebook:
case SignInType.google:
}
}
void logout() {}
}
Create _HomeState class as follows
class _HomeState extends State<Home> {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<SignInProvider>(create: (context) => SignInProvider()),
],
child: Scaffold(
body: StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
final provider =
Provider.of<SignInProvider>(context, listen: true);
if (provider._isSigningIn) {
return const Text("loader");
} else if (snapshot.hasData) {
return const Text("loader");
} else {
// Give option to login via Google and Facebook
return Row(children: [
TextButton(
onPressed: () {
final provider = Provider.of<SignInProvider>(
context,
listen: false,
);
provider.signInType = SignInType.google;
provider.login();
},
child: const Text('Login with Google'),
),
TextButton(
onPressed: () {
final provider = Provider.of<SignInProvider>(
context,
listen: false,
);
provider.signInType = SignInType.facebook;
provider.login();
},
child: const Text('Login with Facebook'),
),
],
);
}
}),
),
);
}
}
This way you will have a more generic provider that can handle multiple type of login providers.
You can also create separate abstract class following the above pattern.
Hope it helps.
Thanks :)