mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-18 21:23:43 +02:00
feat: registration options, translations
This commit is contained in:
parent
7f36ab8eca
commit
6f7ec0b0e6
6 changed files with 216 additions and 162 deletions
|
@ -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!');
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
51
lib/src/config/registration_options.dart
Normal file
51
lib/src/config/registration_options.dart
Normal 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,
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
15
lib/src/config/registration_translations.dart
Normal file
15
lib/src/config/registration_translations.dart
Normal 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;
|
||||
}
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue