mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-19 13:23:45 +02:00
Compare commits
20 commits
Author | SHA1 | Date | |
---|---|---|---|
|
ee29893bd9 | ||
|
4bad032588 | ||
|
cf6e30abec | ||
|
ce80a96958 | ||
|
71259c7f78 | ||
|
d82df68989 | ||
|
c91a1c0856 | ||
|
444b11a44f | ||
|
1be83c7013 | ||
|
990259dabc | ||
|
d849a1834a | ||
|
05b143a5bd | ||
|
a44a419bc4 | ||
|
7890c17587 | ||
|
3ec92aa68c | ||
|
7a4bcd8b3f | ||
|
3884ef08b1 | ||
|
dcea299aa6 | ||
|
0f28c7084e | ||
|
44871bf44e |
22 changed files with 1018 additions and 573 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -35,3 +35,7 @@ build/
|
||||||
.flutter-plugins
|
.flutter-plugins
|
||||||
.flutter-plugins-dependencies
|
.flutter-plugins-dependencies
|
||||||
.metadata
|
.metadata
|
||||||
|
|
||||||
|
# FVM Version Cache
|
||||||
|
.fvm/
|
||||||
|
.fvmrc
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -3,6 +3,23 @@ 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
|
||||||
|
- feat: added maxFormWidth to AuthScreen
|
||||||
|
|
||||||
|
# 2.0.3
|
||||||
|
- feat: added default registrationOptions
|
||||||
|
|
||||||
|
# 2.0.2
|
||||||
|
- fix: fixed the issue with values not being saved when calling nextStep.
|
||||||
|
|
||||||
|
# 2.0.1
|
||||||
|
- feat: added circular progress indicator while awaiting registration of user
|
||||||
|
- feat: added alignment option for buttons
|
||||||
|
|
||||||
# 2.0.0
|
# 2.0.0
|
||||||
- feat(buttons): Added the possiblity to only have a next button by return zero on the previous button builder
|
- feat(buttons): Added the possiblity to only have a next button by return zero on the previous button builder
|
||||||
- feat: exposed input decoration in AuthTextField
|
- feat: exposed input decoration in AuthTextField
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -65,12 +65,11 @@ packages:
|
||||||
flutter_input_library:
|
flutter_input_library:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: "."
|
name: flutter_input_library
|
||||||
ref: "3.0.0"
|
sha256: db8d9d57c31f166ed7c4ec3d060d18891533c57877a30c6c2668231efa1a44f8
|
||||||
resolved-ref: "7d1880b8e348435fc8dc2d3a11f936b224cbd5b7"
|
url: "https://forgejo.internal.iconica.nl/api/packages/internal/pub/"
|
||||||
url: "https://github.com/Iconica-Development/flutter_input_library"
|
source: hosted
|
||||||
source: git
|
version: "3.6.0"
|
||||||
version: "3.0.0"
|
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
|
@ -90,7 +89,7 @@ packages:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "1.2.0"
|
version: "2.0.4"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -100,10 +99,34 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.1"
|
version: "0.19.0"
|
||||||
|
leak_tracker:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker
|
||||||
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "10.0.4"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.3"
|
||||||
|
leak_tracker_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_testing
|
||||||
|
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -116,34 +139,34 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.16"
|
version: "0.12.16+1"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.0"
|
version: "0.8.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.12.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.3"
|
version: "1.9.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -193,10 +216,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.1"
|
version: "0.7.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -205,14 +228,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
web:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: vm_service
|
||||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "14.2.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.2.0-194.0.dev <4.0.0"
|
dart: ">=3.3.0 <4.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=3.18.0-18.0.pre.54"
|
||||||
|
|
|
@ -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,12 +2,51 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
class AuthScreen extends StatefulWidget {
|
class AuthScreen extends StatefulWidget {
|
||||||
|
/// Constructs an [AuthScreen] object.
|
||||||
|
///
|
||||||
|
/// [appBarTitle] specifies the title of the app bar.
|
||||||
|
///
|
||||||
|
/// [onFinish] is a function called upon
|
||||||
|
/// completion of the authentication process.
|
||||||
|
///
|
||||||
|
/// [steps] is a list of authentication steps to be completed.
|
||||||
|
///
|
||||||
|
/// [submitBtnTitle] specifies the title of the submit button.
|
||||||
|
///
|
||||||
|
/// [nextBtnTitle] specifies the title of the next button.
|
||||||
|
///
|
||||||
|
/// [previousBtnTitle] specifies the title of the previous button.
|
||||||
|
///
|
||||||
|
/// [customAppBar] allows customization of the app bar.
|
||||||
|
///
|
||||||
|
/// [buttonMainAxisAlignment] specifies the alignment of the buttons.
|
||||||
|
///
|
||||||
|
/// [customBackgroundColor] allows customization of the background color.
|
||||||
|
///
|
||||||
|
/// [nextButtonBuilder] allows customization of the next button.
|
||||||
|
///
|
||||||
|
/// [previousButtonBuilder] allows customization of the previous button.
|
||||||
|
///
|
||||||
|
/// [titleWidget] specifies a custom widget
|
||||||
|
/// to be displayed at the top of the screen.
|
||||||
|
///
|
||||||
|
/// [loginButton] specifies a custom login button widget.
|
||||||
|
///
|
||||||
|
/// [titleFlex] specifies the flex value for the title widget.
|
||||||
|
///
|
||||||
|
/// [formFlex] specifies the flex value for the form widget.
|
||||||
|
///
|
||||||
|
/// [beforeTitleFlex] specifies the flex value before the title widget.
|
||||||
|
///
|
||||||
|
/// [afterTitleFlex] specifies the flex value after the title widget.
|
||||||
|
|
||||||
const AuthScreen({
|
const AuthScreen({
|
||||||
required this.appBarTitle,
|
required this.appBarTitle,
|
||||||
required this.steps,
|
required this.steps,
|
||||||
|
@ -16,6 +55,7 @@ class AuthScreen extends StatefulWidget {
|
||||||
required this.previousBtnTitle,
|
required this.previousBtnTitle,
|
||||||
required this.onFinish,
|
required this.onFinish,
|
||||||
this.customAppBar,
|
this.customAppBar,
|
||||||
|
this.buttonMainAxisAlignment,
|
||||||
this.customBackgroundColor,
|
this.customBackgroundColor,
|
||||||
this.nextButtonBuilder,
|
this.nextButtonBuilder,
|
||||||
this.previousButtonBuilder,
|
this.previousButtonBuilder,
|
||||||
|
@ -25,35 +65,79 @@ class AuthScreen extends StatefulWidget {
|
||||||
this.formFlex,
|
this.formFlex,
|
||||||
this.beforeTitleFlex,
|
this.beforeTitleFlex,
|
||||||
this.afterTitleFlex,
|
this.afterTitleFlex,
|
||||||
|
this.maxFormWidth,
|
||||||
super.key,
|
super.key,
|
||||||
}) : assert(steps.length > 0, 'At least one step is required');
|
}) : assert(steps.length > 0, "At least one step is required");
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// The maximum width of the form.
|
||||||
|
final double? maxFormWidth;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AuthScreen> createState() => _AuthScreenState();
|
State<AuthScreen> createState() => _AuthScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The state for [AuthScreen].
|
||||||
class _AuthScreenState extends State<AuthScreen> {
|
class _AuthScreenState extends State<AuthScreen> {
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _pageController = PageController();
|
final _pageController = PageController();
|
||||||
|
@ -61,21 +145,27 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
final _animationCurve = Curves.ease;
|
final _animationCurve = Curves.ease;
|
||||||
bool _formValid = false;
|
bool _formValid = false;
|
||||||
|
|
||||||
|
/// Gets the app bar.
|
||||||
AppBar get _appBar =>
|
AppBar get _appBar =>
|
||||||
widget.customAppBar ??
|
widget.customAppBar ??
|
||||||
AppBar(
|
AppBar(
|
||||||
|
backgroundColor: const Color(0xffFAF9F6),
|
||||||
title: Text(widget.appBarTitle),
|
title: Text(widget.appBarTitle),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Handles previous button press.
|
||||||
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,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles next button press.
|
||||||
Future<void> onNext(AuthStep step) async {
|
Future<void> onNext(AuthStep step) async {
|
||||||
if (!_formKey.currentState!.validate()) {
|
if (!_formKey.currentState!.validate()) {
|
||||||
return;
|
return;
|
||||||
|
@ -96,30 +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.
|
||||||
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;
|
||||||
|
@ -138,162 +236,186 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: widget.customBackgroundColor ?? Colors.white,
|
backgroundColor: widget.customBackgroundColor ?? const Color(0xffFAF9F6),
|
||||||
appBar: _appBar,
|
appBar: _appBar,
|
||||||
body: Form(
|
body: SafeArea(
|
||||||
key: _formKey,
|
child: Form(
|
||||||
child: PageView(
|
key: _formKey,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
child: PageView(
|
||||||
controller: _pageController,
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
children: <Widget>[
|
controller: _pageController,
|
||||||
for (var i = 0; i < widget.steps.length; i++)
|
children: <Widget>[
|
||||||
Column(
|
for (var currentStep = 0;
|
||||||
mainAxisSize: MainAxisSize.min,
|
currentStep < widget.steps.length;
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
currentStep++)
|
||||||
children: [
|
Column(
|
||||||
if (widget.titleWidget != null) ...[
|
mainAxisSize: MainAxisSize.min,
|
||||||
Expanded(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
flex: widget.titleFlex ?? 1,
|
children: [
|
||||||
child: Column(
|
if (widget.titleWidget != null) ...[
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
Expanded(
|
||||||
mainAxisSize: MainAxisSize.min,
|
flex: widget.titleFlex ?? 1,
|
||||||
children: [
|
child: Column(
|
||||||
Spacer(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
flex: widget.beforeTitleFlex ?? 3,
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
children: [
|
||||||
widget.titleWidget!,
|
Expanded(
|
||||||
Spacer(
|
flex: widget.beforeTitleFlex ?? 2,
|
||||||
flex: widget.afterTitleFlex ?? 2,
|
child: Container(),
|
||||||
),
|
),
|
||||||
],
|
widget.titleWidget!,
|
||||||
|
Expanded(
|
||||||
|
flex: widget.afterTitleFlex ?? 2,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
Expanded(
|
||||||
Expanded(
|
flex: widget.formFlex ?? 3,
|
||||||
flex: widget.formFlex ?? 3,
|
child: Align(
|
||||||
child: Column(
|
alignment: Alignment.topCenter,
|
||||||
children: [
|
child: ConstrainedBox(
|
||||||
for (AuthField field in widget.steps[i].fields)
|
constraints: BoxConstraints(
|
||||||
Align(
|
maxWidth: widget.maxFormWidth ?? 300,
|
||||||
child: Column(
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
for (AuthField field
|
||||||
|
in widget.steps[currentStep].fields) ...[
|
||||||
if (field.title != null) ...[
|
if (field.title != null) ...[
|
||||||
field.title!,
|
wrapWithDefaultStyle(
|
||||||
|
style: theme.textTheme.headlineLarge!,
|
||||||
|
widget: field.title!,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
field.build(context, () {
|
field.build(context, () {
|
||||||
_validate(i);
|
_validate(currentStep);
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 15.0,
|
|
||||||
bottom: 30.0,
|
|
||||||
left: widget.previousButtonBuilder == null &&
|
|
||||||
widget.steps.first != widget.steps[i]
|
|
||||||
? 30.0
|
|
||||||
: 0.0,
|
|
||||||
right: widget.nextButtonBuilder == null &&
|
|
||||||
widget.previousButtonBuilder == null
|
|
||||||
? 30.0
|
|
||||||
: 0.0,
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
(widget.previousButtonBuilder != null &&
|
|
||||||
widget.previousButtonBuilder?.call(
|
|
||||||
onPrevious,
|
|
||||||
widget.previousBtnTitle,
|
|
||||||
i,
|
|
||||||
) ==
|
|
||||||
null)
|
|
||||||
? MainAxisAlignment.spaceAround
|
|
||||||
: widget.steps.first != widget.steps[i]
|
|
||||||
? MainAxisAlignment.spaceBetween
|
|
||||||
: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
if (widget.previousButtonBuilder == null) ...[
|
|
||||||
if (widget.steps.first != widget.steps[i])
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: onPrevious,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.arrow_back,
|
|
||||||
size: 18,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 4.0),
|
|
||||||
child: Text(widget.previousBtnTitle),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
] else if (widget.previousButtonBuilder?.call(
|
|
||||||
onPrevious, widget.previousBtnTitle, i) !=
|
|
||||||
null) ...[
|
|
||||||
widget.previousButtonBuilder!
|
|
||||||
.call(onPrevious, widget.previousBtnTitle, i)!
|
|
||||||
],
|
|
||||||
widget.nextButtonBuilder?.call(
|
|
||||||
!_formValid
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
await onNext(widget.steps[i]);
|
|
||||||
},
|
|
||||||
widget.steps.last == widget.steps[i]
|
|
||||||
? widget.submitBtnTitle
|
|
||||||
: widget.nextBtnTitle,
|
|
||||||
i,
|
|
||||||
_formValid,
|
|
||||||
) ??
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: !_formValid
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
await onNext(widget.steps[i]);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.steps.last == widget.steps[i]
|
|
||||||
? widget.submitBtnTitle
|
|
||||||
: widget.nextBtnTitle,
|
|
||||||
),
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.only(left: 4.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_forward,
|
|
||||||
size: 18,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
if (widget.loginButton != null)
|
if (widget.loginButton != null)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 20.0),
|
padding: const EdgeInsets.only(top: 20.0),
|
||||||
child: widget.loginButton!,
|
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);
|
||||||
}
|
}
|
||||||
|
|
12
lib/src/config/example_registration_repository.dart
Normal file
12
lib/src/config/example_registration_repository.dart
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import "dart:collection";
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
|
/// A registration repository that does nothing.
|
||||||
|
class ExampleRegistrationRepository with RegistrationRepository {
|
||||||
|
@override
|
||||||
|
Future<String?> register(HashMap values) {
|
||||||
|
debugPrint("register $values");
|
||||||
|
return Future.value(null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,170 +2,227 @@
|
||||||
//
|
//
|
||||||
// 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";
|
||||||
|
|
||||||
|
/// A set of options for configuring the
|
||||||
|
/// registration process in a Flutter application.
|
||||||
class RegistrationOptions {
|
class RegistrationOptions {
|
||||||
|
/// Registration options Constructor
|
||||||
RegistrationOptions({
|
RegistrationOptions({
|
||||||
required this.registrationRepository,
|
|
||||||
required this.registrationSteps,
|
|
||||||
required this.afterRegistration,
|
required this.afterRegistration,
|
||||||
|
this.registrationRepository,
|
||||||
|
this.registrationSteps,
|
||||||
|
this.titleFlex,
|
||||||
|
this.formFlex,
|
||||||
|
this.beforeTitleFlex,
|
||||||
|
this.afterTitleFlex,
|
||||||
this.registrationTranslations = const RegistrationTranslations.empty(),
|
this.registrationTranslations = const RegistrationTranslations.empty(),
|
||||||
this.onError,
|
this.onError,
|
||||||
this.customAppbarBuilder,
|
this.customAppbarBuilder = _createCustomAppBar,
|
||||||
this.nextButtonBuilder,
|
this.nextButtonBuilder,
|
||||||
this.previousButtonBuilder,
|
this.previousButtonBuilder,
|
||||||
|
this.buttonMainAxisAlignment,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
this.titleWidget,
|
this.titleWidget,
|
||||||
this.loginButton,
|
this.loginButton,
|
||||||
});
|
this.maxFormWidth,
|
||||||
|
this.beforeRegistration,
|
||||||
|
}) {
|
||||||
|
if (registrationSteps == null || registrationSteps!.isEmpty) {
|
||||||
|
steps = RegistrationOptions.getDefaultSteps();
|
||||||
|
} else {
|
||||||
|
steps = registrationSteps!;
|
||||||
|
}
|
||||||
|
registrationRepository ??= ExampleRegistrationRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translations for registration-related messages and prompts.
|
||||||
final RegistrationTranslations registrationTranslations;
|
final RegistrationTranslations registrationTranslations;
|
||||||
final List<AuthStep> registrationSteps;
|
|
||||||
|
/// The steps involved in the registration process.
|
||||||
|
final List<AuthStep>? registrationSteps;
|
||||||
|
|
||||||
|
/// The steps involved in the registration process.
|
||||||
|
List<AuthStep> steps = [];
|
||||||
|
|
||||||
|
/// A function that handles errors during registration.
|
||||||
final int? Function(String error)? onError;
|
final int? Function(String error)? onError;
|
||||||
|
|
||||||
|
/// A callback function executed after successful registration.
|
||||||
final VoidCallback afterRegistration;
|
final VoidCallback afterRegistration;
|
||||||
final RegistrationRepository registrationRepository;
|
|
||||||
|
/// The repository responsible for registration.
|
||||||
|
RegistrationRepository? registrationRepository;
|
||||||
|
|
||||||
|
/// A function for customizing the app bar displayed during registration.
|
||||||
final AppBar Function(String title)? customAppbarBuilder;
|
final AppBar Function(String title)? customAppbarBuilder;
|
||||||
final Widget Function(Future<void> Function()? onPressed, String label,
|
|
||||||
int step, bool enabled)? nextButtonBuilder;
|
/// A function for customizing the "Next" button.
|
||||||
|
final Widget Function(
|
||||||
|
Future<void> Function()? onPressed,
|
||||||
|
String label,
|
||||||
|
int step,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
bool enabled,
|
||||||
|
)? nextButtonBuilder;
|
||||||
|
|
||||||
|
/// 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)?
|
||||||
previousButtonBuilder;
|
previousButtonBuilder;
|
||||||
final Color? backgroundColor;
|
|
||||||
Widget? titleWidget;
|
|
||||||
Widget? loginButton;
|
|
||||||
|
|
||||||
|
/// Specifies the alignment of buttons.
|
||||||
|
final MainAxisAlignment? buttonMainAxisAlignment;
|
||||||
|
|
||||||
|
/// The background color of the registration screen.
|
||||||
|
final Color? backgroundColor;
|
||||||
|
|
||||||
|
/// A custom widget for displaying the registration title.
|
||||||
|
final Widget? titleWidget;
|
||||||
|
|
||||||
|
/// A custom widget for displaying a login button.
|
||||||
|
final Widget? loginButton;
|
||||||
|
|
||||||
|
/// The number of flex units for the title.
|
||||||
|
final int? titleFlex;
|
||||||
|
|
||||||
|
/// The number of flex units for the form.
|
||||||
|
final int? formFlex;
|
||||||
|
|
||||||
|
/// The number of flex units for the buttons.
|
||||||
|
final int? beforeTitleFlex;
|
||||||
|
|
||||||
|
/// The number of flex units for the buttons.
|
||||||
|
final int? afterTitleFlex;
|
||||||
|
|
||||||
|
/// The maximum width of the form. Defaults to 300.
|
||||||
|
final double? maxFormWidth;
|
||||||
|
|
||||||
|
/// This function gets called before the user gets registered.
|
||||||
|
final Future<void> Function()? beforeRegistration;
|
||||||
|
|
||||||
|
/// Generates default registration steps.
|
||||||
|
///
|
||||||
|
/// [emailController] controller for email input.
|
||||||
|
///
|
||||||
|
/// [pass1Controller] controller for first password input.
|
||||||
|
///
|
||||||
|
/// [pass1Hidden] whether the first password field is initially hidden.
|
||||||
|
///
|
||||||
|
/// [pass2Controller] controller for second password input.
|
||||||
|
///
|
||||||
|
/// [pass2Hidden] whether the second password field is initially hidden.
|
||||||
|
///
|
||||||
|
/// [passHideOnChange] function triggered when password visibility changes.
|
||||||
|
///
|
||||||
|
/// [translations] translations for default registration messages and prompts.
|
||||||
|
///
|
||||||
|
/// [titleBuilder] function for customizing step titles.
|
||||||
|
///
|
||||||
|
/// [labelBuilder] function for customizing field labels.
|
||||||
|
///
|
||||||
|
/// [textStyle] text style for input fields.
|
||||||
|
///
|
||||||
|
/// [initialEmail] initial value for email input.
|
||||||
static List<AuthStep> getDefaultSteps({
|
static List<AuthStep> getDefaultSteps({
|
||||||
TextEditingController? emailController,
|
TextEditingController? emailController,
|
||||||
TextEditingController? pass1Controller,
|
TextEditingController? passController,
|
||||||
bool pass1Hidden = true,
|
bool passHidden = true,
|
||||||
TextEditingController? pass2Controller,
|
// ignore: avoid_positional_boolean_parameters
|
||||||
bool pass2Hidden = true,
|
|
||||||
Function(bool mainPass, bool value)? passHideOnChange,
|
Function(bool mainPass, bool value)? passHideOnChange,
|
||||||
RegistrationTranslations translations =
|
RegistrationTranslations translations =
|
||||||
const RegistrationTranslations.empty(),
|
const RegistrationTranslations.empty(),
|
||||||
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,
|
||||||
}) {
|
}) =>
|
||||||
var password1 = '';
|
[
|
||||||
|
AuthStep(
|
||||||
return [
|
fields: [
|
||||||
AuthStep(
|
AuthTextField(
|
||||||
fields: [
|
name: "email",
|
||||||
AuthTextField(
|
textEditingController: emailController,
|
||||||
name: 'email',
|
value: initialEmail ?? "",
|
||||||
textEditingController: emailController,
|
title: titleBuilder?.call(translations.defaultEmailTitle) ??
|
||||||
value: initialEmail ?? '',
|
Padding(
|
||||||
title: titleBuilder?.call(
|
padding: const EdgeInsets.only(top: 180),
|
||||||
translations.defaultEmailTitle,
|
child: Text(
|
||||||
) ??
|
translations.defaultEmailTitle,
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 24.0,
|
|
||||||
bottom: 12.0,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
translations.defaultEmailTitle,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
textInputType: TextInputType.emailAddress,
|
||||||
textFieldDecoration: InputDecoration(
|
textFieldDecoration: InputDecoration(
|
||||||
label: labelBuilder?.call(translations.defaultEmailLabel),
|
hintStyle: hintStyle,
|
||||||
hintText: translations.defaultEmailHint,
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
label: labelBuilder?.call(translations.defaultEmailLabel),
|
||||||
|
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,
|
],
|
||||||
validators: [
|
),
|
||||||
(email) => (email == null || email.isEmpty)
|
AuthStep(
|
||||||
? translations.defaultEmailEmpty
|
fields: [
|
||||||
: null,
|
AuthPassField(
|
||||||
(email) =>
|
name: "password",
|
||||||
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])+)\])""")
|
textEditingController: passController,
|
||||||
.hasMatch(email!)
|
title: titleBuilder?.call(translations.defaultPasswordTitle) ??
|
||||||
? null
|
Padding(
|
||||||
: translations.defaultEmailValidatorMessage,
|
padding: const EdgeInsets.only(top: 180),
|
||||||
],
|
child: Text(
|
||||||
)
|
translations.defaultPasswordTitle,
|
||||||
],
|
|
||||||
),
|
|
||||||
AuthStep(
|
|
||||||
fields: [
|
|
||||||
AuthPassField(
|
|
||||||
name: 'password1',
|
|
||||||
textEditingController: pass1Controller,
|
|
||||||
title: titleBuilder?.call(
|
|
||||||
translations.defaultPassword1Title,
|
|
||||||
) ??
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 24.0,
|
|
||||||
bottom: 12.0,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
translations.defaultPassword1Title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
textFieldDecoration: InputDecoration(
|
||||||
textFieldDecoration: InputDecoration(
|
hintStyle: hintStyle,
|
||||||
label: labelBuilder?.call(translations.defaultPassword1Label),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
hintText: translations.defaultPassword1Hint,
|
label: labelBuilder?.call(translations.defaultPasswordLabel),
|
||||||
),
|
hintText: translations.defaultPasswordHint,
|
||||||
textStyle: textStyle,
|
border: const OutlineInputBorder(),
|
||||||
validators: [
|
focusedBorder: const OutlineInputBorder(),
|
||||||
(value) => (value == null || value.isEmpty)
|
),
|
||||||
? translations.defaultPassword1ValidatorMessage
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
: null,
|
textStyle: textStyle,
|
||||||
],
|
validators: [
|
||||||
onChange: (value) {
|
(value) {
|
||||||
password1 = value;
|
// ignore: avoid_dynamic_calls
|
||||||
},
|
if (value == null || value.isEmpty) {
|
||||||
),
|
return translations.defaultPasswordValidatorMessage;
|
||||||
AuthPassField(
|
|
||||||
name: 'password2',
|
|
||||||
textEditingController: pass2Controller,
|
|
||||||
title: titleBuilder?.call(
|
|
||||||
translations.defaultPassword2Title,
|
|
||||||
) ??
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 24.0,
|
|
||||||
bottom: 12.0,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
translations.defaultPassword2Title,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
textFieldDecoration: InputDecoration(
|
|
||||||
label: labelBuilder?.call(translations.defaultPassword2Label),
|
|
||||||
hintText: translations.defaultPassword2Hint,
|
|
||||||
),
|
|
||||||
textStyle: textStyle,
|
|
||||||
validators: [
|
|
||||||
(value) {
|
|
||||||
if (pass1Controller != null) {
|
|
||||||
if (value != pass1Controller.value.text) {
|
|
||||||
return translations.defaultPassword2ValidatorMessage;
|
|
||||||
}
|
}
|
||||||
} else {
|
// ignore: avoid_dynamic_calls
|
||||||
if (value != password1) {
|
if (value.length < 6) {
|
||||||
return translations.defaultPassword2ValidatorMessage;
|
return translations.defaultPasswordToShortValidatorMessage;
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
return null;
|
},
|
||||||
}
|
],
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
];
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppBar _createCustomAppBar(String title) => AppBar(
|
||||||
|
iconTheme: const IconThemeData(color: Colors.black, size: 16),
|
||||||
|
title: Text(title),
|
||||||
|
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,
|
||||||
|
@ -15,56 +17,78 @@ class RegistrationTranslations {
|
||||||
required this.defaultEmailHint,
|
required this.defaultEmailHint,
|
||||||
required this.defaultEmailEmpty,
|
required this.defaultEmailEmpty,
|
||||||
required this.defaultEmailValidatorMessage,
|
required this.defaultEmailValidatorMessage,
|
||||||
required this.defaultPassword1Title,
|
required this.defaultPasswordTitle,
|
||||||
required this.defaultPassword1Label,
|
required this.defaultPasswordLabel,
|
||||||
required this.defaultPassword1Hint,
|
required this.defaultPasswordHint,
|
||||||
required this.defaultPassword1ValidatorMessage,
|
required this.defaultPasswordValidatorMessage,
|
||||||
required this.defaultPassword2Title,
|
required this.defaultPasswordToShortValidatorMessage,
|
||||||
required this.defaultPassword2Label,
|
|
||||||
required this.defaultPassword2Hint,
|
|
||||||
required this.defaultPassword2ValidatorMessage,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Constructs a [RegistrationTranslations] object with empty strings.
|
||||||
const RegistrationTranslations.empty()
|
const RegistrationTranslations.empty()
|
||||||
: title = 'Register',
|
: title = "",
|
||||||
registerBtn = 'Register',
|
registerBtn = "Register",
|
||||||
previousStepBtn = 'Previous',
|
previousStepBtn = "Previous",
|
||||||
nextStepBtn = 'Next',
|
nextStepBtn = "Next",
|
||||||
closeBtn = 'Close',
|
closeBtn = "Close",
|
||||||
defaultEmailTitle = 'What is your email?',
|
defaultEmailTitle = "enter your email address",
|
||||||
defaultEmailLabel = '',
|
defaultEmailLabel = "",
|
||||||
defaultEmailHint = 'john.doe@domain.com',
|
defaultEmailHint = "Email address",
|
||||||
defaultEmailEmpty = 'Enter your email',
|
defaultEmailEmpty = "Please enter your email address.",
|
||||||
defaultEmailValidatorMessage = 'Enter a valid email address',
|
defaultEmailValidatorMessage = "Please enter a valid email address.",
|
||||||
defaultPassword1Title = 'Enter a password',
|
defaultPasswordTitle = "choose a password",
|
||||||
defaultPassword1Label = '',
|
defaultPasswordLabel = "",
|
||||||
defaultPassword1Hint = '',
|
defaultPasswordHint = "Password",
|
||||||
defaultPassword1ValidatorMessage = 'Enter a valid password',
|
defaultPasswordValidatorMessage = "Enter a valid password",
|
||||||
defaultPassword2Title = 'Re-enter password',
|
defaultPasswordToShortValidatorMessage =
|
||||||
defaultPassword2Label = '',
|
"Password needs to be at least 6 characters long";
|
||||||
defaultPassword2Hint = '',
|
|
||||||
defaultPassword2ValidatorMessage = 'Passwords have to be equal';
|
|
||||||
|
|
||||||
|
/// The title of the registration screen.
|
||||||
final String title;
|
final String title;
|
||||||
final String registerBtn;
|
|
||||||
final String previousStepBtn;
|
|
||||||
final String nextStepBtn;
|
|
||||||
final String closeBtn;
|
|
||||||
final String defaultEmailTitle;
|
|
||||||
final String defaultEmailLabel;
|
|
||||||
final String defaultEmailHint;
|
|
||||||
final String defaultEmailEmpty;
|
|
||||||
final String defaultEmailValidatorMessage;
|
|
||||||
final String defaultPassword1Title;
|
|
||||||
final String defaultPassword1Label;
|
|
||||||
final String defaultPassword1Hint;
|
|
||||||
final String defaultPassword1ValidatorMessage;
|
|
||||||
final String defaultPassword2Title;
|
|
||||||
final String defaultPassword2Label;
|
|
||||||
final String defaultPassword2Hint;
|
|
||||||
final String defaultPassword2ValidatorMessage;
|
|
||||||
|
|
||||||
// create a copywith
|
/// The text for the registration button.
|
||||||
|
final String registerBtn;
|
||||||
|
|
||||||
|
/// The text for the previous step button.
|
||||||
|
final String previousStepBtn;
|
||||||
|
|
||||||
|
/// The text for the next step button.
|
||||||
|
final String nextStepBtn;
|
||||||
|
|
||||||
|
/// The text for the close button.
|
||||||
|
final String closeBtn;
|
||||||
|
|
||||||
|
/// The title for the default email field.
|
||||||
|
final String defaultEmailTitle;
|
||||||
|
|
||||||
|
/// The label for the default email field.
|
||||||
|
final String defaultEmailLabel;
|
||||||
|
|
||||||
|
/// The hint for the default email field.
|
||||||
|
final String defaultEmailHint;
|
||||||
|
|
||||||
|
/// The message for an empty default email field.
|
||||||
|
final String defaultEmailEmpty;
|
||||||
|
|
||||||
|
/// The message for an invalid default email field.
|
||||||
|
final String defaultEmailValidatorMessage;
|
||||||
|
|
||||||
|
/// The title for the default password field.
|
||||||
|
final String defaultPasswordTitle;
|
||||||
|
|
||||||
|
/// The label for the default password field.
|
||||||
|
final String defaultPasswordLabel;
|
||||||
|
|
||||||
|
/// The hint for the default password field.
|
||||||
|
final String defaultPasswordHint;
|
||||||
|
|
||||||
|
/// The message for an invalid default password field.
|
||||||
|
final String defaultPasswordValidatorMessage;
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
@ -76,41 +100,31 @@ class RegistrationTranslations {
|
||||||
String? defaultEmailHint,
|
String? defaultEmailHint,
|
||||||
String? defaultEmailEmpty,
|
String? defaultEmailEmpty,
|
||||||
String? defaultEmailValidatorMessage,
|
String? defaultEmailValidatorMessage,
|
||||||
String? defaultPassword1Title,
|
String? defaultPasswordTitle,
|
||||||
String? defaultPassword1Label,
|
String? defaultPasswordLabel,
|
||||||
String? defaultPassword1Hint,
|
String? defaultPasswordHint,
|
||||||
String? defaultPassword1ValidatorMessage,
|
String? defaultPasswordValidatorMessage,
|
||||||
String? defaultPassword2Title,
|
String? defaultPasswordToShortValidatorMessage,
|
||||||
String? defaultPassword2Label,
|
}) =>
|
||||||
String? defaultPassword2Hint,
|
RegistrationTranslations(
|
||||||
String? defaultPassword2ValidatorMessage,
|
title: title ?? this.title,
|
||||||
}) {
|
registerBtn: registerBtn ?? this.registerBtn,
|
||||||
return RegistrationTranslations(
|
previousStepBtn: previousStepBtn ?? this.previousStepBtn,
|
||||||
title: title ?? this.title,
|
nextStepBtn: nextStepBtn ?? this.nextStepBtn,
|
||||||
registerBtn: registerBtn ?? this.registerBtn,
|
closeBtn: closeBtn ?? this.closeBtn,
|
||||||
previousStepBtn: previousStepBtn ?? this.previousStepBtn,
|
defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle,
|
||||||
nextStepBtn: nextStepBtn ?? this.nextStepBtn,
|
defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel,
|
||||||
closeBtn: closeBtn ?? this.closeBtn,
|
defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint,
|
||||||
defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle,
|
defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty,
|
||||||
defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel,
|
defaultEmailValidatorMessage:
|
||||||
defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint,
|
defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage,
|
||||||
defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty,
|
defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle,
|
||||||
defaultEmailValidatorMessage:
|
defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel,
|
||||||
defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage,
|
defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint,
|
||||||
defaultPassword1Title:
|
defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ??
|
||||||
defaultPassword1Title ?? this.defaultPassword1Title,
|
this.defaultPasswordValidatorMessage,
|
||||||
defaultPassword1Label:
|
defaultPasswordToShortValidatorMessage:
|
||||||
defaultPassword1Label ?? this.defaultPassword1Label,
|
defaultPasswordToShortValidatorMessage ??
|
||||||
defaultPassword1Hint: defaultPassword1Hint ?? this.defaultPassword1Hint,
|
this.defaultPasswordToShortValidatorMessage,
|
||||||
defaultPassword1ValidatorMessage: defaultPassword1ValidatorMessage ??
|
);
|
||||||
this.defaultPassword1ValidatorMessage,
|
|
||||||
defaultPassword2Title:
|
|
||||||
defaultPassword2Title ?? this.defaultPassword2Title,
|
|
||||||
defaultPassword2Label:
|
|
||||||
defaultPassword2Label ?? this.defaultPassword2Label,
|
|
||||||
defaultPassword2Hint: defaultPassword2Hint ?? this.defaultPassword2Hint,
|
|
||||||
defaultPassword2ValidatorMessage: defaultPassword2ValidatorMessage ??
|
|
||||||
this.defaultPassword2ValidatorMessage,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,11 +2,34 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
|
///
|
||||||
|
/// Extends [AuthField].
|
||||||
class AuthBoolField extends AuthField {
|
class AuthBoolField extends AuthField {
|
||||||
|
/// Constructs an [AuthBoolField] object.
|
||||||
|
///
|
||||||
|
/// [name] specifies the name of the field.
|
||||||
|
///
|
||||||
|
/// [widgetType] defines the type of boolean widget to use.
|
||||||
|
///
|
||||||
|
/// [title] specifies the title of the field (optional).
|
||||||
|
///
|
||||||
|
/// [validators] defines a list of validation functions for the field.
|
||||||
|
///
|
||||||
|
/// [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.
|
||||||
|
///
|
||||||
|
/// [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.
|
||||||
AuthBoolField({
|
AuthBoolField({
|
||||||
required super.name,
|
required super.name,
|
||||||
required this.widgetType,
|
required this.widgetType,
|
||||||
|
@ -18,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,60 +1,78 @@
|
||||||
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.
|
||||||
|
///
|
||||||
|
/// Extends [AuthField].
|
||||||
class AuthDropdownField extends AuthField {
|
class AuthDropdownField extends AuthField {
|
||||||
|
/// Constructs an [AuthDropdownField] object.
|
||||||
AuthDropdownField({
|
AuthDropdownField({
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The list of items for the dropdown.
|
||||||
final List<String> items;
|
final List<String> items;
|
||||||
|
|
||||||
|
/// A callback function triggered when the dropdown value changes.
|
||||||
final Function(String?) onChanged;
|
final Function(String?) onChanged;
|
||||||
|
|
||||||
|
/// The currently selected value in the dropdown.
|
||||||
String? selectedValue;
|
String? selectedValue;
|
||||||
|
|
||||||
|
/// The decoration for the dropdown.
|
||||||
final InputDecoration? dropdownDecoration;
|
final InputDecoration? dropdownDecoration;
|
||||||
|
|
||||||
|
/// The padding around the dropdown.
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
/// The text style for the dropdown.
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// The icon to be displayed with the dropdown.
|
||||||
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,9 +2,25 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
|
///
|
||||||
|
/// [T] represents the type of value stored in the field.
|
||||||
abstract class AuthField<T> {
|
abstract class AuthField<T> {
|
||||||
|
/// Constructs an [AuthField] object.
|
||||||
|
///
|
||||||
|
/// [name] specifies the name 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).
|
||||||
|
///
|
||||||
|
/// [title] specifies the title widget of 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,
|
||||||
|
@ -13,12 +29,26 @@ abstract class AuthField<T> {
|
||||||
this.validators = const [],
|
this.validators = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The name of the field.
|
||||||
final String name;
|
final String name;
|
||||||
final Widget? title;
|
|
||||||
List<String? Function(T?)> validators;
|
/// The initial value of the field.
|
||||||
T value;
|
T value;
|
||||||
|
|
||||||
final Function(T)? onValueChanged; // Callback for value changes
|
/// A callback function triggered when the value of the field changes.
|
||||||
|
final Function(T)? onValueChanged;
|
||||||
|
|
||||||
|
/// The title widget of the field.
|
||||||
|
final Widget? title;
|
||||||
|
|
||||||
|
/// A list of validation functions for the field.
|
||||||
|
List<String? Function(T?)> validators;
|
||||||
|
|
||||||
|
/// Builds the widget representing the field.
|
||||||
|
///
|
||||||
|
/// [context] The build context.
|
||||||
|
///
|
||||||
|
/// [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,17 +2,47 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
|
///
|
||||||
|
/// Extends [AuthField].
|
||||||
class AuthPassField extends AuthField {
|
class AuthPassField extends AuthField {
|
||||||
|
/// Constructs an [AuthPassField] object.
|
||||||
|
///
|
||||||
|
/// [name] specifies the name of the field.
|
||||||
|
///
|
||||||
|
/// [textEditingController] controller for the password input (optional).
|
||||||
|
///
|
||||||
|
/// [title] specifies the title widget of 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).
|
||||||
|
///
|
||||||
|
/// [textStyle] defines the text style for the password input.
|
||||||
|
///
|
||||||
|
/// [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).
|
||||||
|
///
|
||||||
|
/// [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)).
|
||||||
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,
|
||||||
|
@ -20,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,55 +2,95 @@
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
|
///
|
||||||
|
/// Extends [AuthField].
|
||||||
class AuthTextField extends AuthField {
|
class AuthTextField extends AuthField {
|
||||||
|
/// Constructs an [AuthTextField] object.
|
||||||
|
///
|
||||||
|
/// [name] specifies the name of the field.
|
||||||
|
///
|
||||||
|
/// [textEditingController] controller for the text input (optional).
|
||||||
|
///
|
||||||
|
/// [title] specifies the title widget of 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).
|
||||||
|
///
|
||||||
|
/// [textStyle] defines the text style for the text input.
|
||||||
|
///
|
||||||
|
/// [onChange] is a callback function triggered
|
||||||
|
/// when the value of the field changes.
|
||||||
|
///
|
||||||
|
/// [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)).
|
||||||
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,60 +1,77 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
import "dart:collection";
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
import 'dart:collection';
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
import "package:flutter_registration/src/auth_screen.dart";
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
/// A screen for user registration.
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
class RegistrationScreen extends StatefulWidget {
|
||||||
import 'package:flutter_registration/src/auth_screen.dart';
|
/// Constructs a [RegistrationScreen] object.
|
||||||
|
///
|
||||||
class RegistrationScreen extends StatelessWidget {
|
/// [registrationOptions] specifies the registration options.
|
||||||
const RegistrationScreen({
|
const RegistrationScreen({
|
||||||
required this.registrationOptions,
|
required this.registrationOptions,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The registration options.
|
||||||
final RegistrationOptions registrationOptions;
|
final RegistrationOptions registrationOptions;
|
||||||
|
|
||||||
Future<void> register({
|
@override
|
||||||
|
RegistrationScreenState createState() => RegistrationScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The state for [RegistrationScreen].
|
||||||
|
class RegistrationScreenState extends State<RegistrationScreen> {
|
||||||
|
/// Registers the user.
|
||||||
|
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 {
|
||||||
var registered =
|
await widget.registrationOptions.beforeRegistration?.call();
|
||||||
await registrationOptions.registrationRepository.register(values);
|
|
||||||
|
var registered = await widget.registrationOptions.registrationRepository!
|
||||||
|
.register(values);
|
||||||
|
|
||||||
if (registered == null) {
|
if (registered == null) {
|
||||||
registrationOptions.afterRegistration();
|
widget.registrationOptions.afterRegistration();
|
||||||
} else {
|
} else {
|
||||||
var pageToReturn = registrationOptions.onError?.call(registered);
|
var pageToReturn = widget.registrationOptions.onError?.call(registered);
|
||||||
|
|
||||||
onError(pageToReturn);
|
onError(pageToReturn);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} on Exception catch (_) {
|
||||||
onError(0);
|
onError(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var translations = registrationOptions.registrationTranslations;
|
var translations = widget.registrationOptions.registrationTranslations;
|
||||||
|
|
||||||
return AuthScreen(
|
return AuthScreen(
|
||||||
steps: registrationOptions.registrationSteps,
|
steps: widget.registrationOptions.steps,
|
||||||
customAppBar: registrationOptions.customAppbarBuilder?.call(
|
customAppBar: widget.registrationOptions.customAppbarBuilder?.call(
|
||||||
translations.title,
|
translations.title,
|
||||||
),
|
),
|
||||||
onFinish: register,
|
onFinish: _register,
|
||||||
appBarTitle: translations.title,
|
appBarTitle: translations.title,
|
||||||
submitBtnTitle: translations.registerBtn,
|
submitBtnTitle: translations.registerBtn,
|
||||||
nextBtnTitle: translations.nextStepBtn,
|
nextBtnTitle: translations.nextStepBtn,
|
||||||
previousBtnTitle: translations.previousStepBtn,
|
previousBtnTitle: translations.previousStepBtn,
|
||||||
nextButtonBuilder: registrationOptions.nextButtonBuilder,
|
nextButtonBuilder: widget.registrationOptions.nextButtonBuilder,
|
||||||
previousButtonBuilder: registrationOptions.previousButtonBuilder,
|
previousButtonBuilder: widget.registrationOptions.previousButtonBuilder,
|
||||||
customBackgroundColor: registrationOptions.backgroundColor,
|
buttonMainAxisAlignment:
|
||||||
titleWidget: registrationOptions.titleWidget,
|
widget.registrationOptions.buttonMainAxisAlignment,
|
||||||
loginButton: registrationOptions.loginButton,
|
customBackgroundColor: widget.registrationOptions.backgroundColor,
|
||||||
|
titleWidget: widget.registrationOptions.titleWidget,
|
||||||
|
loginButton: widget.registrationOptions.loginButton,
|
||||||
|
titleFlex: widget.registrationOptions.titleFlex,
|
||||||
|
formFlex: widget.registrationOptions.formFlex,
|
||||||
|
beforeTitleFlex: widget.registrationOptions.beforeTitleFlex,
|
||||||
|
afterTitleFlex: widget.registrationOptions.afterTitleFlex,
|
||||||
|
maxFormWidth: widget.registrationOptions.maxFormWidth,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
16
pubspec.yaml
16
pubspec.yaml
|
@ -4,20 +4,19 @@
|
||||||
|
|
||||||
name: flutter_registration
|
name: flutter_registration
|
||||||
description: A Flutter Registration package
|
description: A Flutter Registration package
|
||||||
version: 2.0.0
|
version: 3.0.0
|
||||||
repository: https://github.com/Iconica-Development/flutter_registration
|
repository: https://github.com/Iconica-Development/flutter_registration
|
||||||
|
|
||||||
publish_to: none
|
publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.18.0 <3.0.0"
|
sdk: ">=3.0.0 <4.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter_input_library:
|
flutter_input_library:
|
||||||
git:
|
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
url: https://github.com/Iconica-Development/flutter_input_library
|
version: ^3.6.0
|
||||||
ref: 3.1.0
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
@ -27,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