mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-19 13:23:45 +02:00
Compare commits
51 commits
Author | SHA1 | Date | |
---|---|---|---|
|
ee29893bd9 | ||
|
4bad032588 | ||
|
cf6e30abec | ||
|
ce80a96958 | ||
|
71259c7f78 | ||
|
d82df68989 | ||
|
c91a1c0856 | ||
|
444b11a44f | ||
|
1be83c7013 | ||
|
990259dabc | ||
|
d849a1834a | ||
|
05b143a5bd | ||
|
a44a419bc4 | ||
|
7890c17587 | ||
|
3ec92aa68c | ||
|
7a4bcd8b3f | ||
|
3884ef08b1 | ||
|
dcea299aa6 | ||
|
957abd2ac2 | ||
|
0f28c7084e | ||
|
44871bf44e | ||
|
9ebb917fb8 | ||
|
0472ebc4c5 | ||
|
4b855f7488 | ||
|
81fab79e81 | ||
|
855e12f6ae | ||
|
40c4bfea89 | ||
|
ced19fe61a | ||
|
7cfe7d5112 | ||
|
7bb87acf65 | ||
|
3ca9e7bde7 | ||
|
277b38f39e | ||
|
15aa74ebda | ||
|
49b9ece2d5 | ||
|
e112c64ee7 | ||
|
7abb060306 | ||
|
f328df84dd | ||
|
c86fa1ca5f | ||
|
79fe400842 | ||
|
078574c214 | ||
|
46e2d960af | ||
|
7012942ce5 | ||
|
f1663afa1b | ||
|
d1ad003c22 | ||
|
5bc388677b | ||
|
6666e33391 | ||
|
9886c71fcf | ||
|
e2b73bdb7b | ||
|
059ba3c754 | ||
|
fffafdffd5 | ||
|
f8ecce95a0 |
25 changed files with 1300 additions and 483 deletions
14
.github/workflows/component-documentation.yml
vendored
Normal file
14
.github/workflows/component-documentation.yml
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: Iconica Standard Component Documentation Workflow
|
||||||
|
# Workflow Caller version: 1.0.0
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-iconica-component-documentation-workflow:
|
||||||
|
uses: Iconica-Development/.github/.github/workflows/component-documentation.yml@master
|
||||||
|
secrets: inherit
|
||||||
|
permissions: write-all
|
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -34,3 +34,8 @@ migrate_working_dir/
|
||||||
build/
|
build/
|
||||||
.flutter-plugins
|
.flutter-plugins
|
||||||
.flutter-plugins-dependencies
|
.flutter-plugins-dependencies
|
||||||
|
.metadata
|
||||||
|
|
||||||
|
# FVM Version Cache
|
||||||
|
.fvm/
|
||||||
|
.fvmrc
|
||||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -3,6 +3,46 @@ 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
|
||||||
|
- 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: added title widget and login button builder
|
||||||
|
- feat(bool): Add a boolean field. Can be used for accepting terms and conditions
|
||||||
|
- feat(pass): Add dedicated password screen that manages state internally
|
||||||
|
- fix: Small refactor and brought back the normal alignment for the screens
|
||||||
|
- fix: Fixed alignment and spacing when opening keyboard
|
||||||
|
- feat: add auth drop down field
|
||||||
|
- fix: added step to button builders
|
||||||
|
- fix: exported auth_pass_field
|
||||||
|
- feat: added validation to disable next button
|
||||||
|
- feat(auth-screen): add flexible spacing between fields
|
||||||
|
- fix(keyboard-focus): add unfocus for onPrevious
|
||||||
|
- fix: add empty and required constructors for the RegistrationTranslations
|
||||||
|
|
||||||
|
# 1.2.0
|
||||||
|
|
||||||
|
- feat: Added the ability to have an async register function so you can call it asynchronous.
|
||||||
|
|
||||||
|
# 1.1.0
|
||||||
|
|
||||||
|
- feat: Added the ability to go to specific page on error
|
||||||
|
|
||||||
# 1.0.0
|
# 1.0.0
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Iconica
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
image: cirrusci/flutter
|
|
||||||
|
|
||||||
pipelines:
|
|
||||||
pull-requests:
|
|
||||||
feature/*:
|
|
||||||
- step:
|
|
||||||
caches:
|
|
||||||
- gradle
|
|
||||||
- gradlewrapper
|
|
||||||
- flutter
|
|
||||||
name: Run analyzer & test
|
|
||||||
script:
|
|
||||||
- flutter pub get
|
|
||||||
- flutter format -o none --set-exit-if-changed .
|
|
||||||
- flutter analyze
|
|
||||||
|
|
||||||
develop:
|
|
||||||
- step:
|
|
||||||
caches:
|
|
||||||
- gradle
|
|
||||||
- gradlewrapper
|
|
||||||
- flutter
|
|
||||||
name: Run analyzer & test
|
|
||||||
script:
|
|
||||||
- flutter pub get
|
|
||||||
- flutter format -o none --set-exit-if-changed .
|
|
||||||
- flutter analyze
|
|
||||||
|
|
||||||
hotfix/*:
|
|
||||||
- step:
|
|
||||||
caches:
|
|
||||||
- gradle
|
|
||||||
- gradlewrapper
|
|
||||||
- flutter
|
|
||||||
name: Run analyzer & test
|
|
||||||
script:
|
|
||||||
- flutter pub get
|
|
||||||
- flutter format -o none --set-exit-if-changed .
|
|
||||||
- flutter analyze
|
|
||||||
|
|
||||||
definitions:
|
|
||||||
caches:
|
|
||||||
gradlewrapper: ~/.gradle/wrapper
|
|
||||||
flutter: ~/.pub-cache
|
|
|
@ -26,6 +26,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,27 +2,81 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
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 'example_registration_repository.dart';
|
import 'example_registration_repository.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(
|
runApp(
|
||||||
const MaterialApp(
|
MaterialApp(
|
||||||
home: FlutterRegistrationDemo(),
|
theme: ThemeData(
|
||||||
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
|
errorStyle: TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
home: const FlutterRegistrationDemo(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlutterRegistrationDemo extends StatelessWidget {
|
class FlutterRegistrationDemo extends StatefulWidget {
|
||||||
const FlutterRegistrationDemo({Key? key}) : super(key: key);
|
const FlutterRegistrationDemo({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FlutterRegistrationDemo> createState() =>
|
||||||
|
_FlutterRegistrationDemoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FlutterRegistrationDemoState extends State<FlutterRegistrationDemo> {
|
||||||
|
late List<AuthStep> steps;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
steps = RegistrationOptions.getDefaultSteps();
|
||||||
|
|
||||||
|
steps[1].fields.add(
|
||||||
|
AuthBoolField(
|
||||||
|
name: 'termsConditions',
|
||||||
|
widgetType: BoolWidgetType.checkbox,
|
||||||
|
validators: [
|
||||||
|
(value) {
|
||||||
|
if (value == null || !value) {
|
||||||
|
return 'Required';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rightWidget: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: 'I agree with the ',
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: 'terms & conditions',
|
||||||
|
style: const TextStyle(
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
debugPrint('Open terms and conditions');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RegistrationScreen(
|
return RegistrationScreen(
|
||||||
registrationOptions: RegistrationOptions(
|
registrationOptions: RegistrationOptions(
|
||||||
registrationRepository: ExampleRegistrationRepository(),
|
registrationRepository: ExampleRegistrationRepository(),
|
||||||
registrationSteps: RegistrationOptions.getDefaultSteps(),
|
registrationSteps: steps,
|
||||||
afterRegistration: () {
|
afterRegistration: () {
|
||||||
debugPrint('Registered!');
|
debugPrint('Registered!');
|
||||||
},
|
},
|
||||||
|
@ -32,8 +86,7 @@ class FlutterRegistrationDemo extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
|
|
|
@ -37,18 +37,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.2"
|
version: "1.18.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -62,14 +62,22 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_input_library:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_input_library
|
||||||
|
sha256: db8d9d57c31f166ed7c4ec3d060d18891533c57877a30c6c2668231efa1a44f8
|
||||||
|
url: "https://forgejo.internal.iconica.nl/api/packages/internal/pub/"
|
||||||
|
source: hosted
|
||||||
|
version: "3.6.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.3"
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -81,7 +89,7 @@ packages:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.5.0"
|
version: "2.0.4"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -91,50 +99,74 @@ 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:
|
||||||
name: lints
|
name: lints
|
||||||
sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3"
|
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.1.1"
|
||||||
matcher:
|
matcher:
|
||||||
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: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
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
|
||||||
|
@ -152,18 +184,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -184,10 +216,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.7.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -196,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: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4-beta"
|
version: "14.2.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.1.0-185.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,14 +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.
|
||||||
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';
|
show BoolWidgetType;
|
||||||
export 'src/model/auth_exception.dart';
|
|
||||||
export 'src/model/auth_field.dart';
|
export "src/config/registration_options.dart";
|
||||||
export 'src/model/auth_step.dart';
|
export "src/config/registration_translations.dart";
|
||||||
export 'src/model/auth_text_field.dart';
|
export "src/model/auth_bool_field.dart";
|
||||||
export 'src/registration_screen.dart';
|
export "src/model/auth_drop_down.dart";
|
||||||
export 'src/service/registration_repository.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,63 +2,171 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'dart:collection';
|
import "dart:async";
|
||||||
import 'package:flutter/material.dart';
|
import "dart:collection";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import "package:flutter/material.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.title,
|
required this.appBarTitle,
|
||||||
required this.steps,
|
required this.steps,
|
||||||
required this.submitBtnTitle,
|
required this.submitBtnTitle,
|
||||||
required this.nextBtnTitle,
|
required this.nextBtnTitle,
|
||||||
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,
|
||||||
|
this.titleWidget,
|
||||||
|
this.loginButton,
|
||||||
|
this.titleFlex,
|
||||||
|
this.formFlex,
|
||||||
|
this.beforeTitleFlex,
|
||||||
|
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");
|
||||||
|
|
||||||
final String title;
|
/// The title of the app bar.
|
||||||
final Function({
|
final String appBarTitle;
|
||||||
required HashMap<String, String> values,
|
|
||||||
required VoidCallback onError,
|
/// A function called upon completion of the authentication process.
|
||||||
|
final Future<void> Function({
|
||||||
|
required HashMap<String, dynamic> values,
|
||||||
|
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(VoidCallback, String)? nextButtonBuilder;
|
|
||||||
final Widget Function(VoidCallback, String)? previousButtonBuilder;
|
/// 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)?
|
||||||
|
previousButtonBuilder;
|
||||||
|
|
||||||
|
/// A custom widget for the title.
|
||||||
|
final Widget? titleWidget;
|
||||||
|
|
||||||
|
/// A custom widget for the login button.
|
||||||
|
final Widget? loginButton;
|
||||||
|
|
||||||
|
/// The flex value for the title widget.
|
||||||
|
final int? titleFlex;
|
||||||
|
|
||||||
|
/// The flex value for the form widget.
|
||||||
|
final int? formFlex;
|
||||||
|
|
||||||
|
/// The flex value before the title widget.
|
||||||
|
final int? beforeTitleFlex;
|
||||||
|
|
||||||
|
/// The flex value after the title widget.
|
||||||
|
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();
|
||||||
final _animationDuration = const Duration(milliseconds: 300);
|
final _animationDuration = const Duration(milliseconds: 300);
|
||||||
final _animationCurve = Curves.ease;
|
final _animationCurve = Curves.ease;
|
||||||
|
bool _formValid = false;
|
||||||
|
|
||||||
|
/// Gets the app bar.
|
||||||
AppBar get _appBar =>
|
AppBar get _appBar =>
|
||||||
widget.customAppBar ??
|
widget.customAppBar ??
|
||||||
AppBar(
|
AppBar(
|
||||||
title: Text(widget.title),
|
backgroundColor: const Color(0xffFAF9F6),
|
||||||
|
title: Text(widget.appBarTitle),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Handles previous button press.
|
||||||
void onPrevious() {
|
void onPrevious() {
|
||||||
_pageController.previousPage(
|
FocusScope.of(context).unfocus();
|
||||||
duration: _animationDuration,
|
_validate(_pageController.page!.toInt() - 1);
|
||||||
curve: _animationCurve,
|
unawaited(
|
||||||
|
_pageController.previousPage(
|
||||||
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNext(AuthStep step) {
|
/// Handles next button press.
|
||||||
|
Future<void> onNext(AuthStep step) async {
|
||||||
if (!_formKey.currentState!.validate()) {
|
if (!_formKey.currentState!.validate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -68,141 +176,246 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
if (widget.steps.last == step) {
|
if (widget.steps.last == step) {
|
||||||
var values = HashMap<String, String>();
|
var values = HashMap<String, dynamic>();
|
||||||
|
|
||||||
for (var step in widget.steps) {
|
for (var step in widget.steps) {
|
||||||
for (var field in step.fields) {
|
for (var field in step.fields) {
|
||||||
values[field.name] =
|
values[field.name] = field.value;
|
||||||
(field as AuthTextField).textController.value.text;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.onFinish(
|
await widget.onFinish(
|
||||||
values: values,
|
values: values,
|
||||||
onError: () => _pageController.animateToPage(
|
onError: (int? pageToReturn) {
|
||||||
0,
|
if (pageToReturn == null) {
|
||||||
duration: _animationDuration,
|
return;
|
||||||
curve: _animationCurve,
|
}
|
||||||
),
|
_pageController.animateToPage(
|
||||||
|
pageToReturn,
|
||||||
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
_pageController.nextPage(
|
_validate(_pageController.page!.toInt() + 1);
|
||||||
duration: _animationDuration,
|
unawaited(
|
||||||
curve: _animationCurve,
|
_pageController.nextPage(
|
||||||
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates the current step.
|
||||||
|
void _validate(int currentPage) {
|
||||||
|
var isStepValid = true;
|
||||||
|
|
||||||
|
// Loop through each field in the current step
|
||||||
|
for (var field in widget.steps[currentPage].fields) {
|
||||||
|
for (var validator in field.validators) {
|
||||||
|
var validationResult = validator(field.value);
|
||||||
|
if (validationResult != null) {
|
||||||
|
// If any validator returns an error, mark step as invalid and break
|
||||||
|
isStepValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isStepValid) {
|
||||||
|
break; // No need to check further fields if one is already invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_formValid = isStepValid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@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 (AuthStep step in widget.steps)
|
children: <Widget>[
|
||||||
Column(
|
for (var currentStep = 0;
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
currentStep < widget.steps.length;
|
||||||
children: [
|
currentStep++)
|
||||||
Flexible(
|
Column(
|
||||||
child: Center(
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: ListView(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
physics: const ClampingScrollPhysics(),
|
children: [
|
||||||
shrinkWrap: true,
|
if (widget.titleWidget != null) ...[
|
||||||
padding: const EdgeInsets.symmetric(
|
Expanded(
|
||||||
vertical: 8.0,
|
flex: widget.titleFlex ?? 1,
|
||||||
horizontal: 30.0,
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: widget.beforeTitleFlex ?? 2,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
widget.titleWidget!,
|
||||||
|
Expanded(
|
||||||
|
flex: widget.afterTitleFlex ?? 2,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
children: [
|
),
|
||||||
for (AuthField field in step.fields)
|
],
|
||||||
Align(
|
Expanded(
|
||||||
child: Column(
|
flex: widget.formFlex ?? 3,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Align(
|
||||||
children: [
|
alignment: Alignment.topCenter,
|
||||||
if (field.title != null) ...[
|
child: ConstrainedBox(
|
||||||
field.title!,
|
constraints: BoxConstraints(
|
||||||
],
|
maxWidth: widget.maxFormWidth ?? 300,
|
||||||
field.build(),
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
for (AuthField field
|
||||||
|
in widget.steps[currentStep].fields) ...[
|
||||||
|
if (field.title != null) ...[
|
||||||
|
wrapWithDefaultStyle(
|
||||||
|
style: theme.textTheme.headlineLarge!,
|
||||||
|
widget: field.title!,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
field.build(context, () {
|
||||||
)
|
_validate(currentStep);
|
||||||
],
|
}),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Column(
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 15.0,
|
|
||||||
bottom: 30.0,
|
|
||||||
left: 30.0,
|
|
||||||
right: 30.0,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: widget.steps.first != step
|
|
||||||
? MainAxisAlignment.spaceBetween
|
|
||||||
: MainAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
if (widget.steps.first != step)
|
SizedBox(
|
||||||
widget.previousButtonBuilder?.call(
|
width: MediaQuery.of(context).size.width,
|
||||||
onPrevious,
|
child: Padding(
|
||||||
widget.previousBtnTitle,
|
padding: const EdgeInsets.symmetric(
|
||||||
) ??
|
horizontal: 20,
|
||||||
ElevatedButton(
|
),
|
||||||
onPressed: onPrevious,
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: widget.steps.first !=
|
||||||
children: [
|
widget.steps[currentStep]
|
||||||
const Icon(
|
? MainAxisAlignment.spaceBetween
|
||||||
Icons.arrow_back,
|
: MainAxisAlignment.end,
|
||||||
size: 18,
|
children: [
|
||||||
),
|
if (widget.steps.first !=
|
||||||
Padding(
|
widget.steps[currentStep]) ...[
|
||||||
padding: const EdgeInsets.only(left: 4.0),
|
widget.previousButtonBuilder?.call(
|
||||||
child: Text(widget.previousBtnTitle),
|
onPrevious,
|
||||||
),
|
widget.previousBtnTitle,
|
||||||
],
|
currentStep,
|
||||||
),
|
) ??
|
||||||
),
|
_stepButton(
|
||||||
widget.nextButtonBuilder?.call(
|
buttonText: widget.previousBtnTitle,
|
||||||
() => onNext(step),
|
onTap: () async => onPrevious(),
|
||||||
widget.steps.last == step
|
),
|
||||||
? widget.submitBtnTitle
|
const SizedBox(
|
||||||
: widget.nextBtnTitle,
|
width: 8,
|
||||||
) ??
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
onNext(step);
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.steps.last == step
|
|
||||||
? widget.submitBtnTitle
|
|
||||||
: widget.nextBtnTitle,
|
|
||||||
),
|
|
||||||
const Padding(
|
|
||||||
padding: EdgeInsets.only(left: 4.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.arrow_forward,
|
|
||||||
size: 18,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
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);
|
||||||
}
|
}
|
||||||
|
|
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,167 +2,227 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "dart:async";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
|
||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
import "package:flutter_registration/src/config/example_registration_repository.dart";
|
||||||
|
|
||||||
|
/// A set of options for configuring the
|
||||||
|
/// registration process in a Flutter application.
|
||||||
class RegistrationOptions {
|
class RegistrationOptions {
|
||||||
|
/// Registration options Constructor
|
||||||
RegistrationOptions({
|
RegistrationOptions({
|
||||||
required this.registrationRepository,
|
|
||||||
required this.registrationSteps,
|
|
||||||
required this.afterRegistration,
|
required this.afterRegistration,
|
||||||
this.registrationTranslations = const RegistrationTranslations(),
|
this.registrationRepository,
|
||||||
|
this.registrationSteps,
|
||||||
|
this.titleFlex,
|
||||||
|
this.formFlex,
|
||||||
|
this.beforeTitleFlex,
|
||||||
|
this.afterTitleFlex,
|
||||||
|
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.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;
|
|
||||||
final void Function(String error)? onError;
|
/// 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;
|
||||||
|
|
||||||
|
/// 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(VoidCallback onPressed, String label)?
|
|
||||||
nextButtonBuilder;
|
/// A function for customizing the "Next" button.
|
||||||
final Widget Function(VoidCallback onPressed, String label)?
|
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)?
|
||||||
previousButtonBuilder;
|
previousButtonBuilder;
|
||||||
|
|
||||||
|
/// Specifies the alignment of buttons.
|
||||||
|
final MainAxisAlignment? buttonMainAxisAlignment;
|
||||||
|
|
||||||
|
/// The background color of the registration screen.
|
||||||
final Color? backgroundColor;
|
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 = const RegistrationTranslations(),
|
RegistrationTranslations translations =
|
||||||
|
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,
|
||||||
label: labelBuilder?.call(translations.defaultEmailLabel),
|
textFieldDecoration: InputDecoration(
|
||||||
hintText: translations.defaultEmailHint,
|
hintStyle: hintStyle,
|
||||||
textStyle: textStyle,
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
validators: [
|
label: labelBuilder?.call(translations.defaultEmailLabel),
|
||||||
(email) => (email == null || email.isEmpty)
|
hintText: translations.defaultEmailHint,
|
||||||
? translations.defaultEmailEmpty
|
border: const OutlineInputBorder(),
|
||||||
: null,
|
focusedBorder: const OutlineInputBorder(),
|
||||||
(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])+)\])""")
|
textStyle: textStyle,
|
||||||
.hasMatch(email!)
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
? null
|
validators: [
|
||||||
: translations.defaultEmailValidatorMessage,
|
// ignore: avoid_dynamic_calls
|
||||||
],
|
(email) => (email == null || email.isEmpty)
|
||||||
)
|
? translations.defaultEmailEmpty
|
||||||
],
|
: null,
|
||||||
),
|
(email) =>
|
||||||
AuthStep(
|
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])+)\])""")
|
||||||
fields: [
|
.hasMatch(email!)
|
||||||
AuthTextField(
|
? null
|
||||||
name: 'password1',
|
: translations.defaultEmailValidatorMessage,
|
||||||
textEditingController: pass1Controller,
|
],
|
||||||
title: titleBuilder?.call(
|
),
|
||||||
translations.defaultPassword1Title,
|
],
|
||||||
) ??
|
),
|
||||||
Padding(
|
AuthStep(
|
||||||
padding: const EdgeInsets.only(
|
fields: [
|
||||||
top: 24.0,
|
AuthPassField(
|
||||||
bottom: 12.0,
|
name: "password",
|
||||||
),
|
textEditingController: passController,
|
||||||
child: Text(
|
title: titleBuilder?.call(translations.defaultPasswordTitle) ??
|
||||||
translations.defaultPassword1Title,
|
Padding(
|
||||||
style: const TextStyle(
|
padding: const EdgeInsets.only(top: 180),
|
||||||
fontWeight: FontWeight.bold,
|
child: Text(
|
||||||
|
translations.defaultPasswordTitle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
textFieldDecoration: InputDecoration(
|
||||||
label: labelBuilder?.call(translations.defaultPassword1Label),
|
hintStyle: hintStyle,
|
||||||
hintText: translations.defaultPassword1Hint,
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
textStyle: textStyle,
|
label: labelBuilder?.call(translations.defaultPasswordLabel),
|
||||||
obscureText: true,
|
hintText: translations.defaultPasswordHint,
|
||||||
validators: [
|
border: const OutlineInputBorder(),
|
||||||
(value) => (value == null || value.isEmpty)
|
focusedBorder: const OutlineInputBorder(),
|
||||||
? translations.defaultPassword1ValidatorMessage
|
),
|
||||||
: null,
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
],
|
textStyle: textStyle,
|
||||||
onChange: (value) {
|
validators: [
|
||||||
password1 = value;
|
(value) {
|
||||||
},
|
// ignore: avoid_dynamic_calls
|
||||||
hidden: pass1Hidden,
|
if (value == null || value.isEmpty) {
|
||||||
onPassChanged: (value) {
|
return translations.defaultPasswordValidatorMessage;
|
||||||
passHideOnChange?.call(true, value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
AuthTextField(
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
label: labelBuilder?.call(translations.defaultPassword2Label),
|
|
||||||
hintText: translations.defaultPassword2Hint,
|
|
||||||
textStyle: textStyle,
|
|
||||||
obscureText: true,
|
|
||||||
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;
|
},
|
||||||
}
|
],
|
||||||
],
|
),
|
||||||
hidden: pass2Hidden,
|
],
|
||||||
onPassChanged: (value) {
|
),
|
||||||
passHideOnChange?.call(false, value);
|
];
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppBar _createCustomAppBar(String title) => AppBar(
|
||||||
|
iconTheme: const IconThemeData(color: Colors.black, size: 16),
|
||||||
|
title: Text(title),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
||||||
|
|
|
@ -2,44 +2,129 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
/// Holds all the translations for the standard elements
|
||||||
|
/// on the registration screen.
|
||||||
class RegistrationTranslations {
|
class RegistrationTranslations {
|
||||||
|
/// Constructs a [RegistrationTranslations] object.
|
||||||
const RegistrationTranslations({
|
const RegistrationTranslations({
|
||||||
this.title = 'Register',
|
required this.title,
|
||||||
this.registerBtn = 'Register',
|
required this.registerBtn,
|
||||||
this.previousStepBtn = 'Previous',
|
required this.previousStepBtn,
|
||||||
this.nextStepBtn = 'Next',
|
required this.nextStepBtn,
|
||||||
this.closeBtn = 'Close',
|
required this.closeBtn,
|
||||||
this.defaultEmailTitle = 'What is your email?',
|
required this.defaultEmailTitle,
|
||||||
this.defaultEmailLabel = '',
|
required this.defaultEmailLabel,
|
||||||
this.defaultEmailHint = 'john.doe@domain.com',
|
required this.defaultEmailHint,
|
||||||
this.defaultEmailEmpty = 'Enter your email',
|
required this.defaultEmailEmpty,
|
||||||
this.defaultEmailValidatorMessage = 'Enter a valid email address',
|
required this.defaultEmailValidatorMessage,
|
||||||
this.defaultPassword1Title = 'Enter a password',
|
required this.defaultPasswordTitle,
|
||||||
this.defaultPassword1Label = '',
|
required this.defaultPasswordLabel,
|
||||||
this.defaultPassword1Hint = '',
|
required this.defaultPasswordHint,
|
||||||
this.defaultPassword1ValidatorMessage = 'Enter a valid password',
|
required this.defaultPasswordValidatorMessage,
|
||||||
this.defaultPassword2Title = 'Re-enter password',
|
required this.defaultPasswordToShortValidatorMessage,
|
||||||
this.defaultPassword2Label = '',
|
|
||||||
this.defaultPassword2Hint = '',
|
|
||||||
this.defaultPassword2ValidatorMessage = 'Passwords have to be equal',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Constructs a [RegistrationTranslations] object with empty strings.
|
||||||
|
const RegistrationTranslations.empty()
|
||||||
|
: title = "",
|
||||||
|
registerBtn = "Register",
|
||||||
|
previousStepBtn = "Previous",
|
||||||
|
nextStepBtn = "Next",
|
||||||
|
closeBtn = "Close",
|
||||||
|
defaultEmailTitle = "enter your email address",
|
||||||
|
defaultEmailLabel = "",
|
||||||
|
defaultEmailHint = "Email address",
|
||||||
|
defaultEmailEmpty = "Please enter your email address.",
|
||||||
|
defaultEmailValidatorMessage = "Please enter a valid email address.",
|
||||||
|
defaultPasswordTitle = "choose a password",
|
||||||
|
defaultPasswordLabel = "",
|
||||||
|
defaultPasswordHint = "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;
|
||||||
final String defaultPassword1Title;
|
|
||||||
final String defaultPassword1Label;
|
/// The title for the default password field.
|
||||||
final String defaultPassword1Hint;
|
final String defaultPasswordTitle;
|
||||||
final String defaultPassword1ValidatorMessage;
|
|
||||||
final String defaultPassword2Title;
|
/// The label for the default password field.
|
||||||
final String defaultPassword2Label;
|
final String defaultPasswordLabel;
|
||||||
final String defaultPassword2Hint;
|
|
||||||
final String defaultPassword2ValidatorMessage;
|
/// 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({
|
||||||
|
String? title,
|
||||||
|
String? registerBtn,
|
||||||
|
String? previousStepBtn,
|
||||||
|
String? nextStepBtn,
|
||||||
|
String? closeBtn,
|
||||||
|
String? defaultEmailTitle,
|
||||||
|
String? defaultEmailLabel,
|
||||||
|
String? defaultEmailHint,
|
||||||
|
String? defaultEmailEmpty,
|
||||||
|
String? defaultEmailValidatorMessage,
|
||||||
|
String? defaultPasswordTitle,
|
||||||
|
String? defaultPasswordLabel,
|
||||||
|
String? defaultPasswordHint,
|
||||||
|
String? defaultPasswordValidatorMessage,
|
||||||
|
String? defaultPasswordToShortValidatorMessage,
|
||||||
|
}) =>
|
||||||
|
RegistrationTranslations(
|
||||||
|
title: title ?? this.title,
|
||||||
|
registerBtn: registerBtn ?? this.registerBtn,
|
||||||
|
previousStepBtn: previousStepBtn ?? this.previousStepBtn,
|
||||||
|
nextStepBtn: nextStepBtn ?? this.nextStepBtn,
|
||||||
|
closeBtn: closeBtn ?? this.closeBtn,
|
||||||
|
defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle,
|
||||||
|
defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel,
|
||||||
|
defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint,
|
||||||
|
defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty,
|
||||||
|
defaultEmailValidatorMessage:
|
||||||
|
defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage,
|
||||||
|
defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle,
|
||||||
|
defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel,
|
||||||
|
defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint,
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
78
lib/src/model/auth_bool_field.dart
Normal file
78
lib/src/model/auth_bool_field.dart
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_input_library/flutter_input_library.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
|
/// A field for capturing boolean values in a Flutter form.
|
||||||
|
///
|
||||||
|
/// 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({
|
||||||
|
required super.name,
|
||||||
|
required this.widgetType,
|
||||||
|
super.title,
|
||||||
|
super.validators = const [],
|
||||||
|
super.value = false,
|
||||||
|
this.leftWidget,
|
||||||
|
this.rightWidget,
|
||||||
|
this.onChange,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// A widget to be displayed on the left side of the boolean widget.
|
||||||
|
final Widget? leftWidget;
|
||||||
|
|
||||||
|
/// A widget to be displayed on the right side of the boolean widget.
|
||||||
|
final Widget? rightWidget;
|
||||||
|
|
||||||
|
/// The type of boolean widget to use.
|
||||||
|
final BoolWidgetType widgetType;
|
||||||
|
|
||||||
|
/// A callback function triggered when the value of the field changes.
|
||||||
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, Function onValueChanged) =>
|
||||||
|
FlutterFormInputBool(
|
||||||
|
widgetType: widgetType,
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
leftWidget: leftWidget,
|
||||||
|
rightWidget: rightWidget,
|
||||||
|
);
|
||||||
|
}
|
78
lib/src/model/auth_drop_down.dart
Normal file
78
lib/src/model/auth_drop_down.dart
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
|
/// A field for capturing dropdown selections in a Flutter form.
|
||||||
|
///
|
||||||
|
/// Extends [AuthField].
|
||||||
|
class AuthDropdownField extends AuthField {
|
||||||
|
/// Constructs an [AuthDropdownField] object.
|
||||||
|
AuthDropdownField({
|
||||||
|
required super.name,
|
||||||
|
required this.items,
|
||||||
|
required this.onChanged,
|
||||||
|
required super.value,
|
||||||
|
this.dropdownDecoration,
|
||||||
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
|
this.textStyle,
|
||||||
|
this.icon = const Icon(Icons.keyboard_arrow_down),
|
||||||
|
}) {
|
||||||
|
selectedValue = value ?? items.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of items for the dropdown.
|
||||||
|
final List<String> items;
|
||||||
|
|
||||||
|
/// A callback function triggered when the dropdown value changes.
|
||||||
|
final Function(String?) onChanged;
|
||||||
|
|
||||||
|
/// The currently selected value in the dropdown.
|
||||||
|
String? selectedValue;
|
||||||
|
|
||||||
|
/// The decoration for the dropdown.
|
||||||
|
final InputDecoration? dropdownDecoration;
|
||||||
|
|
||||||
|
/// The padding around the dropdown.
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
/// The text style for the dropdown.
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// The icon to be displayed with the dropdown.
|
||||||
|
final Icon icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, Function onValueChanged) => Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
icon: icon,
|
||||||
|
style: textStyle,
|
||||||
|
value: selectedValue,
|
||||||
|
decoration: dropdownDecoration,
|
||||||
|
items: items
|
||||||
|
.map(
|
||||||
|
(String value) => DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Text(value),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onChanged: (newValue) {
|
||||||
|
selectedValue = newValue;
|
||||||
|
onChanged(newValue);
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
onValueChanged();
|
||||||
|
},
|
||||||
|
validator: (value) {
|
||||||
|
if (validators.isNotEmpty) {
|
||||||
|
for (var validator in validators) {
|
||||||
|
var output = validator(value);
|
||||||
|
if (output != null) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,20 +2,53 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import "package:flutter/material.dart";
|
||||||
|
|
||||||
abstract class AuthField {
|
/// An abstract class representing a field in a Flutter form.
|
||||||
|
///
|
||||||
|
/// [T] represents the type of value stored in the field.
|
||||||
|
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,
|
||||||
|
this.onValueChanged,
|
||||||
this.title,
|
this.title,
|
||||||
this.validators = const [],
|
this.validators = const [],
|
||||||
this.value = '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The name of the field.
|
||||||
final String name;
|
final String name;
|
||||||
final Widget? title;
|
|
||||||
List<String? Function(String?)> validators;
|
|
||||||
String value;
|
|
||||||
|
|
||||||
Widget build();
|
/// The initial value of the field.
|
||||||
|
T value;
|
||||||
|
|
||||||
|
/// 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);
|
||||||
}
|
}
|
||||||
|
|
96
lib/src/model/auth_pass_field.dart
Normal file
96
lib/src/model/auth_pass_field.dart
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_input_library/flutter_input_library.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
|
||||||
|
/// A field for capturing password inputs in a Flutter form.
|
||||||
|
///
|
||||||
|
/// 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({
|
||||||
|
required super.name,
|
||||||
|
this.textEditingController,
|
||||||
|
super.title,
|
||||||
|
super.validators = const [],
|
||||||
|
super.value = "",
|
||||||
|
this.textStyle,
|
||||||
|
this.onChange,
|
||||||
|
this.iconSize,
|
||||||
|
this.textFieldDecoration,
|
||||||
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The text style for the password input.
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// The size of the icon displayed with the password input.
|
||||||
|
final double? iconSize;
|
||||||
|
|
||||||
|
/// A callback function triggered when the value of the field changes.
|
||||||
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
|
/// The decoration for the password input field.
|
||||||
|
final InputDecoration? textFieldDecoration;
|
||||||
|
|
||||||
|
/// The padding around the password input field.
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
/// The controller for the password input.
|
||||||
|
final TextEditingController? textEditingController;
|
||||||
|
|
||||||
|
@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,82 +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.obscureText = false,
|
|
||||||
this.hintText,
|
|
||||||
this.label,
|
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.onChange,
|
this.onChange,
|
||||||
this.hidden,
|
this.textFieldDecoration,
|
||||||
this.onPassChanged,
|
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;
|
||||||
final bool obscureText;
|
|
||||||
final String? hintText;
|
/// The text style for the text input.
|
||||||
final Widget? label;
|
|
||||||
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;
|
||||||
final bool? hidden;
|
|
||||||
final Function(bool value)? onPassChanged;
|
/// The decoration for the text input field.
|
||||||
|
final InputDecoration? textFieldDecoration;
|
||||||
|
|
||||||
|
/// The padding around the text input field.
|
||||||
|
final EdgeInsets padding;
|
||||||
|
|
||||||
|
/// The type of text input.
|
||||||
|
final TextInputType? textInputType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build() {
|
Widget build(BuildContext context, Function onValueChanged) => Padding(
|
||||||
Widget? suffix;
|
padding: padding,
|
||||||
|
child: TextFormField(
|
||||||
if (hidden != null) {
|
keyboardType: textInputType,
|
||||||
if (hidden!) {
|
style: textStyle,
|
||||||
suffix = GestureDetector(
|
decoration: textFieldDecoration,
|
||||||
onTap: () {
|
controller: textController,
|
||||||
onPassChanged?.call(!hidden!);
|
onChanged: (v) {
|
||||||
|
value = v;
|
||||||
|
onChange?.call(value);
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
onValueChanged();
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.visibility),
|
validator: (value) {
|
||||||
);
|
for (var validator in validators) {
|
||||||
} else {
|
var output = validator(value);
|
||||||
suffix = GestureDetector(
|
if (output != null) {
|
||||||
onTap: () {
|
return output;
|
||||||
onPassChanged?.call(!hidden!);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.visibility_off),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return TextFormField(
|
|
||||||
style: textStyle,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
label: label,
|
|
||||||
hintText: hintText,
|
|
||||||
suffix: suffix,
|
|
||||||
),
|
|
||||||
controller: textController,
|
|
||||||
obscureText: hidden ?? obscureText,
|
|
||||||
onChanged: (v) {
|
|
||||||
value = v;
|
|
||||||
onChange?.call(value);
|
|
||||||
},
|
|
||||||
validator: (value) {
|
|
||||||
for (var validator in validators) {
|
|
||||||
var output = validator(value);
|
|
||||||
if (output != null) {
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +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
|
||||||
required HashMap<String, String> values,
|
RegistrationScreenState createState() => RegistrationScreenState();
|
||||||
required VoidCallback onError,
|
}
|
||||||
|
|
||||||
|
/// The state for [RegistrationScreen].
|
||||||
|
class RegistrationScreenState extends State<RegistrationScreen> {
|
||||||
|
/// Registers the user.
|
||||||
|
Future<void> _register({
|
||||||
|
required HashMap<String, dynamic> values,
|
||||||
|
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 {
|
||||||
registrationOptions.onError?.call(registered);
|
var pageToReturn = widget.registrationOptions.onError?.call(registered);
|
||||||
onError();
|
|
||||||
|
onError(pageToReturn);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} on Exception catch (_) {
|
||||||
onError();
|
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,
|
||||||
title: 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:
|
||||||
|
widget.registrationOptions.buttonMainAxisAlignment,
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
15
pubspec.yaml
15
pubspec.yaml
|
@ -4,14 +4,20 @@
|
||||||
|
|
||||||
name: flutter_registration
|
name: flutter_registration
|
||||||
description: A Flutter Registration package
|
description: A Flutter Registration package
|
||||||
version: 1.0.0
|
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
|
||||||
|
|
||||||
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:
|
||||||
|
hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
|
||||||
|
version: ^3.6.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
|
@ -20,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