@@ -119,8 +119,10 @@ class BooleanMetaDataField extends MetaDataField {
119119 this .isRequired = false ,
120120 this .checkboxPosition = ListTileControlAffinity .platform,
121121 required super .key,
122- }) : assert (label != null || richLabelSpans != null ,
123- 'Either label or richLabelSpans must be provided' ),
122+ }) : assert (
123+ label != null || richLabelSpans != null ,
124+ 'Either label or richLabelSpans must be provided' ,
125+ ),
124126 super (label: label ?? '' );
125127
126128 Widget getLabelWidget (BuildContext context) {
@@ -132,10 +134,7 @@ class BooleanMetaDataField extends MetaDataField {
132134 : Theme .of (context).textTheme.titleMedium;
133135 return richLabelSpans != null
134136 ? RichText (
135- text: TextSpan (
136- style: defaultStyle,
137- children: richLabelSpans,
138- ),
137+ text: TextSpan (style: defaultStyle, children: richLabelSpans),
139138 )
140139 : Text (label, style: defaultStyle);
141140 }
@@ -226,6 +225,15 @@ class SupaEmailAuth extends StatefulWidget {
226225 /// Pre-filled password for the form
227226 final String ? prefilledPassword;
228227
228+ /// Whether pressing Enter on the on-screen keyboard should automatically
229+ /// submit the form.
230+ ///
231+ /// When set to `false` , the user must explicitly click the submit button
232+ /// to proceed with the authentication process.
233+ ///
234+ /// Defaults to `true` for backward compatibility.
235+ final bool enableAutomaticFormSubmission;
236+
229237 /// {@macro supa_email_auth}
230238 const SupaEmailAuth ({
231239 super .key,
@@ -248,6 +256,7 @@ class SupaEmailAuth extends StatefulWidget {
248256 this .showConfirmPasswordField = false ,
249257 this .prefilledEmail,
250258 this .prefilledPassword,
259+ this .enableAutomaticFormSubmission = true ,
251260 });
252261
253262 @override
@@ -276,14 +285,16 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
276285 _emailController.text = widget.prefilledEmail ?? '' ;
277286 _passwordController.text = widget.prefilledPassword ?? '' ;
278287 _isSigningIn = widget.isInitiallySigningIn;
279- _metadataControllers = Map .fromEntries ((widget.metadataFields ?? []).map (
280- (metadataField) => MapEntry (
281- metadataField.key,
282- metadataField is BooleanMetaDataField
283- ? metadataField.value
284- : TextEditingController (),
288+ _metadataControllers = Map .fromEntries (
289+ (widget.metadataFields ?? []).map (
290+ (metadataField) => MapEntry (
291+ metadataField.key,
292+ metadataField is BooleanMetaDataField
293+ ? metadataField.value
294+ : TextEditingController (),
295+ ),
285296 ),
286- )) ;
297+ );
287298 }
288299
289300 @override
@@ -331,7 +342,8 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
331342 ),
332343 controller: _emailController,
333344 onFieldSubmitted: (_) {
334- if (_isRecoveringPassword) {
345+ if (_isRecoveringPassword &&
346+ widget.enableAutomaticFormSubmission) {
335347 _passwordRecovery ();
336348 }
337349 },
@@ -360,7 +372,8 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
360372 obscureText: true ,
361373 controller: _passwordController,
362374 onFieldSubmitted: (_) {
363- if (widget.metadataFields == null || _isSigningIn) {
375+ if ((widget.metadataFields == null || _isSigningIn) &&
376+ widget.enableAutomaticFormSubmission) {
364377 _signInSignUp ();
365378 }
366379 },
@@ -385,91 +398,96 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
385398 spacer (16 ),
386399 if (widget.metadataFields != null && ! _isSigningIn)
387400 ...widget.metadataFields!
388- .map ((metadataField) => [
389- // Render a Checkbox that displays an error message
390- // beneath it if the field is required and the user
391- // hasn't checked it when submitting the form.
392- if (metadataField is BooleanMetaDataField )
393- FormField <bool >(
394- validator: metadataField.isRequired
395- ? (bool ? value) {
396- if (value != true ) {
397- return localization.requiredFieldError;
398- }
399- return null ;
401+ .map (
402+ (metadataField) => [
403+ // Render a Checkbox that displays an error message
404+ // beneath it if the field is required and the user
405+ // hasn't checked it when submitting the form.
406+ if (metadataField is BooleanMetaDataField )
407+ FormField <bool >(
408+ validator: metadataField.isRequired
409+ ? (bool ? value) {
410+ if (value != true ) {
411+ return localization.requiredFieldError;
400412 }
401- : null ,
402- builder: (FormFieldState <bool > field) {
403- final theme = Theme .of (context);
404-
405- return Column (
406- crossAxisAlignment: CrossAxisAlignment .start,
407- children: [
408- CheckboxListTile (
409- title:
410- metadataField.getLabelWidget (context),
411- value: _metadataControllers[
412- metadataField.key] as bool ,
413- onChanged: (bool ? value) {
414- setState (() {
415- _metadataControllers[metadataField
416- .key] = value ?? false ;
417- });
418- field.didChange (value);
419- },
420- checkboxSemanticLabel:
421- metadataField.checkboxSemanticLabel,
422- controlAffinity:
423- metadataField.checkboxPosition,
424- contentPadding:
425- const EdgeInsets .symmetric (
426- horizontal: 4.0 ),
413+ return null ;
414+ }
415+ : null ,
416+ builder: (FormFieldState <bool > field) {
417+ final theme = Theme .of (context);
418+
419+ return Column (
420+ crossAxisAlignment: CrossAxisAlignment .start,
421+ children: [
422+ CheckboxListTile (
423+ title: metadataField.getLabelWidget (
424+ context,
425+ ),
426+ value:
427+ _metadataControllers[metadataField.key]
428+ as bool ,
429+ onChanged: (bool ? value) {
430+ setState (() {
431+ _metadataControllers[
432+ metadataField.key] = value ?? false ;
433+ });
434+ field.didChange (value);
435+ },
436+ checkboxSemanticLabel:
437+ metadataField.checkboxSemanticLabel,
438+ controlAffinity:
439+ metadataField.checkboxPosition,
440+ contentPadding: const EdgeInsets .symmetric (
441+ horizontal: 4.0 ,
427442 ),
428- if (field.hasError)
429- Padding (
430- padding: const EdgeInsets .only (
431- left: 16 , top: 4 ),
432- child: Text (
433- field.errorText! ,
434- style: theme.textTheme.labelSmall
435- ? .copyWith (
436- color: theme.colorScheme.error,
437- ),
443+ ),
444+ if (field.hasError)
445+ Padding (
446+ padding: const EdgeInsets .only (
447+ left: 16 ,
448+ top: 4 ,
449+ ),
450+ child: Text (
451+ field.errorText! ,
452+ style: theme.textTheme.labelSmall
453+ ? .copyWith (
454+ color: theme.colorScheme.error,
438455 ),
439456 ),
440- ],
441- );
442- },
443- )
444- else
445- // Otherwise render a normal TextFormField matching
446- // the style of the other fields in the form.
447- TextFormField (
448- controller:
449- _metadataControllers[metadataField.key]
450- as TextEditingController ,
451- textInputAction:
452- widget.metadataFields! .last == metadataField
453- ? TextInputAction .done
454- : TextInputAction .next,
455- decoration: InputDecoration (
456- label: Text (metadataField.label),
457- prefixIcon: metadataField.prefixIcon,
458- ),
459- validator: metadataField.validator,
460- autovalidateMode:
461- AutovalidateMode .onUserInteraction,
462- onFieldSubmitted: (_) {
463- if (metadataField !=
464- widget.metadataFields! .last) {
465- FocusScope .of (context).nextFocus ();
466- } else {
467- _signInSignUp ();
468- }
469- },
457+ ),
458+ ],
459+ );
460+ },
461+ )
462+ else
463+ // Otherwise render a normal TextFormField matching
464+ // the style of the other fields in the form.
465+ TextFormField (
466+ controller: _metadataControllers[metadataField.key]
467+ as TextEditingController ,
468+ textInputAction:
469+ widget.metadataFields! .last == metadataField
470+ ? TextInputAction .done
471+ : TextInputAction .next,
472+ decoration: InputDecoration (
473+ label: Text (metadataField.label),
474+ prefixIcon: metadataField.prefixIcon,
470475 ),
471- spacer (16 ),
472- ])
476+ validator: metadataField.validator,
477+ autovalidateMode:
478+ AutovalidateMode .onUserInteraction,
479+ onFieldSubmitted: (_) {
480+ if (metadataField !=
481+ widget.metadataFields! .last) {
482+ FocusScope .of (context).nextFocus ();
483+ } else if (widget.enableAutomaticFormSubmission) {
484+ _signInSignUp ();
485+ }
486+ },
487+ ),
488+ spacer (16 ),
489+ ],
490+ )
473491 .expand ((element) => element),
474492 ElevatedButton (
475493 onPressed: _signInSignUp,
@@ -482,9 +500,11 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
482500 strokeWidth: 1.5 ,
483501 ),
484502 )
485- : Text (_isSigningIn
486- ? localization.signIn
487- : localization.signUp),
503+ : Text (
504+ _isSigningIn
505+ ? localization.signIn
506+ : localization.signUp,
507+ ),
488508 ),
489509 spacer (16 ),
490510 if (_isSigningIn) ...[
@@ -508,9 +528,11 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
508528 widget.onToggleSignIn? .call (_isSigningIn);
509529 widget.onToggleRecoverPassword? .call (_isRecoveringPassword);
510530 },
511- child: Text (_isSigningIn
512- ? localization.dontHaveAccount
513- : localization.haveAccount),
531+ child: Text (
532+ _isSigningIn
533+ ? localization.dontHaveAccount
534+ : localization.haveAccount,
535+ ),
514536 ),
515537 ],
516538 if (_isSigningIn && _isRecoveringPassword) ...[
@@ -584,7 +606,8 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
584606 } catch (error) {
585607 if (widget.onError == null && mounted) {
586608 context.showErrorSnackBar (
587- '${widget .localization .unexpectedError }: $error ' );
609+ '${widget .localization .unexpectedError }: $error ' ,
610+ );
588611 } else {
589612 widget.onError? .call (error);
590613 }
@@ -645,10 +668,15 @@ class _SupaEmailAuthState extends State<SupaEmailAuth> {
645668
646669 /// Resolve the user_metadata coming from the metadataFields
647670 Map <String , dynamic > _resolveMetadataFieldsData () {
648- return Map .fromEntries (_metadataControllers.entries.map ((entry) => MapEntry (
649- entry.key,
650- entry.value is TextEditingController
651- ? (entry.value as TextEditingController ).text
652- : entry.value)));
671+ return Map .fromEntries (
672+ _metadataControllers.entries.map (
673+ (entry) => MapEntry (
674+ entry.key,
675+ entry.value is TextEditingController
676+ ? (entry.value as TextEditingController ).text
677+ : entry.value,
678+ ),
679+ ),
680+ );
653681 }
654682}
0 commit comments