feat: registration options, translations

This commit is contained in:
Stein Milder 2022-09-26 10:35:53 +02:00
parent 7f36ab8eca
commit 6f7ec0b0e6
6 changed files with 216 additions and 162 deletions

View file

@ -16,10 +16,13 @@ class FlutterRegistrationDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RegistrationScreen(
afterRegistration: () {
debugPrint('Registered!');
},
repository: ExampleRegistrationRepository(),
registrationOptions: RegistrationOptions(
registrationRepository: ExampleRegistrationRepository(),
registrationSteps: RegistrationOptions.defaultSteps,
afterRegistration: () {
debugPrint('Registered!');
},
),
);
}
}

View file

@ -1,5 +1,7 @@
library flutter_registration;
export 'src/config/registration_options.dart';
export 'src/config/registration_translations.dart';
export 'src/model/auth_exception.dart';
export 'src/model/auth_field.dart';
export 'src/model/auth_step.dart';

View file

@ -8,6 +8,8 @@ class AuthScreen extends StatefulWidget {
required this.title,
required this.steps,
required this.submitBtnTitle,
required this.nextBtnTitle,
required this.previousBtnTitle,
required this.onFinish,
super.key,
}) : assert(steps.length > 0, 'At least one step is required');
@ -16,6 +18,8 @@ class AuthScreen extends StatefulWidget {
final Function(HashMap<String, String>) onFinish;
final List<AuthStep> steps;
final String submitBtnTitle;
final String nextBtnTitle;
final String previousBtnTitle;
@override
State<AuthScreen> createState() => _AuthScreenState();
@ -28,128 +32,136 @@ class _AuthScreenState extends State<AuthScreen> {
final _animationCurve = Curves.ease;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _formKey,
child: PageView(
physics: const NeverScrollableScrollPhysics(),
controller: _pageController,
children: <Widget>[
for (AuthStep step in widget.steps)
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 30.0,
Widget build(BuildContext context) => Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _formKey,
child: PageView(
physics: const NeverScrollableScrollPhysics(),
controller: _pageController,
children: <Widget>[
for (AuthStep step in widget.steps)
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Center(
child: ListView(
physics: const ClampingScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 30.0,
),
children: [
for (AuthField field in step.fields)
Align(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
top: 24.0,
bottom: 12.0,
),
child: Text(
field.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
field.build(),
],
),
)
],
),
),
),
child: Column(
children: [
for (AuthField field in step.fields)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(
top: 24.0,
bottom: 12.0,
),
child: Text(
field.title,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
Padding(
padding: const EdgeInsets.only(
top: 15.0,
bottom: 30.0,
left: 30.0,
right: 30.0,
),
child: Row(
mainAxisAlignment: widget.steps.first != step
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.end,
children: [
if (widget.steps.first != step)
ElevatedButton(
onPressed: () => _pageController.previousPage(
duration: _animationDuration,
curve: _animationCurve,
),
child: Row(
children: [
const Icon(
Icons.arrow_back,
size: 18,
),
Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Text(widget.previousBtnTitle),
),
],
),
field.build(),
],
)
],
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.all(30.0),
child: Row(
mainAxisAlignment: widget.steps.first != step
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.end,
children: [
if (widget.steps.first != step)
ElevatedButton(
onPressed: () => _pageController.previousPage(
duration: _animationDuration,
curve: _animationCurve,
),
ElevatedButton(
onPressed: () {
if (!_formKey.currentState!.validate()) {
return;
}
if (widget.steps.last == step) {
var values = HashMap<String, String>();
for (var step in widget.steps) {
for (var field in step.fields) {
values[field.name] = field.value;
}
}
widget.onFinish(values);
return;
}
_pageController.nextPage(
duration: _animationDuration,
curve: _animationCurve,
);
},
child: Row(
children: [
const Icon(
Icons.arrow_back,
size: 18,
Text(
widget.steps.last == step
? widget.submitBtnTitle
: widget.nextBtnTitle,
),
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Text('Vorige stap'),
child: Icon(
Icons.arrow_forward,
size: 18,
),
),
],
),
),
ElevatedButton(
onPressed: () {
if (!_formKey.currentState!.validate()) {
return;
}
if (widget.steps.last == step) {
var values = HashMap<String, String>();
for (var step in widget.steps) {
for (var field in step.fields) {
values[field.name] = field.value;
}
}
widget.onFinish(values);
return;
}
_pageController.nextPage(
duration: _animationDuration,
curve: _animationCurve,
);
},
child: Row(
children: [
Text(
widget.steps.last == step
? widget.submitBtnTitle
: 'Volgende stap',
),
const Padding(
padding: EdgeInsets.only(left: 4.0),
child: Icon(
Icons.arrow_forward,
size: 18,
),
),
],
),
),
],
),
)
],
),
],
],
),
)
],
),
],
),
),
),
);
}
);
}

