mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-19 05:23:43 +02:00
Compare commits
77 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 | ||
|
a9380b0eb7 | ||
|
a4cbbc5e74 | ||
|
63d4773034 | ||
|
e715b1192b | ||
|
caf68b6d8f | ||
|
38624ac637 | ||
|
74a65923de | ||
|
9da59f3d75 | ||
|
a4e39977ad | ||
|
e2f65cdde9 | ||
|
0ec09155f9 | ||
|
84d7519da6 | ||
|
67d4aa1c45 | ||
|
bcb9328c4a | ||
|
63a3223d0f | ||
|
1666185920 | ||
|
ac3c1e0d48 | ||
|
4ff212ad91 | ||
cd09b2cc56 | |||
|
4eaa584f17 | ||
|
154e7a80d1 | ||
|
3242002182 | ||
|
188a174cbd | ||
|
08532d368a | ||
|
6f7ec0b0e6 | ||
|
7f36ab8eca |
33 changed files with 1645 additions and 816 deletions
10
.github/dependabot.yaml
vendored
Normal file
10
.github/dependabot.yaml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "pub"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
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
|
32
.github/workflows/flutter.yml
vendored
Normal file
32
.github/workflows/flutter.yml
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- feature/*
|
||||||
|
- bugfix/*
|
||||||
|
- hotfix/*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/wrapper
|
||||||
|
/opt/hostedtoolcache/flutter
|
||||||
|
key: ${{ runner.OS }}-flutter-install-cache
|
||||||
|
- uses: subosito/flutter-action@v2
|
||||||
|
with:
|
||||||
|
channel: 'stable'
|
||||||
|
- name: Flutter pub get
|
||||||
|
run: flutter pub get
|
||||||
|
- name: Dart format
|
||||||
|
run: dart format -o none --set-exit-if-changed .
|
||||||
|
- name: Flutter analyze
|
||||||
|
run: flutter analyze
|
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
|
||||||
|
|
72
CHANGELOG.md
72
CHANGELOG.md
|
@ -3,12 +3,76 @@ 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
|
||||||
|
|
||||||
## 0.0.1
|
# 2.0.3
|
||||||
|
- feat: added default registrationOptions
|
||||||
|
|
||||||
* Initial version
|
# 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
|
||||||
|
|
||||||
|
- feat: Added an onError function.
|
||||||
|
- feat: Register function in RegistrationRepository can now return a nullable string. Works like a validator.
|
||||||
|
|
||||||
|
# 0.5.0
|
||||||
|
|
||||||
|
- feat: add customBackgroundColor to AuthScreen
|
||||||
|
- fix: fix linter
|
||||||
|
- fix: fix translations to English
|
||||||
|
|
||||||
|
# 0.4.0
|
||||||
|
|
||||||
|
- feat: Added the abilty to show and hide the passwords
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
|
- Added the abilty to set an initial value fot the default email field
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
- Added the abilty to add labels
|
||||||
|
- The default password step now includes two textfields
|
||||||
|
|
||||||
## 0.0.2
|
## 0.0.2
|
||||||
|
|
||||||
* Firebase integration
|
- Firebase integration
|
||||||
* Registration capabilities
|
- Registration capabilities
|
||||||
|
|
||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
- Initial version
|
||||||
|
|
9
LICENSE
Normal file
9
LICENSE
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Copyright (c) 2022 Iconica, All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
60
README.md
60
README.md
|
@ -1,33 +1,63 @@
|
||||||
[](https://github.com/Iconica-Development) [](URL TO GITHUB ACTIONS) [](https://github.com/tenhobi/effective_dart)
|
[](https://github.com/Iconica-Development) [](https://github.com/Iconica-Development/flutter_registration/actions/new) [](https://github.com/tenhobi/effective_dart)
|
||||||
|
|
||||||
Short description of what your package is, why you created it. What issues it fixes and how it works. Also mention the available platforms
|
# Flutter Registration
|
||||||
|
|
||||||
|
Flutter Registration is a package to easily implement
|
||||||
|
a registration flow in your app.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
What setup steps are neccesarry and why>
|
To use this package, add `flutter_registration` as a dependency in your pubspec.yaml file.
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>PLATFORM</summary>
|
|
||||||
|
|
||||||
specific platform steps
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
How can we use the package descibe the most common ways with examples in
|
To configure the registration plug-in use the ```RegistrationScreen``` widget.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
codeblocks
|
void main() {
|
||||||
|
runApp(
|
||||||
|
MaterialApp(
|
||||||
|
home: RegistrationScreen(
|
||||||
|
registrationOptions: RegistrationOptions(
|
||||||
|
registrationRepository: ExampleRegistrationRepository(),
|
||||||
|
registrationSteps: RegistrationOptions.defaultSteps,
|
||||||
|
afterRegistration: () {
|
||||||
|
debugPrint('Registered!');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You are required to provide your own RegistrationRepository, this can be done using the parameter ```registrationRepository``` within the RegistrationsOptions which can be assigned to the RegistrationScreen widget.
|
||||||
|
|
||||||
|
A RegistrationRepository is responsible for sending the provided user details (email address and password for example) to an API.
|
||||||
|
|
||||||
|
An example for creating a RegistrationRepository is specificied below:
|
||||||
|
```dart
|
||||||
|
class ExampleRegistrationRepository with RegistrationRepository {
|
||||||
|
@override
|
||||||
|
Future<bool> register(HashMap values) {
|
||||||
|
debugPrint('register: $values');
|
||||||
|
return Future.value(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [Example Code](example/lib/main.dart) for an example on how to use this package.
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
Please file any issues, bugs or feature request as an issue on our [GitHub](REPO URL) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
|
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_registration) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
|
||||||
|
|
||||||
## Want to contribute
|
## Want to contribute
|
||||||
|
|
||||||
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](URL TO PULL REQUEST TAB IN REPO).
|
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_registration/pulls).
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
This flutter_registration for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
This `flutter_registration` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
|
@ -1,218 +1,9 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Iconica
|
include: package:flutter_iconica_analysis/components_options.yaml
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# Possible to overwrite the rules from the package
|
||||||
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
|
||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
exclude:
|
||||||
todo: ignore
|
|
||||||
exclude: [lib/generated_plugin_registrant.dart]
|
|
||||||
linter:
|
|
||||||
# https://dart.dev/tools/linter-rules#lints
|
|
||||||
rules:
|
|
||||||
# error rules
|
|
||||||
always_use_package_imports: false
|
|
||||||
avoid_dynamic_calls: true
|
|
||||||
avoid_empty_else: true
|
|
||||||
avoid_print: true
|
|
||||||
avoid_relative_lib_imports: true
|
|
||||||
avoid_returning_null_for_future: true
|
|
||||||
avoid_slow_async_io: true
|
|
||||||
avoid_type_to_string: true
|
|
||||||
avoid_types_as_parameter_names: true
|
|
||||||
avoid_web_libraries_in_flutter: true
|
|
||||||
cancel_subscriptions: true
|
|
||||||
close_sinks: true
|
|
||||||
comment_references: false
|
|
||||||
control_flow_in_finally: true
|
|
||||||
diagnostic_describe_all_properties: false
|
|
||||||
empty_statements: true
|
|
||||||
hash_and_equals: true
|
|
||||||
invariant_booleans: true
|
|
||||||
iterable_contains_unrelated_type: true
|
|
||||||
list_remove_unrelated_type: true
|
|
||||||
literal_only_boolean_expressions: true
|
|
||||||
no_adjacent_strings_in_list: true
|
|
||||||
no_duplicate_case_values: true
|
|
||||||
no_logic_in_create_state: true
|
|
||||||
prefer_relative_imports: false
|
|
||||||
prefer_void_to_null: true
|
|
||||||
test_types_in_equals: true
|
|
||||||
throw_in_finally: true
|
|
||||||
unnecessary_statements: true
|
|
||||||
unrelated_type_equality_checks: true
|
|
||||||
unsafe_html: true
|
|
||||||
use_build_context_synchronously: true
|
|
||||||
use_key_in_widget_constructors: true
|
|
||||||
valid_regexps: true
|
|
||||||
# style rules
|
|
||||||
always_declare_return_types: true
|
|
||||||
always_put_control_body_on_new_line: true
|
|
||||||
always_put_required_named_parameters_first: true
|
|
||||||
always_require_non_null_named_parameters: true
|
|
||||||
always_specify_types: false
|
|
||||||
annotate_overrides: true
|
|
||||||
avoid_annotating_with_dynamic: false
|
|
||||||
avoid_bool_literals_in_conditional_expressions: true
|
|
||||||
avoid_catches_without_on_clauses: false
|
|
||||||
avoid_catching_errors: false
|
|
||||||
avoid_classes_with_only_static_members: true
|
|
||||||
avoid_double_and_int_checks: true
|
|
||||||
avoid_equals_and_hash_code_on_mutable_classes: false
|
|
||||||
avoid_escaping_inner_quotes: false
|
|
||||||
avoid_field_initializers_in_const_classes: true
|
|
||||||
avoid_final_parameters: true
|
|
||||||
avoid_function_literals_in_foreach_calls: true
|
|
||||||
avoid_implementing_value_types: true
|
|
||||||
avoid_init_to_null: true
|
|
||||||
avoid_js_rounded_ints: true
|
|
||||||
avoid_multiple_declarations_per_line: true
|
|
||||||
avoid_null_checks_in_equality_operators: true
|
|
||||||
avoid_positional_boolean_parameters: true
|
|
||||||
avoid_private_typedef_functions: true
|
|
||||||
avoid_redundant_argument_values: false
|
|
||||||
avoid_renaming_method_parameters: true
|
|
||||||
avoid_return_types_on_setters: true
|
|
||||||
avoid_returning_null: true
|
|
||||||
avoid_returning_null_for_void: true
|
|
||||||
avoid_returning_this: true
|
|
||||||
avoid_setters_without_getters: true
|
|
||||||
avoid_shadowing_type_parameters: true
|
|
||||||
avoid_single_cascade_in_expression_statements: true
|
|
||||||
avoid_types_on_closure_parameters: false
|
|
||||||
avoid_unnecessary_containers: false
|
|
||||||
avoid_unused_constructor_parameters: true
|
|
||||||
avoid_void_async: true
|
|
||||||
await_only_futures: true
|
|
||||||
camel_case_extensions: true
|
|
||||||
camel_case_types: true
|
|
||||||
cascade_invocations: true
|
|
||||||
cast_nullable_to_non_nullable: true
|
|
||||||
conditional_uri_does_not_exist: true
|
|
||||||
constant_identifier_names: true
|
|
||||||
curly_braces_in_flow_control_structures: true
|
|
||||||
deprecated_consistency: true
|
|
||||||
directives_ordering: true
|
|
||||||
do_not_use_environment: true
|
|
||||||
empty_catches: true
|
|
||||||
empty_constructor_bodies: true
|
|
||||||
eol_at_end_of_file: true
|
|
||||||
exhaustive_cases: true
|
|
||||||
file_names: true
|
|
||||||
flutter_style_todos: true
|
|
||||||
implementation_imports: true
|
|
||||||
join_return_with_assignment: true
|
|
||||||
leading_newlines_in_multiline_strings: true
|
|
||||||
library_names: true
|
|
||||||
library_prefixes: true
|
|
||||||
library_private_types_in_public_api: true
|
|
||||||
lines_longer_than_80_chars: true
|
|
||||||
missing_whitespace_between_adjacent_strings: true
|
|
||||||
no_default_cases: true
|
|
||||||
no_leading_underscores_for_library_prefixes: true
|
|
||||||
no_leading_underscores_for_local_identifiers: true
|
|
||||||
no_runtimeType_toString: true
|
|
||||||
non_constant_identifier_names: true
|
|
||||||
noop_primitive_operations: true
|
|
||||||
null_check_on_nullable_type_parameter: true
|
|
||||||
null_closures: true
|
|
||||||
omit_local_variable_types: true
|
|
||||||
one_member_abstracts: true
|
|
||||||
only_throw_errors: true
|
|
||||||
overridden_fields: true
|
|
||||||
package_api_docs: true
|
|
||||||
package_prefixed_library_names: true
|
|
||||||
parameter_assignments: true
|
|
||||||
prefer_adjacent_string_concatenation: true
|
|
||||||
prefer_asserts_in_initializer_lists: true
|
|
||||||
prefer_asserts_with_message: true
|
|
||||||
prefer_collection_literals: true
|
|
||||||
prefer_conditional_assignment: true
|
|
||||||
prefer_const_constructors: true
|
|
||||||
prefer_const_constructors_in_immutables: true
|
|
||||||
prefer_const_declarations: false
|
|
||||||
prefer_const_literals_to_create_immutables: false
|
|
||||||
prefer_constructors_over_static_methods: true
|
|
||||||
prefer_contains: true
|
|
||||||
prefer_double_quotes: false
|
|
||||||
prefer_equal_for_default_values: true
|
|
||||||
prefer_expression_function_bodies: false
|
|
||||||
prefer_final_fields: true
|
|
||||||
prefer_final_in_for_each: false
|
|
||||||
prefer_final_locals: false
|
|
||||||
prefer_final_parameters: false
|
|
||||||
prefer_for_elements_to_map_fromIterable: true
|
|
||||||
prefer_foreach: true
|
|
||||||
prefer_function_declarations_over_variables: true
|
|
||||||
prefer_generic_function_type_aliases: true
|
|
||||||
prefer_if_elements_to_conditional_expressions: true
|
|
||||||
prefer_if_null_operators: true
|
|
||||||
prefer_initializing_formals: true
|
|
||||||
prefer_inlined_adds: true
|
|
||||||
prefer_int_literals: false
|
|
||||||
prefer_interpolation_to_compose_strings: true
|
|
||||||
prefer_is_empty: true
|
|
||||||
prefer_is_not_empty: true
|
|
||||||
prefer_is_not_operator: true
|
|
||||||
prefer_iterable_whereType: true
|
|
||||||
prefer_mixin: true
|
|
||||||
prefer_null_aware_method_calls: true
|
|
||||||
prefer_null_aware_operators: true
|
|
||||||
prefer_single_quotes: true
|
|
||||||
prefer_spread_collections: true
|
|
||||||
prefer_typing_uninitialized_variables: true
|
|
||||||
provide_deprecation_message: true
|
|
||||||
public_member_api_docs: false
|
|
||||||
recursive_getters: true
|
|
||||||
require_trailing_commas: true
|
|
||||||
sized_box_for_whitespace: true
|
|
||||||
sized_box_shrink_expand: true
|
|
||||||
slash_for_doc_comments: true
|
|
||||||
sort_child_properties_last: true
|
|
||||||
sort_constructors_first: true
|
|
||||||
sort_unnamed_constructors_first: true
|
|
||||||
tighten_type_of_initializing_formals: true
|
|
||||||
type_annotate_public_apis: true
|
|
||||||
type_init_formals: true
|
|
||||||
unawaited_futures: true
|
|
||||||
unnecessary_await_in_return: true
|
|
||||||
unnecessary_brace_in_string_interps: true
|
|
||||||
unnecessary_const: false
|
|
||||||
unnecessary_constructor_name: true
|
|
||||||
unnecessary_final: true
|
|
||||||
unnecessary_getters_setters: true
|
|
||||||
unnecessary_lambdas: true
|
|
||||||
unnecessary_late: true
|
|
||||||
unnecessary_new: true
|
|
||||||
unnecessary_null_aware_assignments: true
|
|
||||||
unnecessary_null_checks: true
|
|
||||||
unnecessary_null_in_if_null_operators: true
|
|
||||||
unnecessary_nullable_for_final_variable_declarations: true
|
|
||||||
unnecessary_overrides: true
|
|
||||||
unnecessary_parenthesis: true
|
|
||||||
unnecessary_raw_strings: true
|
|
||||||
unnecessary_string_escapes: true
|
|
||||||
unnecessary_string_interpolations: true
|
|
||||||
unnecessary_this: true
|
|
||||||
use_decorated_box: true
|
|
||||||
use_full_hex_values_for_flutter_colors: true
|
|
||||||
use_function_type_syntax_for_parameters: true
|
|
||||||
use_if_null_to_convert_nulls_to_bools: true
|
|
||||||
use_is_even_rather_than_modulo: true
|
|
||||||
use_late_for_private_fields_and_variables: true
|
|
||||||
use_named_constants: true
|
|
||||||
use_raw_strings: false
|
|
||||||
use_rethrow_when_possible: true
|
|
||||||
use_setters_to_change_properties: true
|
|
||||||
use_string_buffers: true
|
|
||||||
use_test_throws_matchers: true
|
|
||||||
use_to_and_as_if_applicable: true
|
|
||||||
void_checks: true
|
|
||||||
# pub rules
|
|
||||||
depend_on_referenced_packages: true
|
|
||||||
lowercase_with_underscores: true
|
|
||||||
secure_pubspec_urls: false
|
|
||||||
sort_pub_dependencies: false
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
linter:
|
||||||
# https://dart.dev/guides/language/analysis-options
|
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
|
|
|
@ -1,218 +1,4 @@
|
||||||
# SPDX-FileCopyrightText: 2022 Iconica
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
analyzer:
|
|
||||||
errors:
|
|
||||||
todo: ignore
|
|
||||||
exclude: [lib/generated_plugin_registrant.dart]
|
|
||||||
linter:
|
|
||||||
# https://dart.dev/tools/linter-rules#lints
|
|
||||||
rules:
|
|
||||||
# error rules
|
|
||||||
always_use_package_imports: false
|
|
||||||
avoid_dynamic_calls: true
|
|
||||||
avoid_empty_else: true
|
|
||||||
avoid_print: true
|
|
||||||
avoid_relative_lib_imports: true
|
|
||||||
avoid_returning_null_for_future: true
|
|
||||||
avoid_slow_async_io: true
|
|
||||||
avoid_type_to_string: true
|
|
||||||
avoid_types_as_parameter_names: true
|
|
||||||
avoid_web_libraries_in_flutter: true
|
|
||||||
cancel_subscriptions: true
|
|
||||||
close_sinks: true
|
|
||||||
comment_references: false
|
|
||||||
control_flow_in_finally: true
|
|
||||||
diagnostic_describe_all_properties: false
|
|
||||||
empty_statements: true
|
|
||||||
hash_and_equals: true
|
|
||||||
invariant_booleans: true
|
|
||||||
iterable_contains_unrelated_type: true
|
|
||||||
list_remove_unrelated_type: true
|
|
||||||
literal_only_boolean_expressions: true
|
|
||||||
no_adjacent_strings_in_list: true
|
|
||||||
no_duplicate_case_values: true
|
|
||||||
no_logic_in_create_state: true
|
|
||||||
prefer_relative_imports: false
|
|
||||||
prefer_void_to_null: true
|
|
||||||
test_types_in_equals: true
|
|
||||||
throw_in_finally: true
|
|
||||||
unnecessary_statements: true
|
|
||||||
unrelated_type_equality_checks: true
|
|
||||||
unsafe_html: true
|
|
||||||
use_build_context_synchronously: true
|
|
||||||
use_key_in_widget_constructors: true
|
|
||||||
valid_regexps: true
|
|
||||||
# style rules
|
|
||||||
always_declare_return_types: true
|
|
||||||
always_put_control_body_on_new_line: true
|
|
||||||
always_put_required_named_parameters_first: true
|
|
||||||
always_require_non_null_named_parameters: true
|
|
||||||
always_specify_types: false
|
|
||||||
annotate_overrides: true
|
|
||||||
avoid_annotating_with_dynamic: false
|
|
||||||
avoid_bool_literals_in_conditional_expressions: true
|
|
||||||
avoid_catches_without_on_clauses: false
|
|
||||||
avoid_catching_errors: false
|
|
||||||
avoid_classes_with_only_static_members: true
|
|
||||||
avoid_double_and_int_checks: true
|
|
||||||
avoid_equals_and_hash_code_on_mutable_classes: false
|
|
||||||
avoid_escaping_inner_quotes: false
|
|
||||||
avoid_field_initializers_in_const_classes: true
|
|
||||||
avoid_final_parameters: true
|
|
||||||
avoid_function_literals_in_foreach_calls: true
|
|
||||||
avoid_implementing_value_types: true
|
|
||||||
avoid_init_to_null: true
|
|
||||||
avoid_js_rounded_ints: true
|
|
||||||
avoid_multiple_declarations_per_line: true
|
|
||||||
avoid_null_checks_in_equality_operators: true
|
|
||||||
avoid_positional_boolean_parameters: true
|
|
||||||
avoid_private_typedef_functions: true
|
|
||||||
avoid_redundant_argument_values: false
|
|
||||||
avoid_renaming_method_parameters: true
|
|
||||||
avoid_return_types_on_setters: true
|
|
||||||
avoid_returning_null: true
|
|
||||||
avoid_returning_null_for_void: true
|
|
||||||
avoid_returning_this: true
|
|
||||||
avoid_setters_without_getters: true
|
|
||||||
avoid_shadowing_type_parameters: true
|
|
||||||
avoid_single_cascade_in_expression_statements: true
|
|
||||||
avoid_types_on_closure_parameters: false
|
|
||||||
avoid_unnecessary_containers: false
|
|
||||||
avoid_unused_constructor_parameters: true
|
|
||||||
avoid_void_async: true
|
|
||||||
await_only_futures: true
|
|
||||||
camel_case_extensions: true
|
|
||||||
camel_case_types: true
|
|
||||||
cascade_invocations: true
|
|
||||||
cast_nullable_to_non_nullable: true
|
|
||||||
conditional_uri_does_not_exist: true
|
|
||||||
constant_identifier_names: true
|
|
||||||
curly_braces_in_flow_control_structures: true
|
|
||||||
deprecated_consistency: true
|
|
||||||
directives_ordering: true
|
|
||||||
do_not_use_environment: true
|
|
||||||
empty_catches: true
|
|
||||||
empty_constructor_bodies: true
|
|
||||||
eol_at_end_of_file: true
|
|
||||||
exhaustive_cases: true
|
|
||||||
file_names: true
|
|
||||||
flutter_style_todos: true
|
|
||||||
implementation_imports: true
|
|
||||||
join_return_with_assignment: true
|
|
||||||
leading_newlines_in_multiline_strings: true
|
|
||||||
library_names: true
|
|
||||||
library_prefixes: true
|
|
||||||
library_private_types_in_public_api: true
|
|
||||||
lines_longer_than_80_chars: true
|
|
||||||
missing_whitespace_between_adjacent_strings: true
|
|
||||||
no_default_cases: true
|
|
||||||
no_leading_underscores_for_library_prefixes: true
|
|
||||||
no_leading_underscores_for_local_identifiers: true
|
|
||||||
no_runtimeType_toString: true
|
|
||||||
non_constant_identifier_names: true
|
|
||||||
noop_primitive_operations: true
|
|
||||||
null_check_on_nullable_type_parameter: true
|
|
||||||
null_closures: true
|
|
||||||
omit_local_variable_types: true
|
|
||||||
one_member_abstracts: true
|
|
||||||
only_throw_errors: true
|
|
||||||
overridden_fields: true
|
|
||||||
package_api_docs: true
|
|
||||||
package_prefixed_library_names: true
|
|
||||||
parameter_assignments: true
|
|
||||||
prefer_adjacent_string_concatenation: true
|
|
||||||
prefer_asserts_in_initializer_lists: true
|
|
||||||
prefer_asserts_with_message: true
|
|
||||||
prefer_collection_literals: true
|
|
||||||
prefer_conditional_assignment: true
|
|
||||||
prefer_const_constructors: true
|
|
||||||
prefer_const_constructors_in_immutables: true
|
|
||||||
prefer_const_declarations: false
|
|
||||||
prefer_const_literals_to_create_immutables: false
|
|
||||||
prefer_constructors_over_static_methods: true
|
|
||||||
prefer_contains: true
|
|
||||||
prefer_double_quotes: false
|
|
||||||
prefer_equal_for_default_values: true
|
|
||||||
prefer_expression_function_bodies: false
|
|
||||||
prefer_final_fields: true
|
|
||||||
prefer_final_in_for_each: false
|
|
||||||
prefer_final_locals: false
|
|
||||||
prefer_final_parameters: false
|
|
||||||
prefer_for_elements_to_map_fromIterable: true
|
|
||||||
prefer_foreach: true
|
|
||||||
prefer_function_declarations_over_variables: true
|
|
||||||
prefer_generic_function_type_aliases: true
|
|
||||||
prefer_if_elements_to_conditional_expressions: true
|
|
||||||
prefer_if_null_operators: true
|
|
||||||
prefer_initializing_formals: true
|
|
||||||
prefer_inlined_adds: true
|
|
||||||
prefer_int_literals: false
|
|
||||||
prefer_interpolation_to_compose_strings: true
|
|
||||||
prefer_is_empty: true
|
|
||||||
prefer_is_not_empty: true
|
|
||||||
prefer_is_not_operator: true
|
|
||||||
prefer_iterable_whereType: true
|
|
||||||
prefer_mixin: true
|
|
||||||
prefer_null_aware_method_calls: true
|
|
||||||
prefer_null_aware_operators: true
|
|
||||||
prefer_single_quotes: true
|
|
||||||
prefer_spread_collections: true
|
|
||||||
prefer_typing_uninitialized_variables: true
|
|
||||||
provide_deprecation_message: true
|
|
||||||
public_member_api_docs: false
|
|
||||||
recursive_getters: true
|
|
||||||
require_trailing_commas: true
|
|
||||||
sized_box_for_whitespace: true
|
|
||||||
sized_box_shrink_expand: true
|
|
||||||
slash_for_doc_comments: true
|
|
||||||
sort_child_properties_last: true
|
|
||||||
sort_constructors_first: true
|
|
||||||
sort_unnamed_constructors_first: true
|
|
||||||
tighten_type_of_initializing_formals: true
|
|
||||||
type_annotate_public_apis: true
|
|
||||||
type_init_formals: true
|
|
||||||
unawaited_futures: true
|
|
||||||
unnecessary_await_in_return: true
|
|
||||||
unnecessary_brace_in_string_interps: true
|
|
||||||
unnecessary_const: false
|
|
||||||
unnecessary_constructor_name: true
|
|
||||||
unnecessary_final: true
|
|
||||||
unnecessary_getters_setters: true
|
|
||||||
unnecessary_lambdas: true
|
|
||||||
unnecessary_late: true
|
|
||||||
unnecessary_new: true
|
|
||||||
unnecessary_null_aware_assignments: true
|
|
||||||
unnecessary_null_checks: true
|
|
||||||
unnecessary_null_in_if_null_operators: true
|
|
||||||
unnecessary_nullable_for_final_variable_declarations: true
|
|
||||||
unnecessary_overrides: true
|
|
||||||
unnecessary_parenthesis: true
|
|
||||||
unnecessary_raw_strings: true
|
|
||||||
unnecessary_string_escapes: true
|
|
||||||
unnecessary_string_interpolations: true
|
|
||||||
unnecessary_this: true
|
|
||||||
use_decorated_box: true
|
|
||||||
use_full_hex_values_for_flutter_colors: true
|
|
||||||
use_function_type_syntax_for_parameters: true
|
|
||||||
use_if_null_to_convert_nulls_to_bools: true
|
|
||||||
use_is_even_rather_than_modulo: true
|
|
||||||
use_late_for_private_fields_and_variables: true
|
|
||||||
use_named_constants: true
|
|
||||||
use_raw_strings: false
|
|
||||||
use_rethrow_when_possible: true
|
|
||||||
use_setters_to_change_properties: true
|
|
||||||
use_string_buffers: true
|
|
||||||
use_test_throws_matchers: true
|
|
||||||
use_to_and_as_if_applicable: true
|
|
||||||
void_checks: true
|
|
||||||
# pub rules
|
|
||||||
depend_on_referenced_packages: true
|
|
||||||
lowercase_with_underscores: true
|
|
||||||
secure_pubspec_urls: false
|
|
||||||
sort_pub_dependencies: false
|
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Uncomment this line to define a global platform for your project
|
# Uncomment this line to define a global platform for your project
|
||||||
# platform :ios, '11.0'
|
platform :ios, '11.0'
|
||||||
|
|
||||||
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -5,7 +9,7 @@ import 'package:flutter_registration/flutter_registration.dart';
|
||||||
|
|
||||||
class ExampleRegistrationRepository with RegistrationRepository {
|
class ExampleRegistrationRepository with RegistrationRepository {
|
||||||
@override
|
@override
|
||||||
Future<void> register(HashMap values) {
|
Future<String?> register(HashMap values) {
|
||||||
debugPrint('register: $values');
|
debugPrint('register: $values');
|
||||||
return Future.value(null);
|
return Future.value(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,92 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// 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(
|
||||||
afterRegistration: () {
|
registrationOptions: RegistrationOptions(
|
||||||
debugPrint('Registered!');
|
registrationRepository: ExampleRegistrationRepository(),
|
||||||
},
|
registrationSteps: steps,
|
||||||
repository: ExampleRegistrationRepository(),
|
afterRegistration: () {
|
||||||
|
debugPrint('Registered!');
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
|
|
|
@ -5,49 +5,56 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.9.0"
|
version: "2.11.0"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.3.0"
|
||||||
clock:
|
clock:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.18.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
url: "https://pub.dartlang.org"
|
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||||
|
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:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
flutter:
|
flutter:
|
||||||
|
@ -55,20 +62,22 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_hooks:
|
flutter_input_library:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flutter_hooks
|
name: flutter_input_library
|
||||||
url: "https://pub.dartlang.org"
|
sha256: db8d9d57c31f166ed7c4ec3d060d18891533c57877a30c6c2668231efa1a44f8
|
||||||
|
url: "https://forgejo.internal.iconica.nl/api/packages/internal/pub/"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.5+1"
|
version: "3.6.0"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
url: "https://pub.dartlang.org"
|
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||||
|
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
|
||||||
|
@ -80,7 +89,7 @@ packages:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.0.1"
|
version: "2.0.4"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -90,44 +99,74 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: intl
|
name: intl
|
||||||
url: "https://pub.dartlang.org"
|
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.17.0"
|
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
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||||
|
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
|
||||||
url: "https://pub.dartlang.org"
|
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.12"
|
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
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.5"
|
version: "0.8.0"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.12.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.9.0"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -137,51 +176,66 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.10.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
version: "1.11.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
url: "https://pub.dartlang.org"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.2.0"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.12"
|
version: "0.7.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.4"
|
||||||
|
vm_service:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vm_service
|
||||||
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "14.2.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.0 <3.0.0"
|
dart: ">=3.3.0 <4.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.18.0-18.0.pre.54"
|
||||||
|
|
BIN
flutter_registration.gif
Normal file
BIN
flutter_registration.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 692 KiB |
|
@ -1,8 +1,21 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// 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/model/auth_exception.dart';
|
export "package:flutter_input_library/flutter_input_library.dart"
|
||||||
export 'src/model/auth_field.dart';
|
show BoolWidgetType;
|
||||||
export 'src/model/auth_step.dart';
|
|
||||||
export 'src/model/auth_text_field.dart';
|
export "src/config/registration_options.dart";
|
||||||
export 'src/registration_screen.dart';
|
export "src/config/registration_translations.dart";
|
||||||
export 'src/service/registration_repository.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";
|
||||||
|
|
|
@ -1,128 +1,421 @@
|
||||||
import 'dart:collection';
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
import 'package:flutter/material.dart';
|
//
|
||||||
import 'package:flutter_registration/src/model/auth_action.dart';
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
import 'package:flutter_registration/src/model/auth_field.dart';
|
|
||||||
import 'package:flutter_registration/src/model/auth_step.dart';
|
|
||||||
|
|
||||||
|
import "dart:async";
|
||||||
|
import "dart:collection";
|
||||||
|
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.previousBtnTitle,
|
||||||
required this.onFinish,
|
required this.onFinish,
|
||||||
this.actions,
|
this.customAppBar,
|
||||||
|
this.buttonMainAxisAlignment,
|
||||||
|
this.customBackgroundColor,
|
||||||
|
this.nextButtonBuilder,
|
||||||
|
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(HashMap<String, String>) onFinish;
|
final String appBarTitle;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
final List<AuthAction>? actions;
|
|
||||||
|
/// The title of the next button.
|
||||||
|
final String nextBtnTitle;
|
||||||
|
|
||||||
|
/// The title of the previous button.
|
||||||
|
final String previousBtnTitle;
|
||||||
|
|
||||||
|
/// A custom app bar widget.
|
||||||
|
final AppBar? customAppBar;
|
||||||
|
|
||||||
|
/// The alignment of the buttons.
|
||||||
|
final MainAxisAlignment? buttonMainAxisAlignment;
|
||||||
|
|
||||||
|
/// The background color of the screen.
|
||||||
|
final Color? customBackgroundColor;
|
||||||
|
|
||||||
|
/// 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> {
|
||||||
int _index = 0;
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
final _pageController = PageController();
|
||||||
|
final _animationDuration = const Duration(milliseconds: 300);
|
||||||
|
final _animationCurve = Curves.ease;
|
||||||
|
bool _formValid = false;
|
||||||
|
|
||||||
|
/// Gets the app bar.
|
||||||
|
AppBar get _appBar =>
|
||||||
|
widget.customAppBar ??
|
||||||
|
AppBar(
|
||||||
|
backgroundColor: const Color(0xffFAF9F6),
|
||||||
|
title: Text(widget.appBarTitle),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Handles previous button press.
|
||||||
|
void onPrevious() {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
_validate(_pageController.page!.toInt() - 1);
|
||||||
|
unawaited(
|
||||||
|
_pageController.previousPage(
|
||||||
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles next button press.
|
||||||
|
Future<void> onNext(AuthStep step) async {
|
||||||
|
if (!_formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_formKey.currentState!.save();
|
||||||
|
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
|
if (widget.steps.last == step) {
|
||||||
|
var values = HashMap<String, dynamic>();
|
||||||
|
|
||||||
|
for (var step in widget.steps) {
|
||||||
|
for (var field in step.fields) {
|
||||||
|
values[field.name] = field.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await widget.onFinish(
|
||||||
|
values: values,
|
||||||
|
onError: (int? pageToReturn) {
|
||||||
|
if (pageToReturn == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_pageController.animateToPage(
|
||||||
|
pageToReturn,
|
||||||
|
duration: _animationDuration,
|
||||||
|
curve: _animationCurve,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_validate(_pageController.page!.toInt() + 1);
|
||||||
|
unawaited(
|
||||||
|
_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: Theme.of(context).backgroundColor,
|
backgroundColor: widget.customBackgroundColor ?? const Color(0xffFAF9F6),
|
||||||
appBar: AppBar(
|
appBar: _appBar,
|
||||||
title: Text(widget.title),
|
body: SafeArea(
|
||||||
),
|
child: Form(
|
||||||
body: Form(
|
key: _formKey,
|
||||||
key: _formKey,
|
child: PageView(
|
||||||
child: Column(
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
controller: _pageController,
|
||||||
children: [
|
children: <Widget>[
|
||||||
const Spacer(),
|
for (var currentStep = 0;
|
||||||
Padding(
|
currentStep < widget.steps.length;
|
||||||
padding: const EdgeInsets.symmetric(
|
currentStep++)
|
||||||
vertical: 8.0,
|
Column(
|
||||||
horizontal: 30.0,
|
mainAxisSize: MainAxisSize.min,
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
if (widget.titleWidget != null) ...[
|
||||||
Column(
|
Expanded(
|
||||||
children: [
|
flex: widget.titleFlex ?? 1,
|
||||||
for (AuthField field in widget.steps[_index].fields)
|
child: Column(
|
||||||
Column(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Expanded(
|
||||||
padding: const EdgeInsets.only(
|
flex: widget.beforeTitleFlex ?? 2,
|
||||||
top: 24.0,
|
child: Container(),
|
||||||
bottom: 12.0,
|
),
|
||||||
),
|
widget.titleWidget!,
|
||||||
child: Text(
|
Expanded(
|
||||||
field.title,
|
flex: widget.afterTitleFlex ?? 2,
|
||||||
style: const TextStyle(
|
child: Container(),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
field.build(),
|
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
)
|
],
|
||||||
],
|
Expanded(
|
||||||
),
|
flex: widget.formFlex ?? 3,
|
||||||
),
|
child: Align(
|
||||||
const Spacer(),
|
alignment: Alignment.topCenter,
|
||||||
Padding(
|
child: ConstrainedBox(
|
||||||
padding: const EdgeInsets.all(30.0),
|
constraints: BoxConstraints(
|
||||||
child: Column(
|
maxWidth: widget.maxFormWidth ?? 300,
|
||||||
children: [
|
),
|
||||||
ElevatedButton(
|
child: Column(
|
||||||
style: ElevatedButton.styleFrom(
|
children: [
|
||||||
minimumSize: const Size.fromHeight(50),
|
for (AuthField field
|
||||||
),
|
in widget.steps[currentStep].fields) ...[
|
||||||
onPressed: () {
|
if (field.title != null) ...[
|
||||||
if (!_formKey.currentState!.validate()) {
|
wrapWithDefaultStyle(
|
||||||
return;
|
style: theme.textTheme.headlineLarge!,
|
||||||
}
|
widget: field.title!,
|
||||||
if (widget.steps.length > _index + 1) {
|
),
|
||||||
setState(() {
|
],
|
||||||
_index++;
|
field.build(context, () {
|
||||||
});
|
_validate(currentStep);
|
||||||
} else {
|
}),
|
||||||
var values = HashMap<String, String>();
|
],
|
||||||
|
],
|
||||||
for (var step in widget.steps) {
|
),
|
||||||
for (var field in step.fields) {
|
),
|
||||||
values[field.name] = field.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
widget.onFinish(values);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
widget.steps.length > _index + 1
|
|
||||||
? 'Volgende stap'
|
|
||||||
: widget.submitBtnTitle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
for (AuthAction action in widget.actions ?? [])
|
|
||||||
TextButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
minimumSize: const Size.fromHeight(40),
|
|
||||||
),
|
),
|
||||||
onPressed: action.onPress,
|
|
||||||
child: Text(action.title),
|
|
||||||
),
|
),
|
||||||
],
|
Column(
|
||||||
),
|
children: [
|
||||||
)
|
SizedBox(
|
||||||
],
|
width: MediaQuery.of(context).size.width,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 20,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: widget.steps.first !=
|
||||||
|
widget.steps[currentStep]
|
||||||
|
? MainAxisAlignment.spaceBetween
|
||||||
|
: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
if (widget.steps.first !=
|
||||||
|
widget.steps[currentStep]) ...[
|
||||||
|
widget.previousButtonBuilder?.call(
|
||||||
|
onPrevious,
|
||||||
|
widget.previousBtnTitle,
|
||||||
|
currentStep,
|
||||||
|
) ??
|
||||||
|
_stepButton(
|
||||||
|
buttonText: widget.previousBtnTitle,
|
||||||
|
onTap: () async => onPrevious(),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 8,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
widget.nextButtonBuilder?.call(
|
||||||
|
!_formValid
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
await onNext(
|
||||||
|
widget.steps[currentStep],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
widget.steps.last ==
|
||||||
|
widget.steps[currentStep]
|
||||||
|
? widget.submitBtnTitle
|
||||||
|
: widget.nextBtnTitle,
|
||||||
|
currentStep,
|
||||||
|
_formValid,
|
||||||
|
) ??
|
||||||
|
_stepButton(
|
||||||
|
buttonText: widget.steps.last ==
|
||||||
|
widget.steps[currentStep]
|
||||||
|
? widget.submitBtnTitle
|
||||||
|
: widget.nextBtnTitle,
|
||||||
|
onTap: () async {
|
||||||
|
await onNext(
|
||||||
|
widget.steps[currentStep],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
if (widget.loginButton != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 20.0),
|
||||||
|
child: widget.loginButton,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _stepButton({
|
||||||
|
required String buttonText,
|
||||||
|
required Future Function()? onTap,
|
||||||
|
}) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
return Flexible(
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 180,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(
|
||||||
|
0xff979797,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: Text(
|
||||||
|
buttonText,
|
||||||
|
style: theme.textTheme.bodyMedium,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget wrapWithDefaultStyle({
|
||||||
|
required Widget widget,
|
||||||
|
required TextStyle style,
|
||||||
|
}) =>
|
||||||
|
DefaultTextStyle(style: style, child: widget);
|
||||||
}
|
}
|
||||||
|
|
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);
|
||||||
|
}
|
||||||
|
}
|
228
lib/src/config/registration_options.dart
Normal file
228
lib/src/config/registration_options.dart
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import "dart:async";
|
||||||
|
|
||||||
|
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 {
|
||||||
|
/// Registration options Constructor
|
||||||
|
RegistrationOptions({
|
||||||
|
required this.afterRegistration,
|
||||||
|
this.registrationRepository,
|
||||||
|
this.registrationSteps,
|
||||||
|
this.titleFlex,
|
||||||
|
this.formFlex,
|
||||||
|
this.beforeTitleFlex,
|
||||||
|
this.afterTitleFlex,
|
||||||
|
this.registrationTranslations = const RegistrationTranslations.empty(),
|
||||||
|
this.onError,
|
||||||
|
this.customAppbarBuilder = _createCustomAppBar,
|
||||||
|
this.nextButtonBuilder,
|
||||||
|
this.previousButtonBuilder,
|
||||||
|
this.buttonMainAxisAlignment,
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// The repository responsible for registration.
|
||||||
|
RegistrationRepository? registrationRepository;
|
||||||
|
|
||||||
|
/// A function for customizing the app bar displayed during registration.
|
||||||
|
final AppBar Function(String title)? customAppbarBuilder;
|
||||||
|
|
||||||
|
/// 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)?
|
||||||
|
previousButtonBuilder;
|
||||||
|
|
||||||
|
/// 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({
|
||||||
|
TextEditingController? emailController,
|
||||||
|
TextEditingController? passController,
|
||||||
|
bool passHidden = true,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
Function(bool mainPass, bool value)? passHideOnChange,
|
||||||
|
RegistrationTranslations translations =
|
||||||
|
const RegistrationTranslations.empty(),
|
||||||
|
Function(String title)? titleBuilder,
|
||||||
|
Function(String label)? labelBuilder,
|
||||||
|
TextStyle? textStyle,
|
||||||
|
TextStyle? hintStyle,
|
||||||
|
String? initialEmail,
|
||||||
|
}) =>
|
||||||
|
[
|
||||||
|
AuthStep(
|
||||||
|
fields: [
|
||||||
|
AuthTextField(
|
||||||
|
name: "email",
|
||||||
|
textEditingController: emailController,
|
||||||
|
value: initialEmail ?? "",
|
||||||
|
title: titleBuilder?.call(translations.defaultEmailTitle) ??
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 180),
|
||||||
|
child: Text(
|
||||||
|
translations.defaultEmailTitle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textInputType: TextInputType.emailAddress,
|
||||||
|
textFieldDecoration: InputDecoration(
|
||||||
|
hintStyle: hintStyle,
|
||||||
|
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,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
AuthStep(
|
||||||
|
fields: [
|
||||||
|
AuthPassField(
|
||||||
|
name: "password",
|
||||||
|
textEditingController: passController,
|
||||||
|
title: titleBuilder?.call(translations.defaultPasswordTitle) ??
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 180),
|
||||||
|
child: Text(
|
||||||
|
translations.defaultPasswordTitle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
textFieldDecoration: InputDecoration(
|
||||||
|
hintStyle: hintStyle,
|
||||||
|
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
label: labelBuilder?.call(translations.defaultPasswordLabel),
|
||||||
|
hintText: translations.defaultPasswordHint,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
focusedBorder: const OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||||
|
textStyle: textStyle,
|
||||||
|
validators: [
|
||||||
|
(value) {
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return translations.defaultPasswordValidatorMessage;
|
||||||
|
}
|
||||||
|
// ignore: avoid_dynamic_calls
|
||||||
|
if (value.length < 6) {
|
||||||
|
return translations.defaultPasswordToShortValidatorMessage;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
AppBar _createCustomAppBar(String title) => AppBar(
|
||||||
|
iconTheme: const IconThemeData(color: Colors.black, size: 16),
|
||||||
|
title: Text(title),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
);
|
130
lib/src/config/registration_translations.dart
Normal file
130
lib/src/config/registration_translations.dart
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
/// Holds all the translations for the standard elements
|
||||||
|
/// on the registration screen.
|
||||||
|
class RegistrationTranslations {
|
||||||
|
/// Constructs a [RegistrationTranslations] object.
|
||||||
|
const RegistrationTranslations({
|
||||||
|
required this.title,
|
||||||
|
required this.registerBtn,
|
||||||
|
required this.previousStepBtn,
|
||||||
|
required this.nextStepBtn,
|
||||||
|
required this.closeBtn,
|
||||||
|
required this.defaultEmailTitle,
|
||||||
|
required this.defaultEmailLabel,
|
||||||
|
required this.defaultEmailHint,
|
||||||
|
required this.defaultEmailEmpty,
|
||||||
|
required this.defaultEmailValidatorMessage,
|
||||||
|
required this.defaultPasswordTitle,
|
||||||
|
required this.defaultPasswordLabel,
|
||||||
|
required this.defaultPasswordHint,
|
||||||
|
required this.defaultPasswordValidatorMessage,
|
||||||
|
required this.defaultPasswordToShortValidatorMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
/// 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({
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,11 +1,20 @@
|
||||||
import 'package:flutter/material.dart';
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,9 +1,15 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,54 @@
|
||||||
import 'package:flutter/material.dart';
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
abstract class AuthField {
|
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> {
|
||||||
|
/// 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.title,
|
required this.value,
|
||||||
|
this.onValueChanged,
|
||||||
|
this.title,
|
||||||
this.validators = const [],
|
this.validators = const [],
|
||||||
this.value = '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The name of the field.
|
||||||
final String name;
|
final String name;
|
||||||
final String 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;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,9 +1,16 @@
|
||||||
import 'package:flutter_registration/src/model/auth_field.dart';
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,96 @@
|
||||||
import 'package:flutter/material.dart';
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import "package:flutter/material.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,
|
||||||
required super.title,
|
TextEditingController? textEditingController,
|
||||||
|
super.title,
|
||||||
super.validators = const [],
|
super.validators = const [],
|
||||||
super.value = '',
|
super.value = "",
|
||||||
this.obscureText = false,
|
this.textStyle,
|
||||||
|
this.onChange,
|
||||||
|
this.textFieldDecoration,
|
||||||
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
|
this.textInputType,
|
||||||
}) {
|
}) {
|
||||||
_textEditingController = TextEditingController();
|
textController =
|
||||||
|
textEditingController ?? TextEditingController(text: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
late TextEditingController _textEditingController;
|
/// The controller for the text input.
|
||||||
final bool obscureText;
|
late TextEditingController textController;
|
||||||
|
|
||||||
|
/// The text style for the text input.
|
||||||
|
final TextStyle? textStyle;
|
||||||
|
|
||||||
|
/// A callback function triggered when the value of the field changes.
|
||||||
|
final Function(String value)? onChange;
|
||||||
|
|
||||||
|
/// 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() => TextFormField(
|
Widget build(BuildContext context, Function onValueChanged) => Padding(
|
||||||
controller: _textEditingController,
|
padding: padding,
|
||||||
obscureText: obscureText,
|
child: TextFormField(
|
||||||
onChanged: (v) {
|
keyboardType: textInputType,
|
||||||
value = v;
|
style: textStyle,
|
||||||
},
|
decoration: textFieldDecoration,
|
||||||
validator: (value) {
|
controller: textController,
|
||||||
for (var validator in validators) {
|
onChanged: (v) {
|
||||||
var output = validator(value);
|
value = v;
|
||||||
if (output != null) {
|
onChange?.call(value);
|
||||||
return output;
|
// ignore: avoid_dynamic_calls
|
||||||
|
onValueChanged();
|
||||||
|
},
|
||||||
|
validator: (value) {
|
||||||
|
for (var validator in validators) {
|
||||||
|
var output = validator(value);
|
||||||
|
if (output != null) {
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +1,77 @@
|
||||||
import 'package:flutter/material.dart';
|
import "dart:collection";
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
|
||||||
import 'package:flutter_registration/src/auth_screen.dart';
|
|
||||||
|
|
||||||
class RegistrationScreen extends StatelessWidget {
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_registration/flutter_registration.dart";
|
||||||
|
import "package:flutter_registration/src/auth_screen.dart";
|
||||||
|
|
||||||
|
/// A screen for user registration.
|
||||||
|
class RegistrationScreen extends StatefulWidget {
|
||||||
|
/// Constructs a [RegistrationScreen] object.
|
||||||
|
///
|
||||||
|
/// [registrationOptions] specifies the registration options.
|
||||||
const RegistrationScreen({
|
const RegistrationScreen({
|
||||||
required this.afterRegistration,
|
required this.registrationOptions,
|
||||||
required this.repository,
|
|
||||||
this.additionalSteps = const [],
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final VoidCallback afterRegistration;
|
/// The registration options.
|
||||||
final RegistrationRepository repository;
|
final RegistrationOptions registrationOptions;
|
||||||
final List<AuthStep> additionalSteps;
|
|
||||||
|
@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 void Function(int? pageToReturn) onError,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
await widget.registrationOptions.beforeRegistration?.call();
|
||||||
|
|
||||||
|
var registered = await widget.registrationOptions.registrationRepository!
|
||||||
|
.register(values);
|
||||||
|
|
||||||
|
if (registered == null) {
|
||||||
|
widget.registrationOptions.afterRegistration();
|
||||||
|
} else {
|
||||||
|
var pageToReturn = widget.registrationOptions.onError?.call(registered);
|
||||||
|
|
||||||
|
onError(pageToReturn);
|
||||||
|
}
|
||||||
|
} on Exception catch (_) {
|
||||||
|
onError(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
void showError(String error) => showDialog<String>(
|
var translations = widget.registrationOptions.registrationTranslations;
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) => AlertDialog(
|
|
||||||
content: Text(error),
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context, 'Sluit'),
|
|
||||||
child: const Text('Sluit'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
void register(values) => repository
|
|
||||||
.register(values)
|
|
||||||
.then(
|
|
||||||
(value) => afterRegistration(),
|
|
||||||
)
|
|
||||||
.catchError(
|
|
||||||
(error) {
|
|
||||||
showError(
|
|
||||||
error.toString(),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return AuthScreen(
|
return AuthScreen(
|
||||||
title: 'Registreren',
|
steps: widget.registrationOptions.steps,
|
||||||
submitBtnTitle: 'Registreren',
|
customAppBar: widget.registrationOptions.customAppbarBuilder?.call(
|
||||||
steps: [
|
translations.title,
|
||||||
AuthStep(
|
),
|
||||||
fields: [
|
onFinish: _register,
|
||||||
AuthTextField(
|
appBarTitle: translations.title,
|
||||||
name: 'email',
|
submitBtnTitle: translations.registerBtn,
|
||||||
title: 'Wat is je e-mailadres?',
|
nextBtnTitle: translations.nextStepBtn,
|
||||||
validators: [
|
previousBtnTitle: translations.previousStepBtn,
|
||||||
(email) => (email == null || email.isEmpty)
|
nextButtonBuilder: widget.registrationOptions.nextButtonBuilder,
|
||||||
? 'Geef uw e-mailadres op'
|
previousButtonBuilder: widget.registrationOptions.previousButtonBuilder,
|
||||||
: null,
|
buttonMainAxisAlignment:
|
||||||
(email) => RegExp(
|
widget.registrationOptions.buttonMainAxisAlignment,
|
||||||
r'^.+@[a-zA-Z]+\.{1}[a-zA-Z]+(\.{0,1}[a-zA-Z]+)$',
|
customBackgroundColor: widget.registrationOptions.backgroundColor,
|
||||||
).hasMatch(email!)
|
titleWidget: widget.registrationOptions.titleWidget,
|
||||||
? 'Geef een geldig e-mailadres op'
|
loginButton: widget.registrationOptions.loginButton,
|
||||||
: null,
|
titleFlex: widget.registrationOptions.titleFlex,
|
||||||
],
|
formFlex: widget.registrationOptions.formFlex,
|
||||||
)
|
beforeTitleFlex: widget.registrationOptions.beforeTitleFlex,
|
||||||
],
|
afterTitleFlex: widget.registrationOptions.afterTitleFlex,
|
||||||
),
|
maxFormWidth: widget.registrationOptions.maxFormWidth,
|
||||||
AuthStep(
|
|
||||||
fields: [
|
|
||||||
AuthTextField(
|
|
||||||
name: 'password',
|
|
||||||
title: 'Kies een wachtwoord',
|
|
||||||
obscureText: true,
|
|
||||||
validators: [
|
|
||||||
(value) => (value == null || value.isEmpty)
|
|
||||||
? 'Geef een wachtwoord op'
|
|
||||||
: null,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
...additionalSteps
|
|
||||||
],
|
|
||||||
onFinish: register,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import 'dart:collection';
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import "dart:collection";
|
||||||
|
|
||||||
|
/// A mixin for a registration repository.
|
||||||
mixin RegistrationRepository {
|
mixin RegistrationRepository {
|
||||||
Future<void> register(HashMap values);
|
/// Registers a user with the given values.
|
||||||
|
Future<String?> register(HashMap values);
|
||||||
}
|
}
|
||||||
|
|
55
pubspec.yaml
55
pubspec.yaml
|
@ -3,59 +3,32 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
name: flutter_registration
|
name: flutter_registration
|
||||||
description: A standard flutter package.
|
description: A Flutter Registration package
|
||||||
version: 0.0.1
|
version: 3.0.0
|
||||||
homepage:
|
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:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_hooks: ^0.18.5+1
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
# For information on the generic Dart part of this file, see the
|
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
|
||||||
flutter:
|
flutter:
|
||||||
|
|
||||||
# To add assets to your package, add an assets section, like this:
|
|
||||||
# assets:
|
|
||||||
# - images/a_dot_burr.jpeg
|
|
||||||
# - images/a_dot_ham.jpeg
|
|
||||||
#
|
|
||||||
# For details regarding assets in packages, see
|
|
||||||
# https://flutter.dev/assets-and-images/#from-packages
|
|
||||||
#
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
|
||||||
|
|
||||||
# To add custom fonts to your package, add a fonts section here,
|
|
||||||
# in this "flutter" section. Each entry in this list should have a
|
|
||||||
# "family" key with the font family name, and a "fonts" key with a
|
|
||||||
# list giving the asset and other descriptors for the font. For
|
|
||||||
# example:
|
|
||||||
# fonts:
|
|
||||||
# - family: Schyler
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/Schyler-Regular.ttf
|
|
||||||
# - asset: fonts/Schyler-Italic.ttf
|
|
||||||
# style: italic
|
|
||||||
# - family: Trajan Pro
|
|
||||||
# fonts:
|
|
||||||
# - asset: fonts/TrajanPro.ttf
|
|
||||||
# - asset: fonts/TrajanPro_Bold.ttf
|
|
||||||
# weight: 700
|
|
||||||
#
|
|
||||||
# For details regarding fonts in packages, see
|
|
||||||
# https://flutter.dev/custom-fonts/#from-packages
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
//
|
//
|
||||||
|
// 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