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 =>
widget.customAppBar ??
AppBar(
backgroundColor: const Color(0xffFAF9F6),
title: Text(widget.appBarTitle),
);
@ -195,150 +196,186 @@ class _AuthScreenState extends State<AuthScreen> {
),
)
: 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: <Widget>[
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: <Widget>[
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!,
),
],
),
],
),
],
),
),
),
);

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_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<AuthStep> registrationSteps;
final List<AuthStep>? registrationSteps;
List<AuthStep> 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<AuthStep> 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),
);
}

View file

@ -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,
);
}
}

View file

@ -35,7 +35,7 @@ class RegistrationScreenState extends State<RegistrationScreen> {
_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<RegistrationScreen> {
var translations = widget.registrationOptions.registrationTranslations;
return AuthScreen(
steps: widget.registrationOptions.registrationSteps,
steps: widget.registrationOptions.steps,
customAppBar: widget.registrationOptions.customAppbarBuilder?.call(
translations.title,
),