View file

@ -0,0 +1,51 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_registration/flutter_registration.dart';
class RegistrationOptions {
RegistrationOptions({
required this.registrationRepository,
required this.registrationSteps,
required this.afterRegistration,
this.registrationTranslations = const RegistrationTranslations(),
});
final RegistrationTranslations registrationTranslations;
final List<AuthStep> registrationSteps;
final VoidCallback afterRegistration;
final RegistrationRepository registrationRepository;
static List<AuthStep> get defaultSteps => [
AuthStep(
fields: [
AuthTextField(
name: 'email',
title: 'Wat is je e-mailadres?',
validators: [
(email) => (email == null || email.isEmpty)
? 'Geef uw e-mailadres op'
: null,
(email) =>
RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(email!)
? null
: 'Geef een geldig e-mailadres op',
],
)
],
),
AuthStep(
fields: [
AuthTextField(
name: 'password',
title: 'Kies een wachtwoord',
obscureText: true,
validators: [
(value) => (value == null || value.isEmpty)
? 'Geef een wachtwoord op'
: null,
],
),
],
),
];
}

View file

@ -0,0 +1,15 @@
class RegistrationTranslations {
const RegistrationTranslations({
this.title = 'Registreren',
this.registerBtn = 'Registreren',
this.previousStepBtn = 'Vorige stap',
this.nextStepBtn = 'Volgende stap',
this.closeBtn = 'Sluiten',
});
final String title;
final String registerBtn;
final String previousStepBtn;
final String nextStepBtn;
final String closeBtn;
}

View file

@ -4,35 +4,38 @@ import 'package:flutter_registration/src/auth_screen.dart';
class RegistrationScreen extends StatelessWidget {
const RegistrationScreen({
required this.afterRegistration,
required this.repository,
this.additionalSteps = const [],
required this.registrationOptions,
super.key,
});
final VoidCallback afterRegistration;
final RegistrationRepository repository;
final List<AuthStep> additionalSteps;
final RegistrationOptions registrationOptions;
@override
Widget build(BuildContext context) {
var translations = registrationOptions.registrationTranslations;
void showError(String error) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
content: Text(error),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Sluit'),
child: const Text('Sluit'),
onPressed: () => Navigator.pop(
context,
translations.closeBtn,
),
child: Text(
translations.closeBtn,
),
),
],
),
);
void register(values) => repository
void register(values) => registrationOptions.registrationRepository
.register(values)
.then(
(value) => afterRegistration(),
(_) => registrationOptions.afterRegistration(),
)
.catchError(
(error) {
@ -43,44 +46,12 @@ class RegistrationScreen extends StatelessWidget {
);
return AuthScreen(
title: 'Registreren',
submitBtnTitle: 'Registreren',
steps: [
AuthStep(
fields: [
AuthTextField(
name: 'email',
title: 'Wat is je e-mailadres?',
validators: [
(email) => (email == null || email.isEmpty)
? 'Geef uw e-mailadres op'
: null,
(email) =>
RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+")
.hasMatch(email!)
? null
: 'Geef een geldig e-mailadres op',
],
)
],
),
AuthStep(
fields: [
AuthTextField(
name: 'password',
title: 'Kies een wachtwoord',
obscureText: true,
validators: [
(value) => (value == null || value.isEmpty)
? 'Geef een wachtwoord op'
: null,
],
),
],
),
...additionalSteps
],
steps: registrationOptions.registrationSteps,
onFinish: register,
title: translations.title,
submitBtnTitle: translations.registerBtn,
nextBtnTitle: translations.nextStepBtn,
previousBtnTitle: translations.previousStepBtn,
);
}
}