diff --git a/lib/src/auth_screen.dart b/lib/src/auth_screen.dart index 8d9d2b9..96777db 100644 --- a/lib/src/auth_screen.dart +++ b/lib/src/auth_screen.dart @@ -109,6 +109,7 @@ class _AuthScreenState extends State { AppBar get _appBar => widget.customAppBar ?? AppBar( + backgroundColor: const Color(0xffFAF9F6), title: Text(widget.appBarTitle), ); @@ -195,150 +196,186 @@ class _AuthScreenState extends State { ), ) : Scaffold( - backgroundColor: widget.customBackgroundColor ?? Colors.white, + backgroundColor: + widget.customBackgroundColor ?? const Color(0xffFAF9F6), appBar: _appBar, - body: Form( - key: _formKey, - child: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _pageController, - children: [ - for (var i = 0; i < widget.steps.length; i++) - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (widget.titleWidget != null) ...[ - Expanded( - flex: widget.titleFlex ?? 1, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - flex: widget.beforeTitleFlex ?? 3, - child: Container(), - ), - widget.titleWidget!, - Expanded( - flex: widget.afterTitleFlex ?? 2, - child: Container(), - ), - ], - ), - ), - ], - Expanded( - flex: widget.formFlex ?? 3, - child: Align( - child: Column( - children: [ - for (AuthField field - in widget.steps[i].fields) ...[ - if (field.title != null) ...[ - field.title!, - ], - field.build(context, () { - _validate(i); - }) - ] - ], - ), - ), - ), - Column( - children: [ - Row( - mainAxisAlignment: widget - .buttonMainAxisAlignment != - null - ? widget.buttonMainAxisAlignment! - : (widget.previousButtonBuilder != null && - widget.previousButtonBuilder?.call( - onPrevious, - widget.previousBtnTitle, - i, - ) == - null) - ? MainAxisAlignment.start - : widget.steps.first != widget.steps[i] - ? MainAxisAlignment.center - : MainAxisAlignment.end, - children: [ - if (widget.previousButtonBuilder == null) ...[ - if (widget.steps.first != widget.steps[i]) - ElevatedButton( - onPressed: onPrevious, - child: Row( - children: [ - const Icon( - Icons.arrow_back, - size: 18, - ), - Padding( - padding: const EdgeInsets.only( - left: 4.0), - child: - Text(widget.previousBtnTitle), - ), - ], - ), - ), - ] else if (widget.previousButtonBuilder?.call( - onPrevious, - widget.previousBtnTitle, - i) != - null) ...[ - widget.previousButtonBuilder!.call( - onPrevious, widget.previousBtnTitle, i)! + body: SafeArea( + child: Form( + key: _formKey, + child: PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _pageController, + children: [ + for (var i = 0; i < widget.steps.length; i++) + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (widget.titleWidget != null) ...[ + Expanded( + flex: widget.titleFlex ?? 1, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + flex: widget.beforeTitleFlex ?? 2, + child: Container(), + ), + widget.titleWidget!, + Expanded( + flex: widget.afterTitleFlex ?? 2, + child: Container(), + ), ], - widget.nextButtonBuilder?.call( - !_formValid - ? null - : () async { - await onNext(widget.steps[i]); - }, - widget.steps.last == widget.steps[i] - ? widget.submitBtnTitle - : widget.nextBtnTitle, - i, - _formValid, - ) ?? - ElevatedButton( - onPressed: !_formValid - ? null - : () async { - await onNext(widget.steps[i]); - }, - child: Row( - children: [ - Text( - widget.steps.last == widget.steps[i] - ? widget.submitBtnTitle - : widget.nextBtnTitle, - ), - const Padding( - padding: EdgeInsets.only(left: 4.0), - child: Icon( - Icons.arrow_forward, - size: 18, + ), + ), + ], + Expanded( + flex: widget.formFlex ?? 3, + child: Align( + child: Column( + children: [ + for (AuthField field + in widget.steps[i].fields) ...[ + if (field.title != null) ...[ + field.title!, + ], + field.build(context, () { + _validate(i); + }) + ] + ], + ), + ), + ), + Column( + children: [ + Row( + mainAxisAlignment: widget + .buttonMainAxisAlignment != + null + ? widget.buttonMainAxisAlignment! + : (widget.previousButtonBuilder != null && + widget.previousButtonBuilder?.call( + onPrevious, + widget.previousBtnTitle, + i, + ) == + null) + ? MainAxisAlignment.start + : widget.steps.first != widget.steps[i] + ? MainAxisAlignment.center + : MainAxisAlignment.end, + children: [ + if (widget.previousButtonBuilder == null) ...[ + if (widget.steps.first != widget.steps[i]) + Padding( + padding: const EdgeInsets.only( + left: 16, bottom: 10, right: 8), + child: InkWell( + onTap: onPrevious, + child: Container( + width: 180, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(20), + border: Border.all( + color: const Color( + 0xff979797, + ), + ), + ), + child: Center( + child: Padding( + padding: + const EdgeInsets.symmetric( + vertical: 2.0), + child: Text( + widget.previousBtnTitle, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), ), ), - ], + ), ), - ), - ], - ), - if (widget.loginButton != null) - Padding( - padding: const EdgeInsets.only(top: 20.0), - child: widget.loginButton!, + ] else if (widget.previousButtonBuilder?.call( + onPrevious, + widget.previousBtnTitle, + i) != + null) ...[ + widget.previousButtonBuilder!.call( + onPrevious, widget.previousBtnTitle, i)! + ], + widget.nextButtonBuilder?.call( + !_formValid + ? null + : () async { + await onNext(widget.steps[i]); + }, + widget.steps.last == widget.steps[i] + ? widget.submitBtnTitle + : widget.nextBtnTitle, + i, + _formValid, + ) ?? + Padding( + padding: const EdgeInsets.only( + right: 16, bottom: 10, left: 8), + child: InkWell( + onTap: !_formValid + ? null + : () async { + await onNext(widget.steps[i]); + }, + child: Container( + width: 180, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(20), + border: Border.all( + color: const Color( + 0xff979797, + ), + ), + ), + child: Center( + child: Padding( + padding: + const EdgeInsets.symmetric( + vertical: 2.0), + child: Text( + widget.steps.last == + widget.steps[i] + ? widget.submitBtnTitle + : widget.nextBtnTitle, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ), + ), + ), + ], ), - ], - ), - ], - ), - ], + if (widget.loginButton != null) + Padding( + padding: const EdgeInsets.only(top: 20.0), + child: widget.loginButton!, + ), + ], + ), + ], + ), + ], + ), ), ), ); diff --git a/lib/src/config/example_registration_repository.dart b/lib/src/config/example_registration_repository.dart new file mode 100644 index 0000000..afc2421 --- /dev/null +++ b/lib/src/config/example_registration_repository.dart @@ -0,0 +1,11 @@ +import 'dart:collection'; +import 'package:flutter/material.dart'; +import 'package:flutter_registration/flutter_registration.dart'; + +class ExampleRegistrationRepository with RegistrationRepository { + @override + Future register(HashMap values) { + debugPrint('register $values'); + return Future.value(null); + } +} diff --git a/lib/src/config/registration_options.dart b/lib/src/config/registration_options.dart index 2b39eb6..b2c78a5 100644 --- a/lib/src/config/registration_options.dart +++ b/lib/src/config/registration_options.dart @@ -6,12 +6,13 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_registration/flutter_registration.dart'; +import 'package:flutter_registration/src/config/example_registration_repository.dart'; /// A set of options for configuring the registration process in a Flutter application. class RegistrationOptions { RegistrationOptions({ - required this.registrationRepository, - required this.registrationSteps, + this.registrationRepository, + this.registrationSteps, required this.afterRegistration, this.titleFlex, this.formFlex, @@ -19,20 +20,29 @@ class RegistrationOptions { this.afterTitleFlex, this.registrationTranslations = const RegistrationTranslations.empty(), this.onError, - this.customAppbarBuilder, + this.customAppbarBuilder = _createCustomAppBar, this.nextButtonBuilder, this.previousButtonBuilder, this.buttonMainAxisAlignment, this.backgroundColor, this.titleWidget, this.loginButton, - }); + }) { + if (registrationSteps == null || registrationSteps!.isEmpty) { + steps = RegistrationOptions.getDefaultSteps(); + } else { + steps = registrationSteps!; + } + registrationRepository ??= ExampleRegistrationRepository(); + } /// Translations for registration-related messages and prompts. final RegistrationTranslations registrationTranslations; /// The steps involved in the registration process. - final List registrationSteps; + final List? registrationSteps; + + List steps = []; /// A function that handles errors during registration. final int? Function(String error)? onError; @@ -41,7 +51,7 @@ class RegistrationOptions { final VoidCallback afterRegistration; /// The repository responsible for registration. - final RegistrationRepository registrationRepository; + RegistrationRepository? registrationRepository; /// A function for customizing the app bar displayed during registration. final AppBar Function(String title)? customAppbarBuilder; @@ -61,10 +71,10 @@ class RegistrationOptions { final Color? backgroundColor; /// A custom widget for displaying the registration title. - Widget? titleWidget; + final Widget? titleWidget; /// A custom widget for displaying a login button. - Widget? loginButton; + final Widget? loginButton; /// The number of flex units for the title. final int? titleFlex; @@ -103,10 +113,8 @@ class RegistrationOptions { /// [initialEmail] initial value for email input. static List getDefaultSteps({ TextEditingController? emailController, - TextEditingController? pass1Controller, - bool pass1Hidden = true, - TextEditingController? pass2Controller, - bool pass2Hidden = true, + TextEditingController? passController, + bool passHidden = true, Function(bool mainPass, bool value)? passHideOnChange, RegistrationTranslations translations = const RegistrationTranslations.empty(), @@ -115,8 +123,6 @@ class RegistrationOptions { TextStyle? textStyle, String? initialEmail, }) { - var password1 = ''; - return [ AuthStep( fields: [ @@ -125,18 +131,25 @@ class RegistrationOptions { textEditingController: emailController, value: initialEmail ?? '', title: titleBuilder?.call(translations.defaultEmailTitle) ?? - Padding( - padding: const EdgeInsets.only(top: 24.0, bottom: 12.0), + const Padding( + padding: EdgeInsets.only(top: 180), child: Text( - translations.defaultEmailTitle, - style: const TextStyle(fontWeight: FontWeight.bold), + 'Enter your e-mail', + style: TextStyle( + color: Color(0xff71C6D1), + fontWeight: FontWeight.w800, + fontSize: 24, + ), ), ), textFieldDecoration: InputDecoration( + contentPadding: const EdgeInsets.symmetric(horizontal: 8), label: labelBuilder?.call(translations.defaultEmailLabel), hintText: translations.defaultEmailHint, + border: const OutlineInputBorder(), ), textStyle: textStyle, + padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 20), validators: [ (email) => (email == null || email.isEmpty) ? translations.defaultEmailEmpty @@ -147,212 +160,49 @@ class RegistrationOptions { ? null : translations.defaultEmailValidatorMessage, ], - ) + ), ], ), AuthStep( fields: [ AuthPassField( - name: 'password1', - textEditingController: pass1Controller, - title: titleBuilder?.call(translations.defaultPassword1Title) ?? + name: 'password', + textEditingController: passController, + title: titleBuilder?.call(translations.defaultPasswordTitle) ?? Padding( - padding: const EdgeInsets.only(top: 24.0, bottom: 12.0), + padding: const EdgeInsets.only(top: 180), child: Text( - translations.defaultPassword1Title, - style: const TextStyle(fontWeight: FontWeight.bold), + translations.defaultPasswordTitle, + style: const TextStyle( + color: Color(0xff71C6D1), + fontWeight: FontWeight.w800, + fontSize: 24, + ), ), ), textFieldDecoration: InputDecoration( - label: labelBuilder?.call(translations.defaultPassword1Label), - hintText: translations.defaultPassword1Hint, + contentPadding: const EdgeInsets.symmetric(horizontal: 8), + label: labelBuilder?.call(translations.defaultPasswordLabel), + hintText: translations.defaultPasswordHint, + border: const OutlineInputBorder(), ), + padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 20), textStyle: textStyle, validators: [ (value) => (value == null || value.isEmpty) - ? translations.defaultPassword1ValidatorMessage + ? translations.defaultPasswordValidatorMessage : null, ], - onChange: (value) { - password1 = value; - }, - ), - AuthPassField( - name: 'password2', - textEditingController: pass2Controller, - title: titleBuilder?.call(translations.defaultPassword2Title) ?? - Padding( - padding: const EdgeInsets.only(top: 24.0, bottom: 12.0), - child: Text( - translations.defaultPassword2Title, - style: const TextStyle(fontWeight: FontWeight.bold), - ), - ), - textFieldDecoration: InputDecoration( - label: labelBuilder?.call(translations.defaultPassword2Label), - hintText: translations.defaultPassword2Hint, - ), - textStyle: textStyle, - validators: [ - (value) { - if (pass1Controller != null) { - if (value != pass1Controller.value.text) { - return translations.defaultPassword2ValidatorMessage; - } - } else { - if (value != password1) { - return translations.defaultPassword2ValidatorMessage; - } - } - return null; - } - ], ), ], ), ]; } - - factory RegistrationOptions.defaults( - BuildContext context, - RegistrationRepository registrationRepository, - dynamic Function() afterRegistration) => - RegistrationOptions( - nextButtonBuilder: (onPressed, label, step, enabled) => Padding( - padding: step > 0 - ? const EdgeInsets.symmetric(horizontal: 2) - : const EdgeInsets.only(right: 16), - child: InkWell( - onTap: onPressed, - child: Container( - width: 180, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: const Color( - 0xff979797, - ), - ), - ), - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Text( - label, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - ), - ), - previousButtonBuilder: (onPressed, label, step) => step > 0 - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), - child: InkWell( - onTap: onPressed, - child: Container( - width: 180, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: const Color( - 0xff979797, - ), - ), - ), - child: Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Text( - label, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - ), - ) - : const SizedBox.shrink(), - backgroundColor: const Color(0xffFAF9F6), - customAppbarBuilder: (title) => AppBar( - backgroundColor: Colors.transparent, - leading: const SizedBox.shrink(), - ), - registrationRepository: registrationRepository, - registrationSteps: [ - AuthStep( - fields: [ - AuthTextField( - padding: - const EdgeInsets.symmetric(horizontal: 60, vertical: 20), - validators: [ - (email) => (email == null || (email as String).isEmpty) - ? 'Please enter your email address.' - : null, - (email) => - RegExp(r"""(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])""") - .hasMatch(email!) - ? null - : 'Please enter a valid email address.', - ], - title: const Padding( - padding: EdgeInsets.only(top: 150), - child: Text( - 'Enter your e-mail', - style: TextStyle( - color: Color(0xff71C6D1), - fontWeight: FontWeight.w800, - fontSize: 24, - ), - ), - ), - name: 'email', - textFieldDecoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric(horizontal: 8), - labelText: 'Email address', - border: OutlineInputBorder(), - ), - ), - ], - ), - AuthStep( - fields: [ - AuthPassField( - padding: - const EdgeInsets.symmetric(horizontal: 60, vertical: 20), - validators: [ - (value) => (value == null || (value as String).isEmpty) - ? 'Please enter a password.' - : null, - ], - title: const Padding( - padding: EdgeInsets.only(top: 150), - child: Text( - 'Choose a password', - style: TextStyle( - color: Color(0xff71C6D1), - fontWeight: FontWeight.w800, - fontSize: 24, - ), - ), - ), - name: 'password', - textFieldDecoration: const InputDecoration( - contentPadding: EdgeInsets.symmetric(horizontal: 8), - labelText: 'password', - border: OutlineInputBorder(), - ), - ), - ], - ), - ], - afterRegistration: () async => afterRegistration(), - ); +} + +AppBar _createCustomAppBar(String title) { + return AppBar( + title: Text(title), + backgroundColor: const Color(0xffFAF9F6), + ); } diff --git a/lib/src/config/registration_translations.dart b/lib/src/config/registration_translations.dart index 81b6dca..f492670 100644 --- a/lib/src/config/registration_translations.dart +++ b/lib/src/config/registration_translations.dart @@ -15,35 +15,27 @@ class RegistrationTranslations { required this.defaultEmailHint, required this.defaultEmailEmpty, required this.defaultEmailValidatorMessage, - required this.defaultPassword1Title, - required this.defaultPassword1Label, - required this.defaultPassword1Hint, - required this.defaultPassword1ValidatorMessage, - required this.defaultPassword2Title, - required this.defaultPassword2Label, - required this.defaultPassword2Hint, - required this.defaultPassword2ValidatorMessage, + required this.defaultPasswordTitle, + required this.defaultPasswordLabel, + required this.defaultPasswordHint, + required this.defaultPasswordValidatorMessage, }); const RegistrationTranslations.empty() - : title = 'Register', + : title = '', registerBtn = 'Register', previousStepBtn = 'Previous', nextStepBtn = 'Next', closeBtn = 'Close', defaultEmailTitle = 'What is your email?', defaultEmailLabel = '', - defaultEmailHint = 'john.doe@domain.com', - defaultEmailEmpty = 'Enter your email', - defaultEmailValidatorMessage = 'Enter a valid email address', - defaultPassword1Title = 'Enter a password', - defaultPassword1Label = '', - defaultPassword1Hint = '', - defaultPassword1ValidatorMessage = 'Enter a valid password', - defaultPassword2Title = 'Re-enter password', - defaultPassword2Label = '', - defaultPassword2Hint = '', - defaultPassword2ValidatorMessage = 'Passwords have to be equal'; + defaultEmailHint = 'Email address', + defaultEmailEmpty = 'Please enter your email address.', + defaultEmailValidatorMessage = 'Please enter a valid email address.', + defaultPasswordTitle = 'Choose a password', + defaultPasswordLabel = 'password', + defaultPasswordHint = '', + defaultPasswordValidatorMessage = 'Enter a valid password'; final String title; final String registerBtn; @@ -55,14 +47,10 @@ class RegistrationTranslations { final String defaultEmailHint; final String defaultEmailEmpty; final String defaultEmailValidatorMessage; - final String defaultPassword1Title; - final String defaultPassword1Label; - final String defaultPassword1Hint; - final String defaultPassword1ValidatorMessage; - final String defaultPassword2Title; - final String defaultPassword2Label; - final String defaultPassword2Hint; - final String defaultPassword2ValidatorMessage; + final String defaultPasswordTitle; + final String defaultPasswordLabel; + final String defaultPasswordHint; + final String defaultPasswordValidatorMessage; // create a copywith RegistrationTranslations copyWith({ @@ -76,14 +64,10 @@ class RegistrationTranslations { String? defaultEmailHint, String? defaultEmailEmpty, String? defaultEmailValidatorMessage, - String? defaultPassword1Title, - String? defaultPassword1Label, - String? defaultPassword1Hint, - String? defaultPassword1ValidatorMessage, - String? defaultPassword2Title, - String? defaultPassword2Label, - String? defaultPassword2Hint, - String? defaultPassword2ValidatorMessage, + String? defaultPasswordTitle, + String? defaultPasswordLabel, + String? defaultPasswordHint, + String? defaultPasswordValidatorMessage, }) { return RegistrationTranslations( title: title ?? this.title, @@ -97,20 +81,11 @@ class RegistrationTranslations { defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty, defaultEmailValidatorMessage: defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage, - defaultPassword1Title: - defaultPassword1Title ?? this.defaultPassword1Title, - defaultPassword1Label: - defaultPassword1Label ?? this.defaultPassword1Label, - defaultPassword1Hint: defaultPassword1Hint ?? this.defaultPassword1Hint, - defaultPassword1ValidatorMessage: defaultPassword1ValidatorMessage ?? - this.defaultPassword1ValidatorMessage, - defaultPassword2Title: - defaultPassword2Title ?? this.defaultPassword2Title, - defaultPassword2Label: - defaultPassword2Label ?? this.defaultPassword2Label, - defaultPassword2Hint: defaultPassword2Hint ?? this.defaultPassword2Hint, - defaultPassword2ValidatorMessage: defaultPassword2ValidatorMessage ?? - this.defaultPassword2ValidatorMessage, + defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle, + defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel, + defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint, + defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ?? + this.defaultPasswordValidatorMessage, ); } } diff --git a/lib/src/registration_screen.dart b/lib/src/registration_screen.dart index 9dbcee3..4a9622c 100644 --- a/lib/src/registration_screen.dart +++ b/lib/src/registration_screen.dart @@ -35,7 +35,7 @@ class RegistrationScreenState extends State { _isLoading = true; }); - var registered = await widget.registrationOptions.registrationRepository + var registered = await widget.registrationOptions.registrationRepository! .register(values); if (registered == null) { @@ -59,7 +59,7 @@ class RegistrationScreenState extends State { var translations = widget.registrationOptions.registrationTranslations; return AuthScreen( - steps: widget.registrationOptions.registrationSteps, + steps: widget.registrationOptions.steps, customAppBar: widget.registrationOptions.customAppbarBuilder?.call( translations.title, ),