feat: add default styling

This commit is contained in:
mike doornenbal 2024-04-19 10:14:08 +02:00
parent 990259dabc
commit 1be83c7013
5 changed files with 268 additions and 395 deletions

View file

@ -109,6 +109,7 @@ class _AuthScreenState extends State<AuthScreen> {
AppBar get _appBar => AppBar get _appBar =>
widget.customAppBar ?? widget.customAppBar ??
AppBar( AppBar(
backgroundColor: const Color(0xffFAF9F6),
title: Text(widget.appBarTitle), title: Text(widget.appBarTitle),
); );
@ -195,9 +196,11 @@ class _AuthScreenState extends State<AuthScreen> {
), ),
) )
: Scaffold( : Scaffold(
backgroundColor: widget.customBackgroundColor ?? Colors.white, backgroundColor:
widget.customBackgroundColor ?? const Color(0xffFAF9F6),
appBar: _appBar, appBar: _appBar,
body: Form( body: SafeArea(
child: Form(
key: _formKey, key: _formKey,
child: PageView( child: PageView(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
@ -216,7 +219,7 @@ class _AuthScreenState extends State<AuthScreen> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Expanded( Expanded(
flex: widget.beforeTitleFlex ?? 3, flex: widget.beforeTitleFlex ?? 2,
child: Container(), child: Container(),
), ),
widget.titleWidget!, widget.titleWidget!,
@ -267,21 +270,37 @@ class _AuthScreenState extends State<AuthScreen> {
children: [ children: [
if (widget.previousButtonBuilder == null) ...[ if (widget.previousButtonBuilder == null) ...[
if (widget.steps.first != widget.steps[i]) if (widget.steps.first != widget.steps[i])
ElevatedButton(
onPressed: onPrevious,
child: Row(
children: [
const Icon(
Icons.arrow_back,
size: 18,
),
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 4.0), left: 16, bottom: 10, right: 8),
child: child: InkWell(
Text(widget.previousBtnTitle), 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,
),
),
),
),
), ),
],
), ),
), ),
] else if (widget.previousButtonBuilder?.call( ] else if (widget.previousButtonBuilder?.call(
@ -304,27 +323,44 @@ class _AuthScreenState extends State<AuthScreen> {
i, i,
_formValid, _formValid,
) ?? ) ??
ElevatedButton( Padding(
onPressed: !_formValid padding: const EdgeInsets.only(
right: 16, bottom: 10, left: 8),
child: InkWell(
onTap: !_formValid
? null ? null
: () async { : () async {
await onNext(widget.steps[i]); await onNext(widget.steps[i]);
}, },
child: Row( child: Container(
children: [ width: 180,
Text( decoration: BoxDecoration(
widget.steps.last == widget.steps[i] 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.submitBtnTitle
: widget.nextBtnTitle, : widget.nextBtnTitle,
), style: const TextStyle(
const Padding( fontSize: 16,
padding: EdgeInsets.only(left: 4.0), fontWeight: FontWeight.w500,
child: Icon( ),
Icons.arrow_forward, ),
size: 18, ),
), ),
), ),
],
), ),
), ),
], ],
@ -341,6 +377,7 @@ class _AuthScreenState extends State<AuthScreen> {
], ],
), ),
), ),
),
); );
} }
} }

View file

@ -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<String?> register(HashMap values) {
debugPrint('register $values');
return Future.value(null);
}
}

View file

