mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-18 21:23:43 +02:00
feat: default styling
This commit is contained in:
parent
cf6e30abec
commit
4bad032588
21 changed files with 671 additions and 521 deletions
|
@ -3,7 +3,10 @@ SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
-->
|
-->
|
||||||
|
# 3.0.0
|
||||||
|
- fix: fixed the issue with the scrollController when the `pageToReturnTo` is null.
|
||||||
|
- feat: Added default styling and theme.
|
||||||
|
- feat: Added Iconica linter.
|
||||||
# 2.0.4
|
# 2.0.4
|
||||||
- feat: added maxFormWidth to AuthScreen
|
- feat: added maxFormWidth to AuthScreen
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_iconica_analysis/components_options.yaml
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Possible to overwrite the rules from the package
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
|
|
@ -21,7 +21,7 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlutterRegistrationDemo extends StatefulWidget {
|
class FlutterRegistrationDemo extends StatefulWidget {
|
||||||
const FlutterRegistrationDemo({Key? key}) : super(key: key);
|
const FlutterRegistrationDemo({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FlutterRegistrationDemo> createState() =>
|
State<FlutterRegistrationDemo> createState() =>
|
||||||
|
@ -86,8 +86,7 @@ class _FlutterRegistrationDemoState extends State<FlutterRegistrationDemo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProtectedScreen extends StatelessWidget {
|
class ProtectedScreen extends StatelessWidget {
|
||||||
const ProtectedScreen({Key? key}) : super(key: key);
|
const ProtectedScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const Scaffold(
|
return const Scaffold(
|
||||||
|
|
|
@ -66,10 +66,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_input_library
|
name: flutter_input_library
|
||||||
sha256: "346823caa633d47ad21d8ce92f3c2ed460619fd5447999c35cb171da5e86af06"
|
sha256: db8d9d57c31f166ed7c4ec3d060d18891533c57877a30c6c2668231efa1a44f8
|
||||||
url: "https://forgejo.internal.iconica.nl/api/packages/internal/pub/"
|
url: "https://forgejo.internal.iconica.nl/api/packages/internal/pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.6.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
/// Flutter registration component that provides a registration screen with multiple registration steps.
|
/// Flutter registration component that provides a registration
|
||||||
|
/// screen with multiple registration steps.
|
||||||
library flutter_registration;
|
library flutter_registration;
|
||||||
|
|
||||||
export 'src/config/registration_options.dart';
|
export "package:flutter_input_library/flutter_input_library.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';
|
|
||||||
export 'src/model/auth_text_field.dart';
|
|
||||||
export 'src/model/auth_bool_field.dart';
|
|
||||||
export 'src/model/auth_drop_down.dart';
|
|
||||||
export 'src/model/auth_pass_field.dart';
|
|
||||||
export 'src/registration_screen.dart';
|
|
||||||
export 'src/service/registration_repository.dart';
|
|
||||||
export 'package:flutter_input_library/flutter_input_library.dart'
|
|
||||||
show BoolWidgetType;
|
show BoolWidgetType;
|
||||||
|
|
||||||
|
export "src/config/registration_options.dart";
|
||||||
|
export "src/config/registration_translations.dart";
|
||||||
|
export "src/model/auth_bool_field.dart";
|
||||||
|
export "src/model/auth_drop_down.dart";
|
||||||
|
export "src/model/auth_exception.dart";
|
||||||
|
export "src/model/auth_field.dart";
|
||||||
|
export "src/model/auth_pass_field.dart";
|
||||||
|
export "src/model/auth_step.dart";
|
||||||
|
export "src/model/auth_text_field.dart";
|
||||||
|
export "src/registration_screen.dart";
|
||||||
|
export "src/service/registration_repository.dart";
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'dart:async';
|
import "dart:async";
|
||||||
import 'dart:collection';
|
import "dart:collection";
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
/// A widget for handling multi-step authentication processes.
|
/// A widget for handling multi-step authentication processes.
|
||||||
class AuthScreen extends StatefulWidget {
|
class AuthScreen extends StatefulWidget {
|
||||||
|
@ -13,7 +13,8 @@ class AuthScreen extends StatefulWidget {
|
||||||
///
|
///
|
||||||
/// [appBarTitle] specifies the title of the app bar.
|
/// [appBarTitle] specifies the title of the app bar.
|
||||||
///
|
///
|
||||||
/// [onFinish] is a function called upon completion of the authentication process.
|
/// [onFinish] is a function called upon
|
||||||
|
/// completion of the authentication process.
|
||||||
///
|
///
|
||||||
/// [steps] is a list of authentication steps to be completed.
|
/// [steps] is a list of authentication steps to be completed.
|
||||||
///
|
///
|
||||||
|
@ -33,7 +34,8 @@ class AuthScreen extends StatefulWidget {
|
||||||
///
|
///
|
||||||
/// [previousButtonBuilder] allows customization of the previous button.
|
/// [previousButtonBuilder] allows customization of the previous button.
|
||||||
///
|
///
|
||||||
/// [titleWidget] specifies a custom widget to be displayed at the top of the screen.
|
/// [titleWidget] specifies a custom widget
|
||||||
|
/// to be displayed at the top of the screen.
|
||||||
///
|
///
|
||||||
/// [loginButton] specifies a custom login button widget.
|
/// [loginButton] specifies a custom login button widget.
|
||||||
///
|
///
|
||||||
|
@ -44,8 +46,7 @@ class AuthScreen extends StatefulWidget {
|
||||||
/// [beforeTitleFlex] specifies the flex value before the title widget.
|
/// [beforeTitleFlex] specifies the flex value before the title widget.
|
||||||
///
|
///
|
||||||
/// [afterTitleFlex] specifies the flex value after the title widget.
|
/// [afterTitleFlex] specifies the flex value after the title widget.
|
||||||
///
|
|
||||||
/// [isLoading] indicates whether the screen is in a loading state.
|
|
||||||
const AuthScreen({
|
const AuthScreen({
|
||||||
required this.appBarTitle,
|
required this.appBarTitle,
|
||||||
required this.steps,
|
required this.steps,
|
||||||
|
@ -64,35 +65,72 @@ class AuthScreen extends StatefulWidget {
|
||||||
this.formFlex,
|
this.formFlex,
|
||||||
this.beforeTitleFlex,
|
this.beforeTitleFlex,
|
||||||
this.afterTitleFlex,
|
this.afterTitleFlex,
|
||||||
this.isLoading = false,
|
|
||||||
this.maxFormWidth,
|
this.maxFormWidth,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : assert(steps.length > 0, 'At least one step is required'),
|
}) : assert(steps.length > 0, "At least one step is required");
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
|
/// The title of the app bar.
|
||||||
final String appBarTitle;
|
final String appBarTitle;
|
||||||
|
|
||||||
|
/// A function called upon completion of the authentication process.
|
||||||
final Future<void> Function({
|
final Future<void> Function({
|
||||||
required HashMap<String, dynamic> values,
|
required HashMap<String, dynamic> values,
|
||||||
required void Function(int? pageToReturn) onError,
|
required void Function(int? pageToReturn) onError,
|
||||||
}) onFinish;
|
}) onFinish;
|
||||||
|
|
||||||
|
/// The authentication steps to be completed.
|
||||||
final List<AuthStep> steps;
|
final List<AuthStep> steps;
|
||||||
|
|
||||||
|
/// The title of the submit button.
|
||||||
final String submitBtnTitle;
|
final String submitBtnTitle;
|
||||||
|
|
||||||
|
/// The title of the next button.
|
||||||
final String nextBtnTitle;
|
final String nextBtnTitle;
|
||||||
|
|
||||||
|
/// The title of the previous button.
|
||||||
final String previousBtnTitle;
|
final String previousBtnTitle;
|
||||||
|
|
||||||
|
/// A custom app bar widget.
|
||||||
final AppBar? customAppBar;
|
final AppBar? customAppBar;
|
||||||
|
|
||||||
|
/// The alignment of the buttons.
|
||||||
final MainAxisAlignment? buttonMainAxisAlignment;
|
final MainAxisAlignment? buttonMainAxisAlignment;
|
||||||
|
|
||||||
|
/// The background color of the screen.
|
||||||
final Color? customBackgroundColor;
|
final Color? customBackgroundColor;
|
||||||
final Widget Function(Future<void> Function()? onPressed, String label,
|
|
||||||
int step, bool enabled)? nextButtonBuilder;
|
/// A custom widget for the button.
|
||||||
|
final Widget Function(
|
||||||
|
Future<void> Function()? onPressed,
|
||||||
|
String label,
|
||||||
|
int step,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
bool enabled,
|
||||||
|
)? nextButtonBuilder;
|
||||||
|
|
||||||
|
/// A custom widget for the button.
|
||||||
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
||||||
previousButtonBuilder;
|
previousButtonBuilder;
|
||||||
|
|
||||||
|
/// A custom widget for the title.
|
||||||
final Widget? titleWidget;
|
final Widget? titleWidget;
|
||||||
|
|
||||||
|
/// A custom widget for the login button.
|
||||||
final Widget? loginButton;
|
final Widget? loginButton;
|
||||||
|
|
||||||
|
/// The flex value for the title widget.
|
||||||
final int? titleFlex;
|
final int? titleFlex;
|
||||||
|
|
||||||
|
/// The flex value for the form widget.
|
||||||
final int? formFlex;
|
final int? formFlex;
|
||||||
|
|
||||||
|
/// The flex value before the title widget.
|
||||||
final int? beforeTitleFlex;
|
final int? beforeTitleFlex;
|
||||||
|
|
||||||
|
/// The flex value after the title widget.
|
||||||
final int? afterTitleFlex;
|
final int? afterTitleFlex;
|
||||||
final bool isLoading;
|
|
||||||
|
/// The maximum width of the form.
|
||||||
final double? maxFormWidth;
|
final double? maxFormWidth;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -119,9 +157,11 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
void onPrevious() {
|
void onPrevious() {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
_validate(_pageController.page!.toInt() - 1);
|
_validate(_pageController.page!.toInt() - 1);
|
||||||
_pageController.previousPage(
|
unawaited(
|
||||||
duration: _animationDuration,
|
_pageController.previousPage(
|
||||||
curve: _animationCurve,
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,31 +186,38 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
|
|
||||||
await widget.onFinish(
|
await widget.onFinish(
|
||||||
values: values,
|
values: values,
|
||||||
onError: (int? pageToReturn) => _pageController.animateToPage(
|
onError: (int? pageToReturn) {
|
||||||
pageToReturn ?? 0,
|
if (pageToReturn == null) {
|
||||||
duration: _animationDuration,
|
return;
|
||||||
curve: _animationCurve,
|
}
|
||||||
),
|
_pageController.animateToPage(
|
||||||
|
pageToReturn,
|
||||||
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
_validate(_pageController.page!.toInt() + 1);
|
_validate(_pageController.page!.toInt() + 1);
|
||||||
_pageController.nextPage(
|
unawaited(
|
||||||
duration: _animationDuration,
|
_pageController.nextPage(
|
||||||
curve: _animationCurve,
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates the current step.
|
/// Validates the current step.
|
||||||
void _validate(int currentPage) {
|
void _validate(int currentPage) {
|
||||||
bool isStepValid = true;
|
var isStepValid = true;
|
||||||
|
|
||||||
// Loop through each field in the current step
|
// Loop through each field in the current step
|
||||||
for (var field in widget.steps[currentPage].fields) {
|
for (var field in widget.steps[currentPage].fields) {
|
||||||
for (var validator in field.validators) {
|
for (var validator in field.validators) {
|
||||||
String? validationResult = validator(field.value);
|
var validationResult = validator(field.value);
|
||||||
if (validationResult != null) {
|
if (validationResult != null) {
|
||||||
// If any validator returns an error, mark step as invalid and break
|
// If any validator returns an error, mark step as invalid and break
|
||||||
isStepValid = false;
|
isStepValid = false;
|
||||||
|
@ -189,203 +236,186 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.isLoading
|
var theme = Theme.of(context);
|
||||||
? const Center(
|
return Scaffold(
|
||||||
child: SizedBox(
|
backgroundColor: widget.customBackgroundColor ?? const Color(0xffFAF9F6),
|
||||||
height: 120,
|
appBar: _appBar,
|
||||||
width: 120,
|
body: SafeArea(
|
||||||
child: CircularProgressIndicator(),
|
child: Form(
|
||||||
),
|
key: _formKey,
|
||||||
)
|
child: PageView(
|
||||||
: Scaffold(
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
backgroundColor:
|
controller: _pageController,
|
||||||
widget.customBackgroundColor ?? const Color(0xffFAF9F6),
|
children: <Widget>[
|
||||||
appBar: _appBar,
|
for (var currentStep = 0;
|
||||||
body: SafeArea(
|
currentStep < widget.steps.length;
|
||||||
child: Form(
|
currentStep++)
|
||||||
key: _formKey,
|
Column(
|
||||||
child: PageView(
|
mainAxisSize: MainAxisSize.min,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
controller: _pageController,
|
children: [
|
||||||
children: <Widget>[
|
if (widget.titleWidget != null) ...[
|
||||||
for (var i = 0; i < widget.steps.length; i++)
|
Expanded(
|
||||||
Column(
|
flex: widget.titleFlex ?? 1,
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
if (widget.titleWidget != null) ...[
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: widget.titleFlex ?? 1,
|
flex: widget.beforeTitleFlex ?? 2,
|
||||||
child: Column(
|
child: Container(),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
),
|
||||||
mainAxisSize: MainAxisSize.min,
|
widget.titleWidget!,
|
||||||
children: [
|
Expanded(
|
||||||
Expanded(
|
flex: widget.afterTitleFlex ?? 2,
|
||||||
flex: widget.beforeTitleFlex ?? 2,
|
child: Container(),
|
||||||
child: Container(),
|
|
||||||
),
|
|
||||||
widget.titleWidget!,
|
|
||||||
Expanded(
|
|
||||||
flex: widget.afterTitleFlex ?? 2,
|
|
||||||
child: Container(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
Expanded(
|
),
|
||||||
flex: widget.formFlex ?? 3,
|
),
|
||||||
child: Align(
|
],
|
||||||
alignment: Alignment.topCenter,
|
Expanded(
|
||||||
child: ConstrainedBox(
|
flex: widget.formFlex ?? 3,
|
||||||
constraints: BoxConstraints(
|
child: Align(
|
||||||
maxWidth: widget.maxFormWidth ?? 300,
|
alignment: Alignment.topCenter,
|
||||||
),
|
child: ConstrainedBox(
|
||||||
child: Column(
|
constraints: BoxConstraints(
|
||||||
children: [
|
maxWidth: widget.maxFormWidth ?? 300,
|
||||||
for (AuthField field
|
|
||||||
in widget.steps[i].fields) ...[
|
|
||||||
if (field.title != null) ...[
|
|
||||||
field.title!,
|
|
||||||
],
|
|
||||||
field.build(context, () {
|
|
||||||
_validate(i);
|
|
||||||
})
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
for (AuthField field
|
||||||
mainAxisAlignment: widget
|
in widget.steps[currentStep].fields) ...[
|
||||||
.buttonMainAxisAlignment !=
|
if (field.title != null) ...[
|
||||||
null
|
wrapWithDefaultStyle(
|
||||||
? widget.buttonMainAxisAlignment!
|
style: theme.textTheme.headlineLarge!,
|
||||||
: (widget.previousButtonBuilder != null &&
|
widget: field.title!,
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] 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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
field.build(context, () {
|
||||||
if (widget.loginButton != null)
|
_validate(currentStep);
|
||||||
Padding(
|
}),
|
||||||
padding: const EdgeInsets.only(top: 20.0),
|
],
|
||||||
child: widget.loginButton!,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: widget.steps.first !=
|
||||||
|
widget.steps[currentStep]
|
||||||
|
? MainAxisAlignment.spaceBetween
|
||||||
|
: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
if (widget.steps.first !=
|
||||||
|
widget.steps[currentStep]) ...[
|
||||||
|
widget.previousButtonBuilder?.call(
|
||||||
|
onPrevious,
|
||||||
|
widget.previousBtnTitle,
|
||||||
|
currentStep,
|
||||||
|
) ??
|
||||||
|
_stepButton(
|
||||||
|
buttonText: widget.previousBtnTitle,
|
||||||
|
onTap: () async => onPrevious(),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
widget.nextButtonBuilder?.call(
|
||||||
|
!_formValid
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await onNext(
|
||||||
|
widget.steps[currentStep],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
widget.steps.last ==
|
||||||
|
widget.steps[currentStep]
|
||||||
|
? widget.submitBtnTitle
|
||||||
|
: widget.nextBtnTitle,
|
||||||
|
currentStep,
|
||||||
|
_formValid,
|
||||||
|
) ??
|
||||||
|
_stepButton(
|
||||||
|
buttonText: widget.steps.last ==
|
||||||
|
widget.steps[currentStep]
|
||||||
|
? widget.submitBtnTitle
|
||||||
|
: widget.nextBtnTitle,
|
||||||
|
onTap: () async {
|
||||||
|
await onNext(
|
||||||
|
widget.steps[currentStep],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
if (widget.loginButton != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 20.0),
|
||||||
|
child: widget.loginButton,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _stepButton({
|
||||||
|
required String buttonText,
|
||||||
|
required Future Function()? onTap,
|
||||||
|
}) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
return Flexible(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 180,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(
|
||||||
|
0xff979797,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Text(
|
||||||
|
buttonText,
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget wrapWithDefaultStyle({
|
||||||
|
required Widget widget,
|
||||||
|
required TextStyle style,
|
||||||
|
}) =>
|
||||||
|
DefaultTextStyle(style: style, child: widget);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import 'dart:collection';
|
import "dart:collection";
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
|
/// A registration repository that does nothing.
|
||||||
class ExampleRegistrationRepository with RegistrationRepository {
|
class ExampleRegistrationRepository with RegistrationRepository {
|
||||||
@override
|
@override
|
||||||
Future<String?> register(HashMap values) {
|
Future<String?> register(HashMap values) {
|
||||||
debugPrint('register $values');
|
debugPrint("register $values");
|
||||||
return Future.value(null);
|
return Future.value(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,20 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'dart:async';
|
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';
|
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 {
|
||||||
|
/// Registration options Constructor
|
||||||
RegistrationOptions({
|
RegistrationOptions({
|
||||||
|
required this.afterRegistration,
|
||||||
this.registrationRepository,
|
this.registrationRepository,
|
||||||
this.registrationSteps,
|
this.registrationSteps,
|
||||||
required this.afterRegistration,
|
|
||||||
this.titleFlex,
|
this.titleFlex,
|
||||||
this.formFlex,
|
this.formFlex,
|
||||||
this.beforeTitleFlex,
|
this.beforeTitleFlex,
|
||||||
|
@ -28,6 +30,7 @@ class RegistrationOptions {
|
||||||
this.titleWidget,
|
this.titleWidget,
|
||||||
this.loginButton,
|
this.loginButton,
|
||||||
this.maxFormWidth,
|
this.maxFormWidth,
|
||||||
|
this.beforeRegistration,
|
||||||
}) {
|
}) {
|
||||||
if (registrationSteps == null || registrationSteps!.isEmpty) {
|
if (registrationSteps == null || registrationSteps!.isEmpty) {
|
||||||
steps = RegistrationOptions.getDefaultSteps();
|
steps = RegistrationOptions.getDefaultSteps();
|
||||||
|
@ -43,6 +46,7 @@ class RegistrationOptions {
|
||||||
/// The steps involved in the registration process.
|
/// The steps involved in the registration process.
|
||||||
final List<AuthStep>? registrationSteps;
|
final List<AuthStep>? registrationSteps;
|
||||||
|
|
||||||
|
/// The steps involved in the registration process.
|
||||||
List<AuthStep> steps = [];
|
List<AuthStep> steps = [];
|
||||||
|
|
||||||
/// A function that handles errors during registration.
|
/// A function that handles errors during registration.
|
||||||
|
@ -58,8 +62,13 @@ class RegistrationOptions {
|
||||||
final AppBar Function(String title)? customAppbarBuilder;
|
final AppBar Function(String title)? customAppbarBuilder;
|
||||||
|
|
||||||
/// A function for customizing the "Next" button.
|
/// A function for customizing the "Next" button.
|
||||||
final Widget Function(Future<void> Function()? onPressed, String label,
|
final Widget Function(
|
||||||
int step, bool enabled)? nextButtonBuilder;
|
Future<void> Function()? onPressed,
|
||||||
|
String label,
|
||||||
|
int step,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
bool enabled,
|
||||||
|
)? nextButtonBuilder;
|
||||||
|
|
||||||
/// A function for customizing the "Previous" button.
|
/// A function for customizing the "Previous" button.
|
||||||
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
||||||
|
@ -92,6 +101,9 @@ class RegistrationOptions {
|
||||||
/// The maximum width of the form. Defaults to 300.
|
/// The maximum width of the form. Defaults to 300.
|
||||||
final double? maxFormWidth;
|
final double? maxFormWidth;
|
||||||
|
|
||||||
|
/// This function gets called before the user gets registered.
|
||||||
|
final Future<void> Function()? beforeRegistration;
|
||||||
|
|
||||||
/// Generates default registration steps.
|
/// Generates default registration steps.
|
||||||
///
|
///
|
||||||
/// [emailController] controller for email input.
|
/// [emailController] controller for email input.
|
||||||
|
@ -119,94 +131,98 @@ class RegistrationOptions {
|
||||||
TextEditingController? emailController,
|
TextEditingController? emailController,
|
||||||
TextEditingController? passController,
|
TextEditingController? passController,
|
||||||
bool passHidden = true,
|
bool passHidden = true,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
Function(bool mainPass, bool value)? passHideOnChange,
|
Function(bool mainPass, bool value)? passHideOnChange,
|
||||||
RegistrationTranslations translations =
|
RegistrationTranslations translations =
|
||||||
const RegistrationTranslations.empty(),
|
const RegistrationTranslations.empty(),
|
||||||
Function(String title)? titleBuilder,
|
Function(String title)? titleBuilder,
|
||||||
Function(String label)? labelBuilder,
|
Function(String label)? labelBuilder,
|
||||||
TextStyle? textStyle,
|
TextStyle? textStyle,
|
||||||
|
TextStyle? hintStyle,
|
||||||
String? initialEmail,
|
String? initialEmail,
|
||||||
}) {
|
}) =>
|
||||||
return [
|
[
|
||||||
AuthStep(
|
AuthStep(
|
||||||
fields: [
|
fields: [
|
||||||
AuthTextField(
|
AuthTextField(
|
||||||
name: 'email',
|
name: "email",
|
||||||
textEditingController: emailController,
|
textEditingController: emailController,
|
||||||
value: initialEmail ?? '',
|
value: initialEmail ?? "",
|
||||||
title: titleBuilder?.call(translations.defaultEmailTitle) ??
|
title: titleBuilder?.call(translations.defaultEmailTitle) ??
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 180),
|
padding: const EdgeInsets.only(top: 180),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Enter your e-mail',
|
translations.defaultEmailTitle,
|
||||||
style: TextStyle(
|
|
||||||
color: Color(0xff71C6D1),
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
fontSize: 24,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
textInputType: TextInputType.emailAddress,
|
||||||
textFieldDecoration: InputDecoration(
|
textFieldDecoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
hintStyle: hintStyle,
|
||||||
label: labelBuilder?.call(translations.defaultEmailLabel),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
hintText: translations.defaultEmailHint,
|
label: labelBuilder?.call(translations.defaultEmailLabel),
|
||||||
border: const OutlineInputBorder(),
|
hintText: translations.defaultEmailHint,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
focusedBorder: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
textStyle: textStyle,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
|
validators: [
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
(email) => (email == null || email.isEmpty)
|
||||||
|
? translations.defaultEmailEmpty
|
||||||
|
: 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
|
||||||
|
: translations.defaultEmailValidatorMessage,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
textStyle: textStyle,
|
],
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
),
|
||||||
validators: [
|
AuthStep(
|
||||||
(email) => (email == null || email.isEmpty)
|
fields: [
|
||||||
? translations.defaultEmailEmpty
|
AuthPassField(
|
||||||
: null,
|
name: "password",
|
||||||
(email) =>
|
textEditingController: passController,
|
||||||
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])+)\])""")
|
title: titleBuilder?.call(translations.defaultPasswordTitle) ??
|
||||||
.hasMatch(email!)
|
Padding(
|
||||||
? null
|
padding: const EdgeInsets.only(top: 180),
|
||||||
: translations.defaultEmailValidatorMessage,
|
child: Text(
|
||||||
],
|
translations.defaultPasswordTitle,
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
AuthStep(
|
|
||||||
fields: [
|
|
||||||
AuthPassField(
|
|
||||||
name: 'password',
|
|
||||||
textEditingController: passController,
|
|
||||||
title: titleBuilder?.call(translations.defaultPasswordTitle) ??
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 180),
|
|
||||||
child: Text(
|
|
||||||
translations.defaultPasswordTitle,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Color(0xff71C6D1),
|
|
||||||
fontWeight: FontWeight.w800,
|
|
||||||
fontSize: 24,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
textFieldDecoration: InputDecoration(
|
||||||
textFieldDecoration: InputDecoration(
|
hintStyle: hintStyle,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
label: labelBuilder?.call(translations.defaultPasswordLabel),
|
label: labelBuilder?.call(translations.defaultPasswordLabel),
|
||||||
hintText: translations.defaultPasswordHint,
|
hintText: translations.defaultPasswordHint,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
|
focusedBorder: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
|
textStyle: textStyle,
|
||||||
|
validators: [
|
||||||
|
(value) {
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return translations.defaultPasswordValidatorMessage;
|
||||||
|
}
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
if (value.length < 6) {
|
||||||
|
return translations.defaultPasswordToShortValidatorMessage;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
],
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
],
|
||||||
textStyle: textStyle,
|
),
|
||||||
validators: [
|
];
|
||||||
(value) => (value == null || value.isEmpty)
|
|
||||||
? translations.defaultPasswordValidatorMessage
|
|
||||||
: null,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppBar _createCustomAppBar(String title) {
|
AppBar _createCustomAppBar(String title) => AppBar(
|
||||||
return AppBar(
|
iconTheme: const IconThemeData(color: Colors.black, size: 16),
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
backgroundColor: const Color(0xffFAF9F6),
|
backgroundColor: Colors.transparent,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
/// Holds all the translations for the standard elements on the registration screen.
|
/// Holds all the translations for the standard elements
|
||||||
|
/// on the registration screen.
|
||||||
class RegistrationTranslations {
|
class RegistrationTranslations {
|
||||||
|
/// Constructs a [RegistrationTranslations] object.
|
||||||
const RegistrationTranslations({
|
const RegistrationTranslations({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.registerBtn,
|
required this.registerBtn,
|
||||||
|
@ -19,40 +21,74 @@ class RegistrationTranslations {
|
||||||
required this.defaultPasswordLabel,
|
required this.defaultPasswordLabel,
|
||||||
required this.defaultPasswordHint,
|
required this.defaultPasswordHint,
|
||||||
required this.defaultPasswordValidatorMessage,
|
required this.defaultPasswordValidatorMessage,
|
||||||
|
required this.defaultPasswordToShortValidatorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Constructs a [RegistrationTranslations] object with empty strings.
|
||||||
const RegistrationTranslations.empty()
|
const RegistrationTranslations.empty()
|
||||||
: title = '',
|
: title = "",
|
||||||
registerBtn = 'Register',
|
registerBtn = "Register",
|
||||||
previousStepBtn = 'Previous',
|
previousStepBtn = "Previous",
|
||||||
nextStepBtn = 'Next',
|
nextStepBtn = "Next",
|
||||||
closeBtn = 'Close',
|
closeBtn = "Close",
|
||||||
defaultEmailTitle = 'What is your email?',
|
defaultEmailTitle = "enter your email address",
|
||||||
defaultEmailLabel = '',
|
defaultEmailLabel = "",
|
||||||
defaultEmailHint = 'Email address',
|
defaultEmailHint = "Email address",
|
||||||
defaultEmailEmpty = 'Please enter your email address.',
|
defaultEmailEmpty = "Please enter your email address.",
|
||||||
defaultEmailValidatorMessage = 'Please enter a valid email address.',
|
defaultEmailValidatorMessage = "Please enter a valid email address.",
|
||||||
defaultPasswordTitle = 'Choose a password',
|
defaultPasswordTitle = "choose a password",
|
||||||
defaultPasswordLabel = 'password',
|
defaultPasswordLabel = "",
|
||||||
defaultPasswordHint = '',
|
defaultPasswordHint = "Password",
|
||||||
defaultPasswordValidatorMessage = 'Enter a valid password';
|
defaultPasswordValidatorMessage = "Enter a valid password",
|
||||||
|
defaultPasswordToShortValidatorMessage =
|
||||||
|
"Password needs to be at least 6 characters long";
|
||||||
|
|
||||||
|
/// The title of the registration screen.
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
|
/// The text for the registration button.
|
||||||
final String registerBtn;
|
final String registerBtn;
|
||||||
|
|
||||||
|
/// The text for the previous step button.
|
||||||
final String previousStepBtn;
|
final String previousStepBtn;
|
||||||
|
|
||||||
|
/// The text for the next step button.
|
||||||
final String nextStepBtn;
|
final String nextStepBtn;
|
||||||
|
|
||||||
|
/// The text for the close button.
|
||||||
final String closeBtn;
|
final String closeBtn;
|
||||||
|
|
||||||
|
/// The title for the default email field.
|
||||||
final String defaultEmailTitle;
|
final String defaultEmailTitle;
|
||||||
|
|
||||||
|
/// The label for the default email field.
|
||||||
final String defaultEmailLabel;
|
final String defaultEmailLabel;
|
||||||
|
|
||||||
|
/// The hint for the default email field.
|
||||||
final String defaultEmailHint;
|
final String defaultEmailHint;
|
||||||
|
|
||||||
|
/// The message for an empty default email field.
|
||||||
final String defaultEmailEmpty;
|
final String defaultEmailEmpty;
|
||||||
|
|
||||||
|
/// The message for an invalid default email field.
|
||||||
final String defaultEmailValidatorMessage;
|
final String defaultEmailValidatorMessage;
|
||||||
|
|
||||||
|
/// The title for the default password field.
|
||||||
final String defaultPasswordTitle;
|
final String defaultPasswordTitle;
|
||||||
|
|
||||||
|
/// The label for the default password field.
|
||||||
final String defaultPasswordLabel;
|
final String defaultPasswordLabel;
|
||||||
|
|
||||||
|
/// The hint for the default password field.
|
||||||
final String defaultPasswordHint;
|
final String defaultPasswordHint;
|
||||||
|
|
||||||
|
/// The message for an invalid default password field.
|
||||||
final String defaultPasswordValidatorMessage;
|
final String defaultPasswordValidatorMessage;
|
||||||
|
|
||||||
// create a copywith
|
/// The message for a default password that is too short.
|
||||||
|
final String defaultPasswordToShortValidatorMessage;
|
||||||
|
|
||||||
|
/// create a copywith
|
||||||
RegistrationTranslations copyWith({
|
RegistrationTranslations copyWith({
|
||||||
String? title,
|
String? title,
|
||||||
String? registerBtn,
|
String? registerBtn,
|
||||||
|
@ -68,24 +104,27 @@ class RegistrationTranslations {
|
||||||
String? defaultPasswordLabel,
|
String? defaultPasswordLabel,
|
||||||
String? defaultPasswordHint,
|
String? defaultPasswordHint,
|
||||||
String? defaultPasswordValidatorMessage,
|
String? defaultPasswordValidatorMessage,
|
||||||
}) {
|
String? defaultPasswordToShortValidatorMessage,
|
||||||
return RegistrationTranslations(
|
}) =>
|
||||||
title: title ?? this.title,
|
RegistrationTranslations(
|
||||||
registerBtn: registerBtn ?? this.registerBtn,
|
title: title ?? this.title,
|
||||||
previousStepBtn: previousStepBtn ?? this.previousStepBtn,
|
registerBtn: registerBtn ?? this.registerBtn,
|
||||||
nextStepBtn: nextStepBtn ?? this.nextStepBtn,
|
previousStepBtn: previousStepBtn ?? this.previousStepBtn,
|
||||||
closeBtn: closeBtn ?? this.closeBtn,
|
nextStepBtn: nextStepBtn ?? this.nextStepBtn,
|
||||||
defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle,
|
closeBtn: closeBtn ?? this.closeBtn,
|
||||||
defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel,
|
defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle,
|
||||||
defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint,
|
defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel,
|
||||||
defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty,
|
defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint,
|
||||||
defaultEmailValidatorMessage:
|
defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty,
|
||||||
defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage,
|
defaultEmailValidatorMessage:
|
||||||
defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle,
|
defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage,
|
||||||
defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel,
|
defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle,
|
||||||
defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint,
|
defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel,
|
||||||
defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ??
|
defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint,
|
||||||
this.defaultPasswordValidatorMessage,
|
defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ??
|
||||||
);
|
this.defaultPasswordValidatorMessage,
|
||||||
}
|
defaultPasswordToShortValidatorMessage:
|
||||||
|
defaultPasswordToShortValidatorMessage ??
|
||||||
|
this.defaultPasswordToShortValidatorMessage,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,19 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
|
/// An action that can be performed during authentication.
|
||||||
class AuthAction {
|
class AuthAction {
|
||||||
|
/// Constructs an [AuthAction] object.
|
||||||
AuthAction({
|
AuthAction({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.onPress,
|
required this.onPress,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The title of the action.
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
|
/// A callback function triggered when the action is pressed.
|
||||||
final VoidCallback onPress;
|
final VoidCallback onPress;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_input_library/flutter_input_library.dart';
|
import "package:flutter_input_library/flutter_input_library.dart";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
/// A field for capturing boolean values in a Flutter form.
|
/// A field for capturing boolean values in a Flutter form.
|
||||||
///
|
///
|
||||||
|
@ -22,11 +22,14 @@ class AuthBoolField extends AuthField {
|
||||||
///
|
///
|
||||||
/// [value] specifies the initial value of the field (default is false).
|
/// [value] specifies the initial value of the field (default is false).
|
||||||
///
|
///
|
||||||
/// [leftWidget] is a widget to be displayed on the left side of the boolean widget.
|
/// [leftWidget] is a widget to be displayed on the
|
||||||
|
/// left side of the boolean widget.
|
||||||
///
|
///
|
||||||
/// [rightWidget] is a widget to be displayed on the right side of the boolean widget.
|
/// [rightWidget] is a widget to be displayed on the
|
||||||
|
/// right side of the boolean widget.
|
||||||
///
|
///
|
||||||
/// [onChange] is a callback function triggered when the value of the field changes.
|
/// [onChange] is a callback function triggered when
|
||||||
|
/// the value of the field changes.
|
||||||
AuthBoolField({
|
AuthBoolField({
|
||||||
required super.name,
|
required super.name,
|
||||||
required this.widgetType,
|
required this.widgetType,
|
||||||
|
@ -38,31 +41,38 @@ class AuthBoolField extends AuthField {
|
||||||
this.onChange,
|
this.onChange,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// A widget to be displayed on the left side of the boolean widget.
|
||||||
final Widget? leftWidget;
|
final Widget? leftWidget;
|
||||||
|
|
||||||
|
/// A widget to be displayed on the right side of the boolean widget.
|
||||||
final Widget? rightWidget;
|
final Widget? rightWidget;
|
||||||
|
|
||||||
|
/// The type of boolean widget to use.
|
||||||
final BoolWidgetType widgetType;
|
final BoolWidgetType widgetType;
|
||||||
|
|
||||||
|
/// A callback function triggered when the value of the field changes.
|
||||||
final Function(String value)? onChange;
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, Function onValueChanged) {
|
Widget build(BuildContext context, Function onValueChanged) =>
|
||||||
return FlutterFormInputBool(
|
FlutterFormInputBool(
|
||||||
widgetType: widgetType,
|
widgetType: widgetType,
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
value = v;
|
value = v;
|
||||||
onChange?.call(value);
|
onChange?.call(value);
|
||||||
onValueChanged();
|
// ignore: avoid_dynamic_calls
|
||||||
},
|
onValueChanged();
|
||||||
validator: (value) {
|
},
|
||||||
for (var validator in validators) {
|
validator: (value) {
|
||||||
var output = validator(value);
|
for (var validator in validators) {
|
||||||
if (output != null) {
|
var output = validator(value);
|
||||||
return output;
|
if (output != null) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
},
|
||||||
},
|
leftWidget: leftWidget,
|
||||||
leftWidget: leftWidget,
|
rightWidget: rightWidget,
|
||||||
rightWidget: rightWidget,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
/// A field for capturing dropdown selections in a Flutter form.
|
/// A field for capturing dropdown selections in a Flutter form.
|
||||||
///
|
///
|
||||||
|
@ -10,11 +10,11 @@ class AuthDropdownField extends AuthField {
|
||||||
required super.name,
|
required super.name,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
|
required super.value,
|
||||||
this.dropdownDecoration,
|
this.dropdownDecoration,
|
||||||
this.padding = const EdgeInsets.all(8.0),
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.icon = const Icon(Icons.keyboard_arrow_down),
|
this.icon = const Icon(Icons.keyboard_arrow_down),
|
||||||
required super.value,
|
|
||||||
}) {
|
}) {
|
||||||
selectedValue = value ?? items.first;
|
selectedValue = value ?? items.first;
|
||||||
}
|
}
|
||||||
|
@ -41,37 +41,38 @@ class AuthDropdownField extends AuthField {
|
||||||
final Icon icon;
|
final Icon icon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, Function onValueChanged) {
|
Widget build(BuildContext context, Function onValueChanged) => Padding(
|
||||||
return Padding(
|
padding: padding,
|
||||||
padding: padding,
|
child: DropdownButtonFormField<String>(
|
||||||
child: DropdownButtonFormField<String>(
|
icon: icon,
|
||||||
icon: icon,
|
style: textStyle,
|
||||||
style: textStyle,
|
value: selectedValue,
|
||||||
value: selectedValue,
|
decoration: dropdownDecoration,
|
||||||
decoration: dropdownDecoration,
|
items: items
|
||||||
items: items.map((String value) {
|
.map(
|
||||||
return DropdownMenuItem<String>(
|
(String value) => DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
child: Text(value),
|
child: Text(value),
|
||||||
);
|
),
|
||||||
}).toList(),
|
)
|
||||||
onChanged: (newValue) {
|
.toList(),
|
||||||
selectedValue = newValue;
|
onChanged: (newValue) {
|
||||||
onChanged(newValue);
|
selectedValue = newValue;
|
||||||
onValueChanged();
|
onChanged(newValue);
|
||||||
},
|
// ignore: avoid_dynamic_calls
|
||||||
validator: (value) {
|
onValueChanged();
|
||||||
if (validators.isNotEmpty) {
|
},
|
||||||
for (var validator in validators) {
|
validator: (value) {
|
||||||
var output = validator(value);
|
if (validators.isNotEmpty) {
|
||||||
if (output != null) {
|
for (var validator in validators) {
|
||||||
return output;
|
var output = validator(value);
|
||||||
|
if (output != null) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
},
|
||||||
},
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
/// An exception thrown when an authentication error occurs.
|
||||||
class AuthException implements Exception {
|
class AuthException implements Exception {
|
||||||
|
/// Constructs an [AuthException] object.
|
||||||
AuthException(this.message);
|
AuthException(this.message);
|
||||||
|
|
||||||
|
/// The error message.
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => message;
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
/// An abstract class representing a field in a Flutter form.
|
/// An abstract class representing a field in a Flutter form.
|
||||||
///
|
///
|
||||||
|
@ -14,11 +14,13 @@ abstract class AuthField<T> {
|
||||||
///
|
///
|
||||||
/// [value] specifies the initial value of the field.
|
/// [value] specifies the initial value of the field.
|
||||||
///
|
///
|
||||||
/// [onValueChanged] is a callback function triggered when the value of the field changes (optional).
|
/// [onValueChanged] is a callback function triggered when the
|
||||||
|
/// value of the field changes (optional).
|
||||||
///
|
///
|
||||||
/// [title] specifies the title widget of the field (optional).
|
/// [title] specifies the title widget of the field (optional).
|
||||||
///
|
///
|
||||||
/// [validators] defines a list of validation functions for the field (optional).
|
/// [validators] defines a list of validation
|
||||||
|
/// functions for the field (optional).
|
||||||
AuthField({
|
AuthField({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.value,
|
required this.value,
|
||||||
|
@ -46,6 +48,7 @@ abstract class AuthField<T> {
|
||||||
///
|
///
|
||||||
/// [context] The build context.
|
/// [context] The build context.
|
||||||
///
|
///
|
||||||
/// [onValueChanged] A function to be called when the value of the field changes.
|
/// [onValueChanged] A function to be called when
|
||||||
|
/// the value of the field changes.
|
||||||
Widget build(BuildContext context, Function onValueChanged);
|
Widget build(BuildContext context, Function onValueChanged);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_input_library/flutter_input_library.dart';
|
import "package:flutter_input_library/flutter_input_library.dart";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
/// A field for capturing password inputs in a Flutter form.
|
/// A field for capturing password inputs in a Flutter form.
|
||||||
///
|
///
|
||||||
|
@ -18,25 +18,31 @@ class AuthPassField extends AuthField {
|
||||||
///
|
///
|
||||||
/// [title] specifies the title widget of the field (optional).
|
/// [title] specifies the title widget of the field (optional).
|
||||||
///
|
///
|
||||||
/// [validators] defines a list of validation functions for the field (optional).
|
/// [validators] defines a list of validation
|
||||||
|
/// functions for the field (optional).
|
||||||
///
|
///
|
||||||
/// [value] specifies the initial value of the field (default is an empty string).
|
/// [value] specifies the initial value of the
|
||||||
|
/// field (default is an empty string).
|
||||||
///
|
///
|
||||||
/// [textStyle] defines the text style for the password input.
|
/// [textStyle] defines the text style for the password input.
|
||||||
///
|
///
|
||||||
/// [onChange] is a callback function triggered when the value of the field changes.
|
/// [onChange] is a callback function triggered when
|
||||||
|
/// the value of the field changes.
|
||||||
///
|
///
|
||||||
/// [iconSize] specifies the size of the icon displayed with the password input (optional).
|
/// [iconSize] specifies the size of the icon displayed
|
||||||
|
/// with the password input (optional).
|
||||||
///
|
///
|
||||||
/// [textFieldDecoration] defines the decoration for the password input field (optional).
|
/// [textFieldDecoration] defines the decoration for the
|
||||||
|
/// password input field (optional).
|
||||||
///
|
///
|
||||||
/// [padding] defines the padding around the password input field (default is EdgeInsets.all(8.0)).
|
/// [padding] defines the padding around the password input
|
||||||
|
/// field (default is EdgeInsets.all(8.0)).
|
||||||
AuthPassField({
|
AuthPassField({
|
||||||
required super.name,
|
required super.name,
|
||||||
TextEditingController? textEditingController,
|
this.textEditingController,
|
||||||
super.title,
|
super.title,
|
||||||
super.validators = const [],
|
super.validators = const [],
|
||||||
super.value = '',
|
super.value = "",
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.onChange,
|
this.onChange,
|
||||||
this.iconSize,
|
this.iconSize,
|
||||||
|
@ -44,36 +50,47 @@ class AuthPassField extends AuthField {
|
||||||
this.padding = const EdgeInsets.all(8.0),
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The text style for the password input.
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// The size of the icon displayed with the password input.
|
||||||
final double? iconSize;
|
final double? iconSize;
|
||||||
|
|
||||||
|
/// A callback function triggered when the value of the field changes.
|
||||||
final Function(String value)? onChange;
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
|
/// The decoration for the password input field.
|
||||||
final InputDecoration? textFieldDecoration;
|
final InputDecoration? textFieldDecoration;
|
||||||
|
|
||||||
|
/// The padding around the password input field.
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@override
|
/// The controller for the password input.
|
||||||
Widget build(BuildContext context, Function onValueChanged) {
|
final TextEditingController? textEditingController;
|
||||||
return Padding(
|
|
||||||
padding: padding,
|
|
||||||
child: FlutterFormInputPassword(
|
|
||||||
style: textStyle,
|
|
||||||
iconSize: iconSize ?? 24.0,
|
|
||||||
decoration: textFieldDecoration,
|
|
||||||
onChanged: (v) {
|
|
||||||
value = v;
|
|
||||||
onChange?.call(value);
|
|
||||||
onValueChanged();
|
|
||||||
},
|
|
||||||
validator: (value) {
|
|
||||||
for (var validator in validators) {
|
|
||||||
var output = validator(value);
|
|
||||||
if (output != null) {
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
@override
|
||||||
},
|
Widget build(BuildContext context, Function onValueChanged) => Padding(
|
||||||
),
|
padding: padding,
|
||||||
);
|
child: FlutterFormInputPassword(
|
||||||
}
|
style: textStyle,
|
||||||
|
iconSize: iconSize ?? 24.0,
|
||||||
|
decoration: textFieldDecoration,
|
||||||
|
onChanged: (v) {
|
||||||
|
value = v;
|
||||||
|
onChange?.call(value);
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
onValueChanged();
|
||||||
|
},
|
||||||
|
validator: (value) {
|
||||||
|
for (var validator in validators) {
|
||||||
|
var output = validator(value);
|
||||||
|
if (output != null) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,15 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter_registration/src/model/auth_field.dart';
|
import "package:flutter_registration/src/model/auth_field.dart";
|
||||||
|
|
||||||
|
/// A step in the authentication process.
|
||||||
class AuthStep {
|
class AuthStep {
|
||||||
|
/// Constructs an [AuthStep] object.
|
||||||
AuthStep({
|
AuthStep({
|
||||||
required this.fields,
|
required this.fields,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The fields in the step.
|
||||||
List<AuthField> fields;
|
List<AuthField> fields;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
/// A field for capturing text inputs in a Flutter form.
|
/// A field for capturing text inputs in a Flutter form.
|
||||||
///
|
///
|
||||||
|
@ -17,62 +17,80 @@ class AuthTextField extends AuthField {
|
||||||
///
|
///
|
||||||
/// [title] specifies the title widget of the field (optional).
|
/// [title] specifies the title widget of the field (optional).
|
||||||
///
|
///
|
||||||
/// [validators] defines a list of validation functions for the field (optional).
|
/// [validators] defines a list of validation
|
||||||
|
/// functions for the field (optional).
|
||||||
///
|
///
|
||||||
/// [value] specifies the initial value of the field (default is an empty string).
|
/// [value] specifies the initial value of the
|
||||||
|
/// field (default is an empty string).
|
||||||
///
|
///
|
||||||
/// [textStyle] defines the text style for the text input.
|
/// [textStyle] defines the text style for the text input.
|
||||||
///
|
///
|
||||||
/// [onChange] is a callback function triggered when the value of the field changes.
|
/// [onChange] is a callback function triggered
|
||||||
|
/// when the value of the field changes.
|
||||||
///
|
///
|
||||||
/// [textFieldDecoration] defines the decoration for the text input field (optional).
|
/// [textFieldDecoration] defines the decoration
|
||||||
|
/// for the text input field (optional).
|
||||||
///
|
///
|
||||||
/// [padding] defines the padding around the text input field (default is EdgeInsets.all(8.0)).
|
/// [padding] defines the padding around the text
|
||||||
|
/// input field (default is EdgeInsets.all(8.0)).
|
||||||
AuthTextField({
|
AuthTextField({
|
||||||
required super.name,
|
required super.name,
|
||||||
TextEditingController? textEditingController,
|
TextEditingController? textEditingController,
|
||||||
super.title,
|
super.title,
|
||||||
super.validators = const [],
|
super.validators = const [],
|
||||||
super.value = '',
|
super.value = "",
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.onChange,
|
this.onChange,
|
||||||
this.textFieldDecoration,
|
this.textFieldDecoration,
|
||||||
this.padding = const EdgeInsets.all(8.0),
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
|
this.textInputType,
|
||||||
}) {
|
}) {
|
||||||
textController =
|
textController =
|
||||||
textEditingController ?? TextEditingController(text: value);
|
textEditingController ?? TextEditingController(text: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The controller for the text input.
|
||||||
late TextEditingController textController;
|
late TextEditingController textController;
|
||||||
|
|
||||||
|
/// The text style for the text input.
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// A callback function triggered when the value of the field changes.
|
||||||
final Function(String value)? onChange;
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
|
/// The decoration for the text input field.
|
||||||
final InputDecoration? textFieldDecoration;
|
final InputDecoration? textFieldDecoration;
|
||||||
|
|
||||||
|
/// The padding around the text input field.
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@override
|
/// The type of text input.
|
||||||
Widget build(BuildContext context, Function onValueChanged) {
|
final TextInputType? textInputType;
|
||||||
return Padding(
|
|
||||||
padding: padding,
|
|
||||||
child: TextFormField(
|
|
||||||
style: textStyle,
|
|
||||||
decoration: textFieldDecoration,
|
|
||||||
controller: textController,
|
|
||||||
onChanged: (v) {
|
|
||||||
value = v;
|
|
||||||
onChange?.call(value);
|
|
||||||
onValueChanged();
|
|
||||||
},
|
|
||||||
validator: (value) {
|
|
||||||
for (var validator in validators) {
|
|
||||||
var output = validator(value);
|
|
||||||
if (output != null) {
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
@override
|
||||||
},
|
Widget build(BuildContext context, Function onValueChanged) => Padding(
|
||||||
),
|
padding: padding,
|
||||||
);
|
child: TextFormField(
|
||||||
}
|
keyboardType: textInputType,
|
||||||
|
style: textStyle,
|
||||||
|
decoration: textFieldDecoration,
|
||||||
|
controller: textController,
|
||||||
|
onChanged: (v) {
|
||||||
|
value = v;
|
||||||
|
onChange?.call(value);
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
onValueChanged();
|
||||||
|
},
|
||||||
|
validator: (value) {
|
||||||
|
for (var validator in validators) {
|
||||||
|
var output = validator(value);
|
||||||
|
if (output != null) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import 'dart:collection';
|
import "dart:collection";
|
||||||
|
|
||||||
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/auth_screen.dart';
|
import "package:flutter_registration/src/auth_screen.dart";
|
||||||
|
|
||||||
/// A screen for user registration.
|
/// A screen for user registration.
|
||||||
class RegistrationScreen extends StatefulWidget {
|
class RegistrationScreen extends StatefulWidget {
|
||||||
|
@ -11,8 +11,8 @@ class RegistrationScreen extends StatefulWidget {
|
||||||
/// [registrationOptions] specifies the registration options.
|
/// [registrationOptions] specifies the registration options.
|
||||||
const RegistrationScreen({
|
const RegistrationScreen({
|
||||||
required this.registrationOptions,
|
required this.registrationOptions,
|
||||||
Key? key,
|
super.key,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
/// The registration options.
|
/// The registration options.
|
||||||
final RegistrationOptions registrationOptions;
|
final RegistrationOptions registrationOptions;
|
||||||
|
@ -23,17 +23,13 @@ class RegistrationScreen extends StatefulWidget {
|
||||||
|
|
||||||
/// The state for [RegistrationScreen].
|
/// The state for [RegistrationScreen].
|
||||||
class RegistrationScreenState extends State<RegistrationScreen> {
|
class RegistrationScreenState extends State<RegistrationScreen> {
|
||||||
bool _isLoading = false;
|
|
||||||
|
|
||||||
/// Registers the user.
|
/// Registers the user.
|
||||||
Future<void> _register({
|
Future<void> _register({
|
||||||
required HashMap<String, dynamic> values,
|
required HashMap<String, dynamic> values,
|
||||||
required void Function(int? pageToReturn) onError,
|
required void Function(int? pageToReturn) onError,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
setState(() {
|
await widget.registrationOptions.beforeRegistration?.call();
|
||||||
_isLoading = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
var registered = await widget.registrationOptions.registrationRepository!
|
var registered = await widget.registrationOptions.registrationRepository!
|
||||||
.register(values);
|
.register(values);
|
||||||
|
@ -45,12 +41,8 @@ class RegistrationScreenState extends State<RegistrationScreen> {
|
||||||
|
|
||||||
onError(pageToReturn);
|
onError(pageToReturn);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} on Exception catch (_) {
|
||||||
onError(0);
|
onError(null);
|
||||||
} finally {
|
|
||||||
setState(() {
|
|
||||||
_isLoading = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +67,6 @@ class RegistrationScreenState extends State<RegistrationScreen> {
|
||||||
customBackgroundColor: widget.registrationOptions.backgroundColor,
|
customBackgroundColor: widget.registrationOptions.backgroundColor,
|
||||||
titleWidget: widget.registrationOptions.titleWidget,
|
titleWidget: widget.registrationOptions.titleWidget,
|
||||||
loginButton: widget.registrationOptions.loginButton,
|
loginButton: widget.registrationOptions.loginButton,
|
||||||
isLoading: _isLoading,
|
|
||||||
titleFlex: widget.registrationOptions.titleFlex,
|
titleFlex: widget.registrationOptions.titleFlex,
|
||||||
formFlex: widget.registrationOptions.formFlex,
|
formFlex: widget.registrationOptions.formFlex,
|
||||||
beforeTitleFlex: widget.registrationOptions.beforeTitleFlex,
|
beforeTitleFlex: widget.registrationOptions.beforeTitleFlex,
|
||||||
|
|
|
@ -2,8 +2,10 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'dart:collection';
|
import "dart:collection";
|
||||||
|
|
||||||
|
/// A mixin for a registration repository.
|
||||||
mixin RegistrationRepository {
|
mixin RegistrationRepository {
|
||||||
|
/// Registers a user with the given values.
|
||||||
Future<String?> register(HashMap values);
|
Future<String?> register(HashMap values);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
name: flutter_registration
|
name: flutter_registration
|
||||||
description: A Flutter Registration package
|
description: A Flutter Registration package
|
||||||
version: 2.0.4
|
version: 3.0.0
|
||||||
repository: https://github.com/Iconica-Development/flutter_registration
|
repository: https://github.com/Iconica-Development/flutter_registration
|
||||||
|
|
||||||
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
|
@ -16,7 +16,7 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter_input_library:
|
flutter_input_library:
|
||||||
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
version: ">=3.1.0 <4.0.0"
|
version: ^3.6.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
@ -26,6 +26,9 @@ dependencies:
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^2.0.0
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 7.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import "package:flutter_test/flutter_test.dart";
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test('test', () {
|
test("test", () {
|
||||||
expect(true, true);
|
expect(true, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue