From 099a0e9e53c9d7a6e718d1a91b20544f33a7f8ba Mon Sep 17 00:00:00 2001 From: Dominik Helfenstein Date: Sat, 6 Jul 2024 21:53:52 +0200 Subject: [PATCH] chore: allow using external auth state change listener to avoid duplicate listeners (#104) --- lib/src/components/supa_email_auth.dart | 12 +++---- lib/src/components/supa_magic_auth.dart | 36 +++++++++++++-------- lib/src/components/supa_socials_auth.dart | 38 +++++++++++++++-------- 3 files changed, 54 insertions(+), 32 deletions(-) diff --git a/lib/src/components/supa_email_auth.dart b/lib/src/components/supa_email_auth.dart index a87fbd7..85ac10f 100644 --- a/lib/src/components/supa_email_auth.dart +++ b/lib/src/components/supa_email_auth.dart @@ -64,12 +64,12 @@ class SupaEmailAuth extends StatefulWidget { final String? resetPasswordRedirectTo; /// Callback for the user to complete a sign in. - final void Function(AuthResponse response) onSignInComplete; + final void Function(AuthResponse response)? onSignInComplete; /// Callback for the user to complete a signUp. /// /// If email confirmation is turned on, the user is - final void Function(AuthResponse response) onSignUpComplete; + final void Function(AuthResponse response)? onSignUpComplete; /// Callback for sending the password reset email final void Function()? onPasswordResetEmailSent; @@ -100,8 +100,8 @@ class SupaEmailAuth extends StatefulWidget { super.key, this.redirectTo, this.resetPasswordRedirectTo, - required this.onSignInComplete, - required this.onSignUpComplete, + this.onSignInComplete, + this.onSignUpComplete, this.onPasswordResetEmailSent, this.onError, this.onToggleSignIn, @@ -242,7 +242,7 @@ class _SupaEmailAuthState extends State { email: _emailController.text.trim(), password: _passwordController.text.trim(), ); - widget.onSignInComplete.call(response); + widget.onSignInComplete?.call(response); } else { final user = supabase.auth.currentUser; late final AuthResponse response; @@ -265,7 +265,7 @@ class _SupaEmailAuthState extends State { data: _resolveData(), ); } - widget.onSignUpComplete.call(response); + widget.onSignUpComplete?.call(response); } } on AuthException catch (error) { if (widget.onError == null && context.mounted) { diff --git a/lib/src/components/supa_magic_auth.dart b/lib/src/components/supa_magic_auth.dart index 71f366d..7c087c2 100644 --- a/lib/src/components/supa_magic_auth.dart +++ b/lib/src/components/supa_magic_auth.dart @@ -13,22 +13,28 @@ class SupaMagicAuth extends StatefulWidget { /// Typically used to pass a DeepLink final String? redirectUrl; - /// Method to be called when the auth action is success - final void Function(Session response) onSuccess; + /// Method to be called when the auth action is successful. + /// Only called when `useExternalAuthChangeListener` is false + final void Function(Session response)? onSuccess; /// Method to be called when the auth action threw an excepction final void Function(Object error)? onError; + /// Setting this to true will not instantiate a new onAuthStateChanged + /// subscription. Must not be used together with `onSuccess` + final bool useExternalAuthChangeListener; + /// Localization for the form final SupaMagicAuthLocalization localization; const SupaMagicAuth({ super.key, this.redirectUrl, - required this.onSuccess, + this.onSuccess, this.onError, + this.useExternalAuthChangeListener = false, this.localization = const SupaMagicAuthLocalization(), - }); + }) : assert(useExternalAuthChangeListener == false || onSuccess == null); @override State createState() => _SupaMagicAuthState(); @@ -37,26 +43,30 @@ class SupaMagicAuth extends StatefulWidget { class _SupaMagicAuthState extends State { final _formKey = GlobalKey(); final _email = TextEditingController(); - late final StreamSubscription _gotrueSubscription; + late final StreamSubscription? _gotrueSubscription; bool _isLoading = false; @override void initState() { super.initState(); - _gotrueSubscription = - Supabase.instance.client.auth.onAuthStateChange.listen((data) { - final session = data.session; - if (session != null && mounted) { - widget.onSuccess(session); - } - }); + if (widget.useExternalAuthChangeListener) { + _gotrueSubscription = null; + } else { + _gotrueSubscription = + Supabase.instance.client.auth.onAuthStateChange.listen((data) { + final session = data.session; + if (session != null && mounted) { + widget.onSuccess?.call(session); + } + }); + } } @override void dispose() { _email.dispose(); - _gotrueSubscription.cancel(); + _gotrueSubscription?.cancel(); super.dispose(); } diff --git a/lib/src/components/supa_socials_auth.dart b/lib/src/components/supa_socials_auth.dart index cb4b89c..f79cd92 100644 --- a/lib/src/components/supa_socials_auth.dart +++ b/lib/src/components/supa_socials_auth.dart @@ -107,12 +107,17 @@ class SupaSocialsAuth extends StatefulWidget { /// Typically used to pass a DeepLink final String? redirectUrl; - /// Method to be called when the auth action is success - final void Function(Session session) onSuccess; + /// Method to be called when the auth action is successful. + /// Only called when `useExternalAuthChangeListener` is false + final void Function(Session response)? onSuccess; /// Method to be called when the auth action threw an excepction final void Function(Object error)? onError; + /// Setting this to true will not instantiate a new onAuthStateChanged + /// subscription. Must not be used together with `onSuccess` + final bool useExternalAuthChangeListener; + /// Whether to show a SnackBar after a successful sign in final bool showSuccessSnackBar; @@ -132,21 +137,22 @@ class SupaSocialsAuth extends StatefulWidget { required this.socialProviders, this.colored = true, this.redirectUrl, - required this.onSuccess, + this.onSuccess, this.onError, + this.useExternalAuthChangeListener = false, this.socialButtonVariant = SocialButtonVariant.iconAndText, this.showSuccessSnackBar = true, this.scopes, this.queryParams, this.localization = const SupaSocialsAuthLocalization(), - }); + }) : assert(useExternalAuthChangeListener == false || onSuccess == null); @override State createState() => _SupaSocialsAuthState(); } class _SupaSocialsAuthState extends State { - late final StreamSubscription _gotrueSubscription; + late final StreamSubscription? _gotrueSubscription; late final SupaSocialsAuthLocalization localization; /// Performs Google sign in on Android and iOS @@ -210,22 +216,28 @@ class _SupaSocialsAuthState extends State { void initState() { super.initState(); localization = widget.localization; - _gotrueSubscription = - Supabase.instance.client.auth.onAuthStateChange.listen((data) { - final session = data.session; - if (session != null && mounted) { - widget.onSuccess.call(session); + if (widget.useExternalAuthChangeListener) { + _gotrueSubscription = null; + } else { + _gotrueSubscription = + Supabase.instance.client.auth.onAuthStateChange.listen((data) { + final session = data.session; + final onSuccess = widget.onSuccess; + if (session != null && mounted && onSuccess != null) { + onSuccess(session); + widget.onSuccess?.call(session); if (widget.showSuccessSnackBar) { context.showSnackBar(localization.successSignInMessage); } - } - }); + } + }); + } } @override void dispose() { super.dispose(); - _gotrueSubscription.cancel(); + _gotrueSubscription?.cancel(); } @override