@ -6,12 +6,13 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_registration/flutter_registration.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. /// A set of options for configuring the registration process in a Flutter application.
class RegistrationOptions { class RegistrationOptions {
RegistrationOptions({ RegistrationOptions({
required this.registrationRepository, this.registrationRepository,
required this.registrationSteps, this.registrationSteps,
required this.afterRegistration, required this.afterRegistration,
this.titleFlex, this.titleFlex,
this.formFlex, this.formFlex,
@ -19,20 +20,29 @@ class RegistrationOptions {
this.afterTitleFlex, this.afterTitleFlex,
this.registrationTranslations = const RegistrationTranslations.empty(), this.registrationTranslations = const RegistrationTranslations.empty(),
this.onError, this.onError,
this.customAppbarBuilder, this.customAppbarBuilder = _createCustomAppBar,
this.nextButtonBuilder, this.nextButtonBuilder,
this.previousButtonBuilder, this.previousButtonBuilder,
this.buttonMainAxisAlignment, this.buttonMainAxisAlignment,
this.backgroundColor, this.backgroundColor,
this.titleWidget, this.titleWidget,
this.loginButton, this.loginButton,
}); }) {
if (registrationSteps == null || registrationSteps!.isEmpty) {
steps = RegistrationOptions.getDefaultSteps();
} else {
steps = registrationSteps!;
}
registrationRepository ??= ExampleRegistrationRepository();
}
/// Translations for registration-related messages and prompts. /// Translations for registration-related messages and prompts.
final RegistrationTranslations registrationTranslations; final RegistrationTranslations registrationTranslations;
/// The steps involved in the registration process. /// The steps involved in the registration process.
final List<AuthStep> registrationSteps; final List<AuthStep>? registrationSteps;
List<AuthStep> steps = [];
/// A function that handles errors during registration. /// A function that handles errors during registration.
final int? Function(String error)? onError; final int? Function(String error)? onError;
@ -41,7 +51,7 @@ class RegistrationOptions {
final VoidCallback afterRegistration; final VoidCallback afterRegistration;
/// The repository responsible for registration. /// The repository responsible for registration.
final RegistrationRepository registrationRepository; RegistrationRepository? registrationRepository;
/// A function for customizing the app bar displayed during registration. /// A function for customizing the app bar displayed during registration.
final AppBar Function(String title)? customAppbarBuilder; final AppBar Function(String title)? customAppbarBuilder;
@ -61,10 +71,10 @@ class RegistrationOptions {
final Color? backgroundColor; final Color? backgroundColor;
/// A custom widget for displaying the registration title. /// A custom widget for displaying the registration title.
Widget? titleWidget; final Widget? titleWidget;
/// A custom widget for displaying a login button. /// A custom widget for displaying a login button.
Widget? loginButton; final Widget? loginButton;
/// The number of flex units for the title. /// The number of flex units for the title.
final int? titleFlex; final int? titleFlex;
@ -103,10 +113,8 @@ class RegistrationOptions {
/// [initialEmail] initial value for email input. /// [initialEmail] initial value for email input.
static List<AuthStep> getDefaultSteps({ static List<AuthStep> getDefaultSteps({
TextEditingController? emailController, TextEditingController? emailController,
TextEditingController? pass1Controller, TextEditingController? passController,
bool pass1Hidden = true, bool passHidden = true,
TextEditingController? pass2Controller,
bool pass2Hidden = true,
Function(bool mainPass, bool value)? passHideOnChange, Function(bool mainPass, bool value)? passHideOnChange,
RegistrationTranslations translations = RegistrationTranslations translations =
const RegistrationTranslations.empty(), const RegistrationTranslations.empty(),
@ -115,8 +123,6 @@ class RegistrationOptions {
TextStyle? textStyle, TextStyle? textStyle,
String? initialEmail, String? initialEmail,
}) { }) {
var password1 = '';
return [ return [
AuthStep( AuthStep(
fields: [ fields: [
@ -125,18 +131,25 @@ class RegistrationOptions {
textEditingController: emailController, textEditingController: emailController,
value: initialEmail ?? '', value: initialEmail ?? '',
title: titleBuilder?.call(translations.defaultEmailTitle) ?? title: titleBuilder?.call(translations.defaultEmailTitle) ??
Padding( const Padding(
padding: const EdgeInsets.only(top: 24.0, bottom: 12.0), padding: EdgeInsets.only(top: 180),
child: Text( child: Text(
translations.defaultEmailTitle, 'Enter your e-mail',
style: const TextStyle(fontWeight: FontWeight.bold), style: TextStyle(
color: Color(0xff71C6D1),
fontWeight: FontWeight.w800,
fontSize: 24,
),
), ),
), ),
textFieldDecoration: InputDecoration( textFieldDecoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
label: labelBuilder?.call(translations.defaultEmailLabel), label: labelBuilder?.call(translations.defaultEmailLabel),
hintText: translations.defaultEmailHint, hintText: translations.defaultEmailHint,
border: const OutlineInputBorder(),
), ),
textStyle: textStyle, textStyle: textStyle,
padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 20),
validators: [ validators: [
(email) => (email == null || email.isEmpty) (email) => (email == null || email.isEmpty)
? translations.defaultEmailEmpty ? translations.defaultEmailEmpty
@ -147,212 +160,49 @@ class RegistrationOptions {
? null ? null
: translations.defaultEmailValidatorMessage, : translations.defaultEmailValidatorMessage,
], ],
) ),
], ],
), ),
AuthStep( AuthStep(
fields: [ fields: [
AuthPassField( AuthPassField(
name: 'password1', name: 'password',
textEditingController: pass1Controller, textEditingController: passController,
title: titleBuilder?.call(translations.defaultPassword1Title) ?? title: titleBuilder?.call(translations.defaultPasswordTitle) ??
Padding( Padding(
padding: const EdgeInsets.only(top: 24.0, bottom: 12.0), padding: const EdgeInsets.only(top: 180),
child: Text( child: Text(
translations.defaultPassword1Title, translations.defaultPasswordTitle,
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(
color: Color(0xff71C6D1),
fontWeight: FontWeight.w800,
fontSize: 24,
),
), ),
), ),
textFieldDecoration: InputDecoration( textFieldDecoration: InputDecoration(
label: labelBuilder?.call(translations.defaultPassword1Label), contentPadding: const EdgeInsets.symmetric(horizontal: 8),
hintText: translations.defaultPassword1Hint, label: labelBuilder?.call(translations.defaultPasswordLabel),
hintText: translations.defaultPasswordHint,
border: const OutlineInputBorder(),
), ),
padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 20),
textStyle: textStyle, textStyle: textStyle,
validators: [ validators: [
(value) => (value == null || value.isEmpty) (value) => (value == null || value.isEmpty)
? translations.defaultPassword1ValidatorMessage ? translations.defaultPasswordValidatorMessage
: null, : 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( AppBar _createCustomAppBar(String title) {
BuildContext context, return AppBar(
RegistrationRepository registrationRepository, title: Text(title),
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), 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(),
); );
} }

View file

@ -15,35 +15,27 @@ class RegistrationTranslations {
required this.defaultEmailHint, required this.defaultEmailHint,
required this.defaultEmailEmpty, required this.defaultEmailEmpty,
required this.defaultEmailValidatorMessage, required this.defaultEmailValidatorMessage,
required this.defaultPassword1Title, required this.defaultPasswordTitle,
required this.defaultPassword1Label, required this.defaultPasswordLabel,
required this.defaultPassword1Hint, required this.defaultPasswordHint,
required this.defaultPassword1ValidatorMessage, required this.defaultPasswordValidatorMessage,
required this.defaultPassword2Title,
required this.defaultPassword2Label,
required this.defaultPassword2Hint,
required this.defaultPassword2ValidatorMessage,
}); });
const RegistrationTranslations.empty() const RegistrationTranslations.empty()
: title = 'Register', : title = '',
registerBtn = 'Register', registerBtn = 'Register',
previousStepBtn = 'Previous', previousStepBtn = 'Previous',
nextStepBtn = 'Next', nextStepBtn = 'Next',
closeBtn = 'Close', closeBtn = 'Close',
defaultEmailTitle = 'What is your email?', defaultEmailTitle = 'What is your email?',
defaultEmailLabel = '', defaultEmailLabel = '',
defaultEmailHint = 'john.doe@domain.com', defaultEmailHint = 'Email address',
defaultEmailEmpty = 'Enter your email', defaultEmailEmpty = 'Please enter your email address.',
defaultEmailValidatorMessage = 'Enter a valid email address', defaultEmailValidatorMessage = 'Please enter a valid email address.',
defaultPassword1Title = 'Enter a password', defaultPasswordTitle = 'Choose a password',
defaultPassword1Label = '', defaultPasswordLabel = 'password',
defaultPassword1Hint = '', defaultPasswordHint = '',
defaultPassword1ValidatorMessage = 'Enter a valid password', defaultPasswordValidatorMessage = 'Enter a valid password';
defaultPassword2Title = 'Re-enter password',
defaultPassword2Label = '',
defaultPassword2Hint = '',
defaultPassword2ValidatorMessage = 'Passwords have to be equal';
final String title; final String title;
final String registerBtn; final String registerBtn;
@ -55,14 +47,10 @@ class RegistrationTranslations {
final String defaultEmailHint; final String defaultEmailHint;
final String defaultEmailEmpty; final String defaultEmailEmpty;
final String defaultEmailValidatorMessage; final String defaultEmailValidatorMessage;
final String defaultPassword1Title; final String defaultPasswordTitle;
final String defaultPassword1Label; final String defaultPasswordLabel;
final String defaultPassword1Hint; final String defaultPasswordHint;
final String defaultPassword1ValidatorMessage; final String defaultPasswordValidatorMessage;
final String defaultPassword2Title;
final String defaultPassword2Label;
final String defaultPassword2Hint;
final String defaultPassword2ValidatorMessage;
// create a copywith // create a copywith
RegistrationTranslations copyWith({ RegistrationTranslations copyWith({
@ -76,14 +64,10 @@ class RegistrationTranslations {
String? defaultEmailHint, String? defaultEmailHint,
String? defaultEmailEmpty, String? defaultEmailEmpty,
String? defaultEmailValidatorMessage, String? defaultEmailValidatorMessage,
String? defaultPassword1Title, String? defaultPasswordTitle,
String? defaultPassword1Label, String? defaultPasswordLabel,
String? defaultPassword1Hint, String? defaultPasswordHint,
String? defaultPassword1ValidatorMessage, String? defaultPasswordValidatorMessage,
String? defaultPassword2Title,
String? defaultPassword2Label,
String? defaultPassword2Hint,
String? defaultPassword2ValidatorMessage,
}) { }) {
return RegistrationTranslations( return RegistrationTranslations(
title: title ?? this.title, title: title ?? this.title,
@ -97,20 +81,11 @@ class RegistrationTranslations {
defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty, defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty,
defaultEmailValidatorMessage: defaultEmailValidatorMessage:
defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage, defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage,
defaultPassword1Title: defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle,
defaultPassword1Title ?? this.defaultPassword1Title, defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel,
defaultPassword1Label: defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint,
defaultPassword1Label ?? this.defaultPassword1Label, defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ??
defaultPassword1Hint: defaultPassword1Hint ?? this.defaultPassword1Hint, this.defaultPasswordValidatorMessage,
defaultPassword1ValidatorMessage: defaultPassword1ValidatorMessage ??
this.defaultPassword1ValidatorMessage,
defaultPassword2Title:
defaultPassword2Title ?? this.defaultPassword2Title,
defaultPassword2Label:
defaultPassword2Label ?? this.defaultPassword2Label,
defaultPassword2Hint: defaultPassword2Hint ?? this.defaultPassword2Hint,
defaultPassword2ValidatorMessage: defaultPassword2ValidatorMessage ??
this.defaultPassword2ValidatorMessage,
); );
} }
} }

View file

@ -35,7 +35,7 @@ class RegistrationScreenState extends State<RegistrationScreen> {
_isLoading = true; _isLoading = true;
}); });
var registered = await widget.registrationOptions.registrationRepository var registered = await widget.registrationOptions.registrationRepository!
.register(values); .register(values);
if (registered == null) { if (registered == null) {
@ -59,7 +59,7 @@ class RegistrationScreenState extends State<RegistrationScreen> {
var translations = widget.registrationOptions.registrationTranslations; var translations = widget.registrationOptions.registrationTranslations;
return AuthScreen( return AuthScreen(
steps: widget.registrationOptions.registrationSteps, steps: widget.registrationOptions.steps,
customAppBar: widget.registrationOptions.customAppbarBuilder?.call( customAppBar: widget.registrationOptions.customAppbarBuilder?.call(
translations.title, translations.title,
), ),