Compare commits

...

27 commits

Author SHA1 Message Date
Freek van de Ven
6d4db87ff1 feat: add onBack callback for the FlutterFormOptions 2024-09-05 13:09:17 +02:00
Freek van de Ven
5528664fb7 chore: add component release workflow 2024-09-03 14:21:33 +02:00
5eb914c23a chore: ready the package for deployment to the pub server 2024-09-03 14:21:33 +02:00
9af9822fc1 chore: add fvm configuration to gitignore 2024-09-03 14:21:33 +02:00
Gorter-dev
204d0c6ca2
Merge pull request #50 from Iconica-Development/feature/multiple_choice
feat: add multiple choice form field
2024-07-01 16:45:35 +02:00
mike doornenbal
a79b0c3ad3 feat: add multiple choice form field 2024-07-01 16:43:36 +02:00
Gorter-dev
18ac2dd82f
Merge pull request #49 from Iconica-Development/feature/dropdownfield
feat: add dropdownfield
2024-07-01 08:54:54 +02:00
mike doornenbal
a88defd186 feat: add dropdownfield 2024-06-28 14:22:33 +02:00
Gorter-dev
a9d3a2c751
Merge pull request #48 from Iconica-Development/chore/upgrade-intl
chore(deps): loosen dep on intl
2024-05-14 14:20:37 +02:00
Bart Ribbers
335a1e1dd1 chore: upgrade version to 6.3.2 2024-05-14 14:19:08 +02:00
Bart Ribbers
ae071cef41 chore(deps): loosen dep on intl
The latest Flutter requires intl 0.19.0 and now every app/package
depending on us fails to build with the latest Flutter because we were
explicitly requiring 0.18.0

Minor upgrades should be safe anyway, we only care for major versions
upgrade, so loosen the requirements a bit to support anything from
0.18.0 up to (but not including) 1.0.0
2024-05-14 14:17:26 +02:00
Freek van de Ven
48534c0db1
Merge pull request #47 from Iconica-Development/refactor/replace-ShellTranslationService
refactor: replace shelltranslationservice with validationMessage input
2024-02-29 16:52:11 +01:00
Vick Top
7bed6b06b7 doc: update pubspec and changelog 2024-02-29 16:50:16 +01:00
Vick Top
c2fac70838 refactor: expose FlutterFormInputDateTime params from flutter_input_library 2024-02-29 16:11:48 +01:00
Vick Top
b1ca468085 refactor: replace shelltranslationservice with validationMessage input 2024-02-29 15:43:10 +01:00
Freek van de Ven
1b9844c5d0
Merge pull request #43 from Iconica-Development/doc/improve-documentation
doc: create documentation for files
2024-02-29 13:25:56 +01:00
Freek van de Ven
f526dc0830
Merge pull request #44 from Iconica-Development/feature/phone_input_field
feat: Add phone field and added some decorations to some fields
2024-02-21 16:13:55 +01:00
Jacques
929d318668 feat: Add phone field and added some decorations to some fields 2024-02-21 16:02:31 +01:00
Vick Top
7b3a34064c doc: create documentation for files 2024-02-20 13:28:59 +01:00
Freek van de Ven
09939512f9
Merge pull request #42 from Iconica-Development/6.2.5
6.2.5
2024-02-15 09:54:55 +01:00
mike doornenbal
08d9c3c67c 6.2.5 2024-02-15 09:53:32 +01:00
Freek van de Ven
9a55bbb410
Merge pull request #41 from Iconica-Development/update-component-documentation-workflow-correct
Add component-documentation.yml correct
2024-02-13 16:35:04 +01:00
Vick Top
b51a4fec21 fix(ci): run dart format 2024-02-13 15:51:39 +01:00
Vick Top
42333adfc1 feat(documentation): Create component-documentation.yml workflow file 2024-02-13 13:35:54 +01:00
Freek van de Ven
1933a5ae0f feat: update flutter_input_library dependency 2024-02-09 14:32:51 +01:00
mike doornenbal
650418a36f Merge branch 'fix/add_ci_linter' 2024-02-07 12:50:47 +01:00
mike doornenbal
9012b8da4d fix: added CI and linter 2024-02-07 12:50:28 +01:00
38 changed files with 1336 additions and 735 deletions

14
.github/workflows/component-ci.yml vendored Normal file
View file

@ -0,0 +1,14 @@
name: Iconica Standard Component CI Workflow
# Workflow Caller version: 2.0.0
on:
pull_request:
workflow_dispatch:
jobs:
call-global-iconica-workflow:
uses: Iconica-Development/.github/.github/workflows/component-ci.yml@master
secrets: inherit
permissions: write-all
with:
subfolder: "." # add optional subfolder to run workflow in

View 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

View file

@ -1,32 +0,0 @@
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

14
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,14 @@
name: Iconica Standard Component Release Workflow
# Workflow Caller version: 1.0.0
on:
release:
types: [published]
workflow_dispatch:
jobs:
call-global-iconica-workflow:
uses: Iconica-Development/.github/.github/workflows/component-release.yml@master
secrets: inherit
permissions: write-all

7
.gitignore vendored
View file

@ -19,7 +19,7 @@ migrate_working_dir/
# The .vscode folder contains launch configuration and tasks you configure in # The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line # VS Code which you may wish to be included in version control, so this line
# is commented out by default. # is commented out by default.
#.vscode/ .vscode/
# Flutter/Dart/Pub related # Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
@ -28,3 +28,8 @@ migrate_working_dir/
.dart_tool/ .dart_tool/
.packages .packages
build/ build/
.metadata
# FVM Version Cache
.fvm/
.fvmrc

View file

@ -1,10 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
channel: stable
project_type: package

View file

@ -1,117 +1,129 @@
## 0.0.2 - October 10th 2022 ## 6.6.0 - September 5th 2024
- Added `onBack` callback to the `FlutterFormOptions` to detect when the back button is pressed
- Initial release ## 6.5.0 - July 1st 2024
- Added `FlutterFormMultipleChoice` for multiple choice selection
## 0.1.0 - October 12th 2022 ## 6.4.0 - June 28th 2024
- Added `FlutterFormInputDropdown` for dropdown selection
- Added style property to `FlutterFormInputEmail`
- Added a multi line plain text input widget ## 6.3.2 - May 15th 2024
- Ability to set the scrolldirection of the pageview - Loosened the dependency on intl to be more compatible with several Flutter versions
- Ability to set the scrollphysics of the pages' scrollview.
## 0.2.0 - October 13th 2022 ## 6.3.1 - February 29th 2024
- Removed `TranslationService` and add `validationMessage` property to fields with validation.
- Added a way to override a input field validator.
- Exposed all properties for `FlutterFormInputDateTime` provided by `flutter_input_library`.
- Added date input widget ## 6.3.0 - February 21st 2024
- Updated the `flutter_input_library` from 3.1.0 to 3.2.1
- Added `FlutterFormInputPhone` for phone number with dial code selection.
- Added `InputDecoration` parameter to the following input fields: `FlutterFormInputEmail` and `FlutterFormInputPassword`
## 1.0.0 - October 13th 2022 ## 6.2.5 - February 15th 2024
- Updated the `flutter_input_library` from 3.0.1 to 3.1.0
- Fix: Proper use of generics ## 6.2.4 - February 9th 2024
- Inputcontrollers now have an onChange. - Updated the `flutter_input_library` from 3.0.0 to 3.0.1
- Added switch input field
## 1.0.1 - October 13th 2022 ## 6.2.3 - February 7th 2024
- Added CI and linter
- Added forgotten onChanged parameter on date input field ## 6.2.2 - February 6th 2024
- Updated the `flutter_input_library` from 2.6.0 to 3.0.0
## 1.0.2 - October 13th 2022 ## 6.2.1 - January 12th 2024
- Pass on the `initialValue` property to FlutterFormMultiLine
- Added forgotten icon parameter on date input field and scrollpadding on text inputfield ## 6.2.0 - December 14th 2023
## 2.0.0 - October 26th 2022 - Pass on the `textCapitalization` property to FlutterFormPlainText and FlutterFormMultiLine
- Added the id of the input field on the input check widget.
- Ability to set the height of the carousel input field.
- InputController now contains the onSubmit callback.
## 2.0.1 - October 27th 2022
- onChange of switch input not firing fixed
## 4.0.2 - November 29th 2022
- Name change to flutter_form_wizard
## 4.0.3 - November 29th 2022
- Change from input to `flutter_input_library`
## 5.0.0 - November 29th 2022
- `flutter_input_library` now enforces 24h time requirements
## 5.0.4 - January 12th 2023
- Add FocusNode option to inputs
## 5.0.5 - January 12th 2023
- Expose translations
## 5.0.6 - January 12th 2023
- `flutter_input_library` datetime validator fix
## 5.0.7 - January 12th 2023
- `flutter_input_library` add styling text datetime fix
## 5.0.8 - January 18th 2023
- `flutter_input_library` add focusNode option to `FlutterFormInputMultiLine`
## 5.0.9 - January 18th 2023
- `flutter_input_library` add initial time picker parameter
## 6.0.0 - march 28th 2023
- Bump `flutter_input_library` to version 2.0.0
- Remove unnecessary `riverpod` dependency
## 6.1.0 - May 12th 2023
- Make compatible to flutter 3.10
## 6.1.1 - August 10th 2023
- Bump `flutter_input_library` to version 2.2.0
## 6.1.2 - August 11th 2023
- Bump `flutter_input_library` to version 2.2.1
## 6.1.3 - September 26th 2023
- Fix date format for datecontroller
## 6.1.4 - October 26th 2023
- Bump `flutter_input_library` to version 2.3.0
## 6.1.5 - October 26th 2023
- Bump `flutter_input_library` to version 2.4.0
## 6.1.6 - October 26th 2023
- Add enabled property to FlutterFormInputPlainText and FlutterFormInputDateTime
## 6.1.7 - November 1st 2023
- pass on the show icon property to flutter form input date time widget
## 6.1.8 - December 6th 2023 ## 6.1.8 - December 6th 2023
- Pass on the style property to the FlutterFormPlainText input widget - Pass on the style property to the FlutterFormPlainText input widget
- Bump `flutter_input_library` to version 2.5.0 - Bump `flutter_input_library` to version 2.5.0
## 6.2.0 - December 14th 2023 ## 6.1.7 - November 1st 2023
- Pass on the `textCapitalization` property to FlutterFormPlainText and FlutterFormMultiLine - Pass on the show icon property to flutter form input date time widget
## 6.2.1 - January 12th 2024 ## 6.1.6 - October 26th 2023
- Pass on the `initialValue` property to FlutterFormMultiLine - Add enabled property to FlutterFormInputPlainText and FlutterFormInputDateTime
## 6.2.2 - February 6th 2024 ## 6.1.5 - October 26th 2023
- Updated the `flutter_input_library` from 2.6.0 to 3.0.0 - Bump `flutter_input_library` to version 2.4.0
## 6.1.4 - October 26th 2023
- Bump `flutter_input_library` to version 2.3.0
## 6.1.3 - September 26th 2023
- Fix date format for date controller
## 6.1.2 - August 11th 2023
- Bump `flutter_input_library` to version 2.2.1
## 6.1.1 - August 10th 2023
- Bump `flutter_input_library` to version 2.2.0
## 6.1.0 - May 12th 2023
- Make compatible with Flutter 3.10
## 6.0.0 - March 28th 2023
- Bump `flutter_input_library` to version 2.0.0
- Remove unnecessary `riverpod` dependency
## 5.0.9 - January 18th 2023
- `flutter_input_library` add initial time picker parameter
## 5.0.8 - January 18th 2023
- `flutter_input_library` add focusNode option to `FlutterFormInputMultiLine`
## 5.0.7 - January 12th 2023
- `flutter_input_library` add styling text datetime fix
## 5.0.6 - January 12th 2023
- `flutter_input_library` datetime validator fix
## 5.0.5 - January 12th 2023
- Expose translations
## 5.0.4 - January 12th 2023
- Add FocusNode option to inputs
## 5.0.0 - November 29th 2022
- `flutter_input_library` now enforces 24h time requirements
## 4.0.3 - November 29th 2022
- Change from input to `flutter_input_library`
## 4.0.2 - November 29th 2022
- Name change to flutter_form_wizard
## 2.0.1 - October 27th 2022
- onChange of switch input not firing fixed
## 2.0.0 - October 26th 2022
- Added the id of the input field on the input check widget.
- Ability to set the height of the carousel input field.
- InputController now contains the onSubmit callback.
## 1.0.2 - October 13th 2022
- Added forgotten icon parameter on date input field and scroll padding on text input field
## 1.0.1 - October 13th 2022
- Added forgotten onChanged parameter on date input field
## 1.0.0 - October 13th 2022
- Fix: Proper use of generics
- Input controllers now have an onChange.
- Added switch input field
## 0.2.0 - October 13th 2022
- Added date input widget
## 0.1.0 - October 12th 2022
- Added a multi-line plain text input widget
- Ability to set the scroll direction of the page view
- Ability to set the scroll physics of the pages' scroll view.
## 0.0.2 - October 10th 2022
- Initial release

View file

@ -1,4 +1,9 @@
include: package:flutter_lints/flutter.yaml include: package:flutter_iconica_analysis/analysis_options.yaml
# Additional information about this file can be found at # Possible to overwrite the rules from the package
# https://dart.dev/guides/language/analysis-options
analyzer:
exclude:
linter:
rules:

View file

@ -1,45 +0,0 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled.
version:
revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
- platform: android
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
- platform: ios
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
- platform: linux
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
- platform: macos
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
- platform: web
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
- platform: windows
create_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
base_revision: 4f9d92fbbdf072a70a70d2179a9f87392b94104c
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View file

@ -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
} }

View file

@ -35,6 +35,7 @@ class _AgePageState extends State<AgePage> {
minValue: 12, minValue: 12,
maxValue: 120, maxValue: 120,
controller: widget.inputController, controller: widget.inputController,
validationMessage: 'Please fill in your age.',
), ),
], ],
); );

View file

@ -34,7 +34,10 @@ class _CarouselPageState extends State<CarouselPage> {
amountOfPages: 4, amountOfPages: 4,
flutterFormWidgets: [ flutterFormWidgets: [
FlutterFormInputCarousel( FlutterFormInputCarousel(
controller: widget.inputController, items: getCars()) controller: widget.inputController,
items: getCars(),
validationMessage: 'Validation error message.',
)
], ],
); );
} }

View file

@ -46,6 +46,7 @@ class _DatePageState extends State<DatePage> {
), ),
label: const Text("Custom date label"), label: const Text("Custom date label"),
controller: widget.dateController, controller: widget.dateController,
validationMessage: 'Please fill in a date.',
), ),
), ),
], ],

View file

@ -40,6 +40,7 @@ class _NamePageState extends State<NamePage> {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text("First Name"), label: const Text("First Name"),
controller: widget.firstNameController, controller: widget.firstNameController,
validationMessage: 'Please fill in your first name.',
), ),
), ),
if (widget.showLastName) if (widget.showLastName)
@ -48,6 +49,7 @@ class _NamePageState extends State<NamePage> {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text("Last Name"), label: const Text("Last Name"),
controller: widget.lastNameController, controller: widget.lastNameController,
validationMessage: 'Please fill in your last name.',
), ),
), ),
], ],

View file

@ -25,6 +25,14 @@ class _FormExampleState extends ConsumerState<FormExample> {
final String checkPageText = "All entered info: "; final String checkPageText = "All entered info: ";
final phoneInputController = FlutterFormInputPhoneController(
id: 'phone',
onChanged: (value) {
debugPrint(
'${value?.dialCode ?? 'no dial'} ${value?.number ?? 'no number'}');
},
);
final ageInputController = FlutterFormInputNumberPickerController( final ageInputController = FlutterFormInputNumberPickerController(
id: "age", id: "age",
checkPageTitle: (dynamic amount) { checkPageTitle: (dynamic amount) {
@ -177,6 +185,14 @@ class _FormExampleState extends ConsumerState<FormExample> {
return Container(); return Container();
}, },
pages: [ pages: [
FlutterFormPage(
child: Center(
child: FlutterFormInputPhone(
controller: phoneInputController,
validationMessage: 'Please fill in a valid phone number',
),
),
),
FlutterFormPage( FlutterFormPage(
child: AgePage( child: AgePage(
inputController: ageInputController, inputController: ageInputController,

View file

@ -45,10 +45,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: cupertino_icons
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.5" version: "1.0.8"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -68,24 +68,24 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "6.2.1" version: "6.5.0"
flutter_input_library: flutter_input_library:
dependency: transitive dependency: transitive
description: description:
path: "." path: "."
ref: "3.0.0" ref: "3.4.0"
resolved-ref: "7024fb7e404fbeae0331bfe8f7c115283d0951ce" resolved-ref: cdc06bbb7933ba7ac2835e29d2c8fabf69e5f5a6
url: "https://github.com/Iconica-Development/flutter_input_library" url: "https://github.com/Iconica-Development/flutter_input_library"
source: git source: git
version: "3.0.0" version: "3.4.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_lints name: flutter_lints
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.3"
flutter_localizations: flutter_localizations:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -95,10 +95,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_riverpod name: flutter_riverpod
sha256: "371f6e8acb69dbe8aa3e0a50c8a65f8a9352b599134d585cc4923261cb5ae4d6" sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.5.1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -108,58 +108,82 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.18.1" version: "0.19.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints: lints:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3" sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.1.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.16" version: "0.12.16+1"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.11.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.10.0" version: "1.15.0"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.3" version: "1.9.0"
riverpod: riverpod:
dependency: transitive dependency: transitive
description: description:
name: riverpod name: riverpod
sha256: "899cd0999b2f3b798349d9b5639cfea81d406c011bd914097145ff92e91b29f9" sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.5.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -185,10 +209,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: state_notifier name: state_notifier
sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289" sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.2+1" version: "1.0.0"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -217,10 +241,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.1" version: "0.7.2"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -229,14 +253,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
web: vm_service:
dependency: transitive dependency: transitive
description: description:
name: web name: vm_service
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.0" version: "14.2.5"
sdks: sdks:
dart: ">=3.2.0-194.0.dev <4.0.0" dart: ">=3.3.0 <4.0.0"
flutter: ">=3.0.0" flutter: ">=3.18.0-18.0.pre.54"

View file

@ -16,7 +16,7 @@ dependencies:
flutter_riverpod: ^2.1.1 flutter_riverpod: ^2.1.1
flutter_form_wizard: flutter_form_wizard:
path: ../ path: ../
intl: ^0.18.0 intl: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
export 'package:flutter_input_library/flutter_input_library.dart'
show FlutterFormDateTimeType;
export 'src/form.dart'; export 'src/form.dart';
export 'src/widgets/input/abstractions.dart'; export 'src/widgets/input/abstractions.dart';
export 'src/widgets/input/input_types/input_types.dart'; export 'src/widgets/input/input_types/input_types.dart';
export 'package:flutter_input_library/flutter_input_library.dart'
show FlutterFormDateTimeType;
export 'utils/translation_service.dart';
export 'utils/form.dart'; export 'utils/form.dart';

View file

@ -3,18 +3,23 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../flutter_form.dart'; import 'package:flutter_form_wizard/flutter_form.dart';
import 'utils/form_page_controller.dart'; import 'package:flutter_form_wizard/src/utils/form_page_controller.dart';
import 'utils/formstate.dart' as fs; import 'package:flutter_form_wizard/src/utils/formstate.dart' as fs;
/// A wrapper for flutters [Form] that can be controlled by a controller and provides multiple pre-defined input types/fields /// A wrapper for flutters [Form] that can be controlled by a controller and
/// [FlutterForm] also provides multi page forms and a check page for validation. /// provides multiple pre-defined input types/fields
/// [FlutterForm] also provides multi page forms and a check page
/// for validation.
/// ///
/// A [FlutterFormController] has to be given to control what happens to values and pages within the FlutterForm. /// A [FlutterFormController] has to be given to control what happens to values
/// and pages within the FlutterForm.
/// ///
/// [FlutterFormOptions] have to be provided to control the appearance of the form. /// [FlutterFormOptions] have to be provided to control the appearance of
/// the form.
/// ///
/// WARNING Define your FormInputController above your FlutterForm. Otherwise when rebuild the controller will differ from the registered ones. /// WARNING Define your FormInputController above your FlutterForm. Otherwise
/// when rebuild the controller will differ from the registered ones.
/// ``` dart /// ``` dart
/// FlutterFormInputEmailController emailController = /// FlutterFormInputEmailController emailController =
/// FlutterFormInputEmailController(id: 'email'); /// FlutterFormInputEmailController(id: 'email');
@ -180,10 +185,10 @@ import 'utils/formstate.dart' as fs;
/// ``` /// ```
class FlutterForm extends StatefulWidget { class FlutterForm extends StatefulWidget {
const FlutterForm({ const FlutterForm({
Key? key,
required this.options, required this.options,
required this.formController, required this.formController,
}) : super(key: key); super.key,
});
final FlutterFormOptions options; final FlutterFormOptions options;
final FlutterFormController formController; final FlutterFormController formController;
@ -203,9 +208,9 @@ class _FlutterFormState extends State<FlutterForm> {
_formController.setFlutterFormOptions(widget.options); _formController.setFlutterFormOptions(widget.options);
List<GlobalKey<FormState>> keys = []; var keys = <GlobalKey<FormState>>[];
for (FlutterFormPage _ in widget.options.pages) { for (var _ in widget.options.pages) {
keys.add(GlobalKey<FormState>()); keys.add(GlobalKey<FormState>());
} }
@ -215,9 +220,9 @@ class _FlutterFormState extends State<FlutterForm> {
setState(() {}); setState(() {});
}); });
List<FlutterFormPageController> controllers = []; var controllers = <FlutterFormPageController>[];
for (int i = 0; i < widget.options.pages.length; i++) { for (var i = 0; i < widget.options.pages.length; i++) {
controllers.add(FlutterFormPageController()); controllers.add(FlutterFormPageController());
} }
@ -225,10 +230,7 @@ class _FlutterFormState extends State<FlutterForm> {
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) => Stack(
var _ = getTranslator(context);
return Stack(
children: [ children: [
PageView( PageView(
scrollDirection: _formController._options.scrollDirection, scrollDirection: _formController._options.scrollDirection,
@ -278,25 +280,35 @@ class _FlutterFormState extends State<FlutterForm> {
), ),
], ],
), ),
widget.options.nextButton != null if (widget.options.nextButton != null)
? widget.options.nextButton!(_formController.getCurrentStep(), widget.options.nextButton!(
_formController.getCheckpages()) _formController.getCurrentStep(),
: Align( _formController.getCheckpages(),
)
else
Align(
alignment: AlignmentDirectional.bottomCenter, alignment: AlignmentDirectional.bottomCenter,
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 40, vertical: 15), horizontal: 40,
vertical: 15,
),
textStyle: const TextStyle( textStyle: const TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)), fontSize: 20,
fontWeight: FontWeight.bold,
),
),
onPressed: () async { onPressed: () async {
await _formController.autoNextStep(); await _formController.autoNextStep();
}, },
child: Text(_formController.getCurrentStep() >= child: Text(
_formController.getCurrentStep() >=
widget.options.pages.length - 1 widget.options.pages.length - 1
? "Finish" ? 'Finish'
: "Next"), : 'Next',
),
), ),
), ),
if (widget.options.backButton != null) if (widget.options.backButton != null)
@ -307,15 +319,14 @@ class _FlutterFormState extends State<FlutterForm> {
), ),
], ],
); );
}
List<Widget> getResultWidgets() { List<Widget> getResultWidgets() {
List<Widget> widgets = []; var widgets = <Widget>[];
_formController.getAllResults().forEach( _formController.getAllResults().forEach(
(pageNumber, pageResults) { (pageNumber, pageResults) {
pageResults.forEach((inputId, inputResult) { pageResults.forEach((inputId, inputResult) {
FlutterFormInputController? inputController = _formController var inputController = _formController
.getFormPageControllers()[pageNumber] .getFormPageControllers()[pageNumber]
.getController(inputId); .getController(inputId);
@ -327,10 +338,8 @@ class _FlutterFormState extends State<FlutterForm> {
inputController.checkPageTitle != null inputController.checkPageTitle != null
? inputController.checkPageTitle!(inputController.value) ? inputController.checkPageTitle!(inputController.value)
: inputController.value.toString(), : inputController.value.toString(),
inputController.checkPageDescription != null inputController.checkPageDescription
? inputController ?.call(inputController.value),
.checkPageDescription!(inputController.value)
: null,
() async { () async {
await _formController.jumpToPage(pageNumber); await _formController.jumpToPage(pageNumber);
}, },
@ -378,7 +387,7 @@ class _FlutterFormState extends State<FlutterForm> {
Text( Text(
inputController.checkPageDescription!(inputResult), inputController.checkPageDescription!(inputResult),
style: const TextStyle(fontSize: 16), style: const TextStyle(fontSize: 16),
) ),
], ],
), ),
), ),
@ -412,15 +421,14 @@ class FlutterFormController extends ChangeNotifier {
late List<FlutterFormPageController> _formPageControllers; late List<FlutterFormPageController> _formPageControllers;
List<FlutterFormPageController> getFormPageControllers() { List<FlutterFormPageController> getFormPageControllers() =>
return _formPageControllers; _formPageControllers;
}
setFormPageControllers(List<FlutterFormPageController> controllers) { void setFormPageControllers(List<FlutterFormPageController> controllers) {
_formPageControllers = controllers; _formPageControllers = controllers;
} }
disableCheckingPages() { void voidisableCheckingPages() {
_checkingPages = false; _checkingPages = false;
for (var controller in _formPageControllers) { for (var controller in _formPageControllers) {
@ -436,7 +444,9 @@ class FlutterFormController extends ChangeNotifier {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
_options.onNext( _options.onNext(
_currentStep, _formPageControllers[_currentStep].getAllValues()); _currentStep,
_formPageControllers[_currentStep].getAllValues(),
);
if (_currentStep >= _options.pages.length - 1 && if (_currentStep >= _options.pages.length - 1 &&
_options.checkPage == null || _options.checkPage == null ||
@ -449,9 +459,11 @@ class FlutterFormController extends ChangeNotifier {
notifyListeners(); notifyListeners();
await _pageController.animateToPage(_currentStep, await _pageController.animateToPage(
_currentStep,
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
curve: Curves.ease); curve: Curves.ease,
);
} else { } else {
_currentStep += 1; _currentStep += 1;
@ -462,9 +474,11 @@ class FlutterFormController extends ChangeNotifier {
notifyListeners(); notifyListeners();
await _pageController.animateToPage(_currentStep, await _pageController.animateToPage(
_currentStep,
duration: const Duration(milliseconds: 250), duration: const Duration(milliseconds: 250),
curve: Curves.ease); curve: Curves.ease,
);
} }
} }
} }
@ -473,7 +487,7 @@ class FlutterFormController extends ChangeNotifier {
Future<void> previousStep() async { Future<void> previousStep() async {
_currentStep -= 1; _currentStep -= 1;
_options.onBack?.call(_currentStep);
_checkingPages = false; _checkingPages = false;
notifyListeners(); notifyListeners();
@ -507,13 +521,14 @@ class FlutterFormController extends ChangeNotifier {
return false; return false;
} }
Map<String, dynamic> getCurrentStepResults() { Map<String, dynamic> getCurrentStepResults() =>
return _formPageControllers[_currentStep].getAllValues(); _formPageControllers[_currentStep].getAllValues();
}
Future<void> nextStep() async { Future<void> nextStep() async {
_options.onNext( _options.onNext(
_currentStep, _formPageControllers[_currentStep].getAllValues()); _currentStep,
_formPageControllers[_currentStep].getAllValues(),
);
_currentStep += 1; _currentStep += 1;
@ -523,16 +538,19 @@ class FlutterFormController extends ChangeNotifier {
notifyListeners(); notifyListeners();
await _pageController.animateToPage(_currentStep, await _pageController.animateToPage(
duration: const Duration(milliseconds: 250), curve: Curves.ease); _currentStep,
duration: const Duration(milliseconds: 250),
curve: Curves.ease,
);
} }
finishForm() { void finishForm() {
_options.onFinished(getAllResults()); _options.onFinished(getAllResults());
} }
Map<int, Map<String, dynamic>> getAllResults() { Map<int, Map<String, dynamic>> getAllResults() {
Map<int, Map<String, dynamic>> allValues = {}; var allValues = <int, Map<String, dynamic>>{};
for (var i = 0; i < _options.pages.length; i++) { for (var i = 0; i < _options.pages.length; i++) {
allValues.addAll({i: _formPageControllers[i].getAllValues()}); allValues.addAll({i: _formPageControllers[i].getAllValues()});
@ -540,27 +558,19 @@ class FlutterFormController extends ChangeNotifier {
return allValues; return allValues;
} }
setFlutterFormOptions(FlutterFormOptions options) { void setFlutterFormOptions(FlutterFormOptions options) {
_options = options; _options = options;
} }
setKeys(List<GlobalKey<FormState>> keys) { void setKeys(List<GlobalKey<FormState>> keys) {
_keys = keys; _keys = keys;
} }
List<GlobalKey<FormState>> getKeys() { List<GlobalKey<FormState>> getKeys() => _keys;
return _keys;
}
int getCurrentStep() { int getCurrentStep() => _currentStep;
return _currentStep;
}
bool getCheckpages() { bool getCheckpages() => _checkingPages;
return _checkingPages;
}
PageController getPageController() { PageController getPageController() => _pageController;
return _pageController;
}
} }

View file

@ -4,21 +4,26 @@
import 'package:flutter_form_wizard/flutter_form.dart'; import 'package:flutter_form_wizard/flutter_form.dart';
/// Controller class for managing input controllers in a Flutter form.
class FlutterFormPageController { class FlutterFormPageController {
/// List of input controllers.
List<FlutterFormInputController> _controllers = []; List<FlutterFormInputController> _controllers = [];
/// Registers an input controller.
void register(FlutterFormInputController inputController) { void register(FlutterFormInputController inputController) {
_controllers.add(inputController); _controllers.add(inputController);
} }
clearControllers() { /// Clears all registered input controllers.
void clearControllers() {
_controllers = []; _controllers = [];
} }
bool _isRegisteredById(String id) { /// Checks if an input controller is registered with a given ID.
return _controllers.any((element) => (element.id == id)); bool _isRegisteredById(String id) =>
} _controllers.any((element) => element.id == id);
/// Retrieves the input controller associated with the provided ID.
FlutterFormInputController? getController(String key) { FlutterFormInputController? getController(String key) {
if (_isRegisteredById(key)) { if (_isRegisteredById(key)) {
return _controllers.firstWhere((element) => element.id == key); return _controllers.firstWhere((element) => element.id == key);
@ -26,10 +31,11 @@ class FlutterFormPageController {
return null; return null;
} }
/// Retrieves all values from registered input controllers.
Map<String, dynamic> getAllValues() { Map<String, dynamic> getAllValues() {
Map<String, dynamic> values = {}; var values = <String, dynamic>{};
for (FlutterFormInputController controller in _controllers) { for (var controller in _controllers) {
if (controller.value != null) { if (controller.value != null) {
values.addAll({controller.id!: controller.value}); values.addAll({controller.id!: controller.value});
} }

View file

@ -3,20 +3,23 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'form_page_controller.dart'; import 'package:flutter_form_wizard/src/utils/form_page_controller.dart';
/// Widget for managing form state and providing access to form controller.
class FormState extends InheritedWidget { class FormState extends InheritedWidget {
/// Constructor for FormState.
const FormState({ const FormState({
Key? key, required super.child,
required Widget child,
required this.formController, required this.formController,
}) : super(key: key, child: child); super.key,
});
/// The form controller associated with this FormState.
final FlutterFormPageController formController; final FlutterFormPageController formController;
/// Retrieves the nearest ancestor FormState from the given context.
static FormState of(BuildContext context) { static FormState of(BuildContext context) {
final FormState? result = var result = context.dependOnInheritedWidgetOfExactType<FormState>();
context.dependOnInheritedWidgetOfExactType<FormState>();
assert(result != null, 'No FormStat found in context'); assert(result != null, 'No FormStat found in context');
return result!; return result!;
} }

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '/src/utils/formstate.dart' as fs; import 'package:flutter_form_wizard/src/utils/formstate.dart' as fs;
/// Abstract class for the input widgets used in a [FlutterForm]. /// Abstract class for the input widgets used in a [FlutterForm].
/// ///
@ -12,18 +12,19 @@ import '/src/utils/formstate.dart' as fs;
/// ///
/// label is a standard parameter to normally sets the label of the input. /// label is a standard parameter to normally sets the label of the input.
/// ///
/// [registerController] should be called to register the given [controller] to the form page. /// [registerController] should be called to register the given [controller] to
/// the form page.
abstract class FlutterFormInputWidget<T> extends StatelessWidget { abstract class FlutterFormInputWidget<T> extends StatelessWidget {
const FlutterFormInputWidget({ const FlutterFormInputWidget({
Key? key,
required this.controller, required this.controller,
super.key,
this.focusNode, this.focusNode,
this.label, this.label,
this.enabled = true, this.enabled = true,
String? hintText, });
}) : super(key: key);
/// The [controller] which determines how the value is handled and how the value is shown on the checkpage. /// The [controller] which determines how the value is handled and how the
/// value is shown on the checkpage.
final FlutterFormInputController<T> controller; final FlutterFormInputController<T> controller;
/// [label] is a standard parameter to normally sets the label of the input. /// [label] is a standard parameter to normally sets the label of the input.
@ -33,9 +34,10 @@ abstract class FlutterFormInputWidget<T> extends StatelessWidget {
final bool enabled; final bool enabled;
/// [registerController] should be called to register the given [controller] to the form page. /// [registerController] should be called to register the given [controller]
registerController(BuildContext context) { /// to the form page.
FlutterFormInputController? localController = void registerController(BuildContext context) {
var localController =
fs.FormState.of(context).formController.getController(controller.id!); fs.FormState.of(context).formController.getController(controller.id!);
if (localController == null) { if (localController == null) {
@ -48,11 +50,13 @@ abstract class FlutterFormInputWidget<T> extends StatelessWidget {
/// ///
/// The [id] determines the key in the [Map] returned by the [FlutterForm]. /// The [id] determines the key in the [Map] returned by the [FlutterForm].
/// ///
/// [value] is a way to set a initial value and will be the value when change by the user. /// [value] is a way to set a initial value and will be the value when change
/// by the user.
/// ///
/// [mandatory] determines if the input is mandatory. /// [mandatory] determines if the input is mandatory.
/// ///
/// [checkPageTitle] is a function where you can transform the value from the input into something representable. /// [checkPageTitle] is a function where you can transform the value from the
/// input into something representable.
/// This value will be given when defining the check page widgets. /// This value will be given when defining the check page widgets.
/// If this function is not set, the value will be used as is. /// If this function is not set, the value will be used as is.
/// Example: /// Example:
@ -62,27 +66,32 @@ abstract class FlutterFormInputWidget<T> extends StatelessWidget {
/// }, /// },
/// ``` /// ```
/// ///
/// [checkPageDescription] is the same as checkPageTitle but for the description. /// [checkPageDescription] is the same as checkPageTitle but for the
/// If null no description will be shown. /// description. If null no description will be shown.
/// ///
/// [onChanged] can be set to get the value whenever the user changes it. Should not be used to save the value. /// [onChanged] can be set to get the value whenever the user changes it.
/// Should not be used to save the value.
/// ///
/// [onSubmit] can be set to get the value whenever the user submits it. Should not be used to save the value. /// [onSubmit] can be set to get the value whenever the user submits it.
/// Should not be used to save the value.
/// ///
/// [onSaved] goes of when the save function is called for the page if [onValidate] return null. /// [onSaved] goes of when the save function is called for the page if
/// [onValidate] return null.
/// ///
/// [onValidate] is used to validate the given input by the user. /// [onValidate] is used to validate the given input by the user.
abstract class FlutterFormInputController<T> { abstract class FlutterFormInputController<T> {
/// The [id] determines the key in the [Map] returned by the [FlutterForm]. /// The [id] determines the key in the [Map] returned by the [FlutterForm].
String? id; String? id;
/// [value] is a way to set a initial value and will be the value when change by the user. /// [value] is a way to set a initial value and will be the value when
/// change by the user.
T? value; T? value;
/// [mandatory] determines if the input is mandatory. /// [mandatory] determines if the input is mandatory.
bool mandatory = false; bool mandatory = false;
/// [checkPageTitle] is a function where you can transform the value from the input into something representable. /// [checkPageTitle] is a function where you can transform the value from the
/// input into something representable.
/// This value will be given when defining the check page widgets. /// This value will be given when defining the check page widgets.
/// If this function is not set, the value will be used as is. /// If this function is not set, the value will be used as is.
/// Example: /// Example:
@ -93,20 +102,26 @@ abstract class FlutterFormInputController<T> {
/// ``` /// ```
String Function(T? value)? checkPageTitle; String Function(T? value)? checkPageTitle;
/// [checkPageDescription] is the same as checkPageTitle but for the description. /// [checkPageDescription] is the same as checkPageTitle but for the
/// description.
/// If null no description will be shown. /// If null no description will be shown.
String Function(T? value)? checkPageDescription; String Function(T? value)? checkPageDescription;
/// [onChanged] can be set to get the value whenever the user changes it. Should not be used to save the value. /// [onChanged] can be set to get the value whenever the user changes it.
/// Should not be used to save the value.
void Function(T? value)? onChanged; void Function(T? value)? onChanged;
/// [onSubmit] can be set to get the value whenever the user submits it. Should not be used to save the value. /// [onSubmit] can be set to get the value whenever the user submits it.
/// Should not be used to save the value.
void Function(T? value)? onSubmit; void Function(T? value)? onSubmit;
/// [onSaved] goes of when the save function is called for the page if [onValidate] return null. /// [onSaved] goes of when the save function is called for the page if
/// [onValidate] return null.
void onSaved(T? value); void onSaved(T? value);
/// [onValidate] is used to validate the given input by the user. /// [onValidate] is used to validate the given input by the user.
String? onValidate( String? onValidate(
T? value, String Function(String, {List<String>? params}) translator); T? value,
String validationMessage,
);
} }

View file

@ -15,25 +15,28 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input;
/// Height sets the height of the inputfield. Default to 425. /// Height sets the height of the inputfield. Default to 425.
class FlutterFormInputCarousel extends FlutterFormInputWidget<int> { class FlutterFormInputCarousel extends FlutterFormInputWidget<int> {
const FlutterFormInputCarousel({ const FlutterFormInputCarousel({
Key? key, required super.controller,
required FlutterFormInputController<int> controller, required this.validationMessage,
Widget? label,
required this.items, required this.items,
super.key,
super.label,
this.validator,
this.height = 425, this.height = 425,
}) : super(key: key, controller: controller, label: label); });
final List<Widget> items; final List<Widget> items;
final double height; final double height;
final String validationMessage;
final String? Function(int?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
return input.FlutterFormInputCarousel( return input.FlutterFormInputCarousel(
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
onChanged: controller.onChanged, onChanged: controller.onChanged,
initialValue: controller.value ?? 0, initialValue: controller.value ?? 0,
items: items, items: items,
@ -42,7 +45,8 @@ class FlutterFormInputCarousel extends FlutterFormInputWidget<int> {
} }
} }
/// Controller for the carousel used by a [FlutterFormInputWidget] used in a [FlutterForm]. /// Controller for the carousel used by a [FlutterFormInputWidget] used in
/// a [FlutterForm].
/// ///
/// Mainly used by [FlutterFormInputCarousel]. /// Mainly used by [FlutterFormInputCarousel].
class FlutterFormInputCarouselController class FlutterFormInputCarouselController
@ -84,7 +88,9 @@ class FlutterFormInputCarouselController
@override @override
String? onValidate( String? onValidate(
int? value, String Function(String, {List<String>? params}) translator) { int? value,
String validationMessage,
) {
if (mandatory) {} if (mandatory) {}
return null; return null;

View file

@ -5,53 +5,85 @@
// ignore_for_file: overridden_fields, annotate_overrides // ignore_for_file: overridden_fields, annotate_overrides
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../../../flutter_form.dart';
/// Input for a dateTime used in a [FlutterForm]. /// Input for a dateTime used in a [FlutterForm].
/// ///
/// Standard controller is [FlutterFormInputDateController]. /// Standard controller is [FlutterFormInputDateController].
class FlutterFormInputDateTime extends FlutterFormInputWidget<String> { class FlutterFormInputDateTime extends FlutterFormInputWidget<String> {
/// Creates a [FlutterFormInputDateTime].
///
/// The [controller], [inputType], [dateFormat] are required parameters.
/// The [label] parameter specifies the label of the input field.
/// The [showIcon] parameter determines whether to show an icon
/// with the input field.
/// The [firstDate] and [lastDate] parameters specify the range
/// of selectable dates.
/// The [initialDate] parameter specifies the initial date for
/// the input field.
/// The [initialDateTimeRange] parameter specifies the initial
/// date time range for the input field.
/// The [icon] parameter specifies the icon to show with the input field.
/// The [enabled] parameter specifies whether the input field is enabled.
/// The [onTapEnabled] parameter specifies whether tapping on
/// the input field is enabled.
const FlutterFormInputDateTime({ const FlutterFormInputDateTime({
Key? key, required super.controller,
required FlutterFormInputController<String> controller,
Widget? label,
this.showIcon = true,
required this.inputType, required this.inputType,
required this.dateFormat, required this.dateFormat,
required this.validationMessage,
this.decoration,
this.style,
super.key,
this.label,
this.showIcon = true,
this.firstDate, this.firstDate,
this.lastDate, this.lastDate,
this.initialDate, this.initialDate,
this.initialDateTimeRange, this.initialDateTimeRange,
this.initialTime,
this.icon = Icons.calendar_today, this.icon = Icons.calendar_today,
this.initialValue,
this.onChanged,
this.onSaved,
this.validator,
this.autovalidateMode = AutovalidateMode.disabled,
this.timePickerEntryMode = TimePickerEntryMode.dial,
this.enabled = true, this.enabled = true,
this.onTapEnabled = true, this.onTapEnabled = true,
}) : super( });
key: key, final TextStyle? style;
controller: controller, final InputDecoration? decoration;
label: label, final Widget? label;
);
final bool showIcon; final bool showIcon;
final input.FlutterFormDateTimeType inputType; final input.FlutterFormDateTimeType inputType;
final DateFormat dateFormat; final DateFormat dateFormat;
final DateTime? initialDate; final DateTime? initialDate;
final DateTimeRange? initialDateTimeRange; final DateTimeRange? initialDateTimeRange;
final TimeOfDay? initialTime;
final DateTime? firstDate; final DateTime? firstDate;
final DateTime? lastDate; final DateTime? lastDate;
final IconData icon; final IconData icon;
final String? initialValue;
final String? Function(String?)? validator;
final String validationMessage;
final void Function(String?)? onSaved;
final void Function(String?)? onChanged;
final AutovalidateMode autovalidateMode;
final TimePickerEntryMode timePickerEntryMode;
final bool enabled; final bool enabled;
final bool onTapEnabled; final bool onTapEnabled;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
return input.FlutterFormInputDateTime( return input.FlutterFormInputDateTime(
enabled: enabled, enabled: enabled,
showIcon: showIcon, showIcon: showIcon,
decoration: decoration,
onTapEnabled: onTapEnabled, onTapEnabled: onTapEnabled,
label: label, label: label,
icon: icon, icon: icon,
@ -59,8 +91,9 @@ class FlutterFormInputDateTime extends FlutterFormInputWidget<String> {
lastDate: lastDate, lastDate: lastDate,
inputType: inputType, inputType: inputType,
onChanged: (value) => controller.onChanged?.call(value), onChanged: (value) => controller.onChanged?.call(value),
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
initialValue: controller.value, initialValue: controller.value,
dateFormat: dateFormat, dateFormat: dateFormat,
initialDate: initialDate, initialDate: initialDate,
@ -69,21 +102,28 @@ class FlutterFormInputDateTime extends FlutterFormInputWidget<String> {
} }
} }
/// Controller for dates used by a [FlutterFormInputWidget] used in a [FlutterForm]. /// Controller for dates used by a [FlutterFormInputWidget] used in a
/// [FlutterForm].
/// ///
/// Mainly used by [FlutterFormInputDateTime]. /// Mainly used by [FlutterFormInputDateTime].
class FlutterFormInputDateTimeController class FlutterFormInputDateTimeController
implements FlutterFormInputController<String> { implements FlutterFormInputController<String> {
/// Creates a [FlutterFormInputDateTimeController].
///
/// The [id], [dateTimeType], and [dateFormat] are required parameters.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [initialDate], [initialDateTimeRange], [checkPageTitle],
/// [checkPageDescription], and [onChanged] parameters are optional.
FlutterFormInputDateTimeController({ FlutterFormInputDateTimeController({
required this.id, required this.id,
required this.dateTimeType,
required this.dateFormat,
this.mandatory = true, this.mandatory = true,
this.value, this.value,
this.checkPageTitle, this.checkPageTitle,
this.checkPageDescription, this.checkPageDescription,
this.initialDate, this.initialDate,
this.initialDateTimeRange, this.initialDateTimeRange,
required this.dateTimeType,
required this.dateFormat,
this.onChanged, this.onChanged,
}) { }) {
if (value != null) { if (value != null) {
@ -118,16 +158,18 @@ class FlutterFormInputDateTimeController
void Function(String? value)? onSubmit; void Function(String? value)? onSubmit;
@override @override
void onSaved(dynamic value) { void onSaved(value) {
this.value = value; this.value = value;
} }
@override @override
String? onValidate(String? value, String? onValidate(
String Function(String, {List<String>? params}) translator) { String? value,
String validationMessage,
) {
if (mandatory) { if (mandatory) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return translator('shell.form.error.empty'); return validationMessage;
} }
} }

View file

@ -0,0 +1,164 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input;
class FlutterFormInputDropdown extends FlutterFormInputWidget<Object> {
const FlutterFormInputDropdown({
required super.controller,
required this.validationMessage,
this.items,
this.selectedItemBuilder,
this.value,
this.hint,
this.disabledHint,
this.onChanged,
this.onTap,
this.elevation = 8,
this.style,
this.icon,
this.iconDisabledColor,
this.iconEnabledColor,
this.iconSize = 24.0,
this.isDense = false,
this.isExpanded = false,
this.itemHeight,
this.focusColor,
this.autofocus = false,
this.dropdownColor,
this.decoration,
this.onSaved,
this.validator,
this.autovalidateMode,
this.menuMaxHeight,
this.enableFeedback,
this.alignment = Alignment.centerLeft,
this.borderRadius,
this.padding,
super.key,
super.focusNode,
super.label,
});
final List<DropdownMenuItem<Object?>>? items;
final List<Widget> Function(BuildContext)? selectedItemBuilder;
final Object? value;
final Widget? hint;
final Widget? disabledHint;
final void Function(Object?)? onChanged;
final void Function()? onTap;
final int elevation;
final TextStyle? style;
final Widget? icon;
final Color? iconDisabledColor;
final Color? iconEnabledColor;
final double iconSize;
final bool isDense;
final bool isExpanded;
final double? itemHeight;
final Color? focusColor;
final bool autofocus;
final Color? dropdownColor;
final InputDecoration? decoration;
final void Function(Object?)? onSaved;
final String? Function(Object?)? validator;
final AutovalidateMode? autovalidateMode;
final double? menuMaxHeight;
final bool? enableFeedback;
final AlignmentGeometry alignment;
final BorderRadius? borderRadius;
final EdgeInsetsGeometry? padding;
final String validationMessage;
@override
Widget build(BuildContext context) {
super.registerController(context);
return input.FlutterFormInputDropdown(
items: items,
selectedItemBuilder: selectedItemBuilder,
value: value,
hint: hint,
disabledHint: disabledHint,
onChanged: controller.onChanged,
onTap: () => onTap?.call(),
elevation: elevation,
style: style,
icon: icon,
iconDisabledColor: iconDisabledColor,
iconEnabledColor: iconEnabledColor,
iconSize: iconSize,
isDense: isDense,
isExpanded: isExpanded,
itemHeight: itemHeight,
focusColor: focusColor,
focusNode: focusNode,
autofocus: autofocus,
dropdownColor: dropdownColor,
decoration: decoration,
onSaved: controller.onSaved,
validator: validator ??
(value) => controller.onValidate(
value,
validationMessage,
),
autovalidateMode: autovalidateMode,
menuMaxHeight: menuMaxHeight,
enableFeedback: enableFeedback,
alignment: alignment,
borderRadius: borderRadius,
padding: padding,
);
}
}
class FlutterFormInputDropdownController
implements FlutterFormInputController<Object> {
FlutterFormInputDropdownController({
required this.id,
this.mandatory = false,
this.value,
this.checkPageTitle,
this.checkPageDescription,
this.onChanged,
this.onSubmit,
});
@override
String? id;
@override
Object? value;
@override
bool mandatory;
@override
String Function(Object? value)? checkPageTitle;
@override
String Function(Object? value)? checkPageDescription;
@override
void Function(Object? value)? onChanged;
@override
void Function(Object? value)? onSubmit;
@override
void onSaved(Object? value) {
this.value = value;
}
@override
String? onValidate(
Object? value,
String validationMessage,
) {
if (mandatory) {
if (value == null) {
return validationMessage;
}
}
return null;
}
}

View file

@ -3,56 +3,68 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
import '../../../../flutter_form.dart';
/// Input for an email used in a [FlutterForm]. /// Input for an email used in a [FlutterForm].
/// ///
/// Standard controller is [FlutterFormInputEmailController]. /// Standard controller is [FlutterFormInputEmailController].
class FlutterFormInputEmail extends FlutterFormInputWidget<String> { class FlutterFormInputEmail extends FlutterFormInputWidget<String> {
/// Creates a [FlutterFormInputEmail].
///
/// The [controller] parameter is required.
/// The [key], [focusNode], [label], and [enabled] parameters are optional.
const FlutterFormInputEmail({ const FlutterFormInputEmail({
Key? key, required super.controller,
required FlutterFormInputController<String> controller, required this.validationMessage,
FocusNode? focusNode, super.key,
Widget? label, super.focusNode,
super.label,
bool? enabled, bool? enabled,
this.validator,
this.decoration,
this.style,
}) : super( }) : super(
key: key,
controller: controller,
focusNode: focusNode,
label: label,
enabled: enabled ?? true, enabled: enabled ?? true,
); );
final InputDecoration? decoration;
final String validationMessage;
final String? Function(String?)? validator;
final TextStyle? style;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
return input.FlutterFormInputPlainText( return input.FlutterFormInputPlainText(
style: style,
enabled: enabled, enabled: enabled,
initialValue: controller.value, initialValue: controller.value,
onSaved: (value) { onSaved: (value) {
controller.onSaved(value); controller.onSaved(value);
}, },
focusNode: focusNode, focusNode: focusNode,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
onChanged: (value) => controller.onChanged?.call(value), onChanged: (value) => controller.onChanged?.call(value),
decoration: InputDecoration( decoration: decoration,
focusColor: Theme.of(context).primaryColor,
label: label ?? const Text("Email"),
),
); );
} }
} }
/// Controller for emails used by a [FlutterFormInputWidget] used in a [FlutterForm]. /// Controller for emails used by a [FlutterFormInputWidget] used in
/// a [FlutterForm].
/// ///
/// Mainly used by [FlutterFormInputEmail]. /// Mainly used by [FlutterFormInputEmail].
class FlutterFormInputEmailController class FlutterFormInputEmailController
implements FlutterFormInputController<String> { implements FlutterFormInputController<String> {
/// Creates a [FlutterFormInputEmailController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription], [onChanged],
/// and [onSubmit] parameters are optional.
FlutterFormInputEmailController({ FlutterFormInputEmailController({
required this.id, required this.id,
this.mandatory = true, this.mandatory = true,
@ -85,22 +97,24 @@ class FlutterFormInputEmailController
void Function(String? value)? onSubmit; void Function(String? value)? onSubmit;
@override @override
void onSaved(dynamic value) { void onSaved(value) {
this.value = value; this.value = value;
} }
@override @override
String? onValidate(String? value, String? onValidate(
String Function(String, {List<String>? params}) translator) { String? value,
String validationMessage,
) {
if (mandatory) { if (mandatory) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return translator('shell.form.error.empty'); return validationMessage;
} }
if (!RegExp( if (!RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+") r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+",
.hasMatch(value)) { ).hasMatch(value)) {
return translator('shell.form.error.email.notValid'); return validationMessage;
} }
} }

View file

@ -3,8 +3,8 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
import '../../../../../flutter_form.dart';
/// Input for a number used in a [FlutterForm]. /// Input for a number used in a [FlutterForm].
/// ///
@ -14,37 +14,48 @@ import '../../../../../flutter_form.dart';
/// Standard controller is [FlutterFormInputNumberPickerController]. /// Standard controller is [FlutterFormInputNumberPickerController].
class FlutterFormInputNumberPicker extends FlutterFormInputWidget<int> { class FlutterFormInputNumberPicker extends FlutterFormInputWidget<int> {
const FlutterFormInputNumberPicker({ const FlutterFormInputNumberPicker({
Key? key, required super.controller,
required FlutterFormInputController<int> controller, required this.validationMessage,
Widget? label, super.key,
FocusNode? focusNode, super.label,
this.validator,
this.minValue = 0, this.minValue = 0,
this.maxValue = 100, this.maxValue = 100,
}) : assert(minValue < maxValue), }) : assert(minValue < maxValue, 'minValue must be less than maxValue');
super(key: key, controller: controller, label: label);
final int minValue; final int minValue;
final int maxValue; final int maxValue;
final String validationMessage;
final String? Function(int?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
return input.FlutterFormInputNumberPicker( return input.FlutterFormInputNumberPicker(
minValue: minValue, minValue: minValue,
maxValue: maxValue, maxValue: maxValue,
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
onChanged: (value) => controller.onChanged?.call(value), onChanged: (value) => controller.onChanged?.call(value),
initialValue: controller.value ?? minValue, initialValue: controller.value ?? minValue,
); );
} }
} }
/// Controller for numbers used by a [FlutterFormInputWidget] used in a
/// [FlutterForm].
///
/// Mainly used by [FlutterFormInputNumberPicker].
class FlutterFormInputNumberPickerController class FlutterFormInputNumberPickerController
implements FlutterFormInputController<int> { implements FlutterFormInputController<int> {
/// Creates a [FlutterFormInputNumberPickerController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription],
/// and [onChanged] parameters are optional.
FlutterFormInputNumberPickerController({ FlutterFormInputNumberPickerController({
required this.id, required this.id,
this.mandatory = true, this.mandatory = true,
@ -82,7 +93,9 @@ class FlutterFormInputNumberPickerController
@override @override
String? onValidate( String? onValidate(
int? value, String Function(String, {List<String>? params}) translator) { int? value,
String validationMessage,
) {
if (mandatory) {} if (mandatory) {}
return null; return null;

View file

@ -3,50 +3,59 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
import '../../../../../flutter_form.dart';
/// Input for a password used in a [FlutterForm]. /// Input for a password used in a [FlutterForm].
/// ///
/// Standard controller is [FlutterFormInputEmailController]. /// Standard controller is [FlutterFormInputEmailController].
class FlutterFormInputPassword extends FlutterFormInputWidget<String> { class FlutterFormInputPassword extends FlutterFormInputWidget<String> {
const FlutterFormInputPassword({ const FlutterFormInputPassword({
Key? key, required super.controller,
required FlutterFormInputController<String> controller, required this.validationMessage,
FocusNode? focusNode, super.key,
Widget? label, super.focusNode,
super.label,
bool? enabled, bool? enabled,
this.validator,
this.decoration,
}) : super( }) : super(
key: key,
controller: controller,
focusNode: focusNode,
label: label,
enabled: enabled ?? true, enabled: enabled ?? true,
); );
final InputDecoration? decoration;
final String validationMessage;
final String? Function(String?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.registerController(context); super.registerController(context);
String Function(String, {List<String>? params}) _ = getTranslator(context);
return input.FlutterFormInputPassword( return input.FlutterFormInputPassword(
enabled: enabled, enabled: enabled,
initialValue: controller.value, initialValue: controller.value,
focusNode: focusNode, focusNode: focusNode,
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
onChanged: (value) => controller.onChanged?.call(value), onChanged: (value) => controller.onChanged?.call(value),
onFieldSubmitted: (value) => controller.onSubmit?.call(value), onFieldSubmitted: (value) => controller.onSubmit?.call(value),
decoration: decoration,
); );
} }
} }
/// Controller for passwords used by a [FlutterFormInputWidget] used in a [ShellFrom]. /// Controller for passwords used by a [FlutterFormInputWidget] used in a
/// [ShellFrom].
/// ///
/// Mainly used by [FlutterFormInputPassword]. /// Mainly used by [FlutterFormInputPassword].
class FlutterFormInputPasswordController class FlutterFormInputPasswordController
implements FlutterFormInputController<String> { implements FlutterFormInputController<String> {
/// Creates a [FlutterFormInputPasswordController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription], [onChanged],
/// and [onSubmit] parameters are optional.
FlutterFormInputPasswordController({ FlutterFormInputPasswordController({
required this.id, required this.id,
this.mandatory = true, this.mandatory = true,
@ -79,20 +88,22 @@ class FlutterFormInputPasswordController
void Function(String? value)? onSubmit; void Function(String? value)? onSubmit;
@override @override
void onSaved(dynamic value) { void onSaved(value) {
this.value = value; this.value = value;
} }
@override @override
String? onValidate(String? value, String? onValidate(
String Function(String, {List<String>? params}) translator) { String? value,
String validationMessage,
) {
if (mandatory) { if (mandatory) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return translator('Field can not be empty'); return validationMessage;
} }
if (value.length < 6) { if (value.length < 6) {
return translator('Field should be atleast 6 characters long'); return validationMessage;
} }
} }

View file

@ -0,0 +1,116 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
// ignore_for_file: overridden_fields
import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input;
/// Input for plain text input used in a [FlutterForm].
///
/// Standard controller is [FlutterFormInputPlainTextController].
class FlutterFormInputPhone extends FlutterFormInputWidget<input.PhoneNumber?> {
const FlutterFormInputPhone({
required super.controller,
required this.validationMessage,
super.key,
super.focusNode,
super.label,
this.decoration,
this.enabled = true,
this.numberFieldStyle,
this.validator,
this.dialCodeSelectorStyle,
this.dialCodeSelectorPadding = const EdgeInsets.only(top: 6),
this.textAlignVertical = TextAlignVertical.top,
});
final InputDecoration? decoration;
@override
final bool enabled;
final TextStyle? numberFieldStyle;
final TextStyle? dialCodeSelectorStyle;
final EdgeInsets dialCodeSelectorPadding;
final TextAlignVertical textAlignVertical;
final String validationMessage;
final String? Function(PhoneNumber?)? validator;
@override
Widget build(BuildContext context) {
super.registerController(context);
var inputDecoration = decoration ??
InputDecoration(
label: label ?? const Text('Phone field'),
);
return input.FlutterFormInputPhone(
numberFieldStyle: numberFieldStyle,
dialCodeSelectorStyle: dialCodeSelectorStyle,
enabled: enabled,
initialValue: controller.value,
onSaved: controller.onSaved,
validator: validator ??
(value) => controller.onValidate(value, validationMessage),
onChanged: (value) => controller.onChanged?.call(value),
onFieldSubmitted: (value) => controller.onSubmit?.call(value),
decoration: inputDecoration,
dialCodeSelectorPadding: dialCodeSelectorPadding,
textAlignVertical: textAlignVertical,
);
}
}
class FlutterFormInputPhoneController
implements FlutterFormInputController<input.PhoneNumber?> {
FlutterFormInputPhoneController({
required this.id,
this.mandatory = true,
this.value,
this.checkPageTitle,
this.checkPageDescription,
this.onChanged,
});
@override
String? id;
@override
input.PhoneNumber? value;
@override
bool mandatory;
@override
String Function(input.PhoneNumber? value)? checkPageTitle;
@override
String Function(input.PhoneNumber? value)? checkPageDescription;
@override
void Function(input.PhoneNumber? value)? onChanged;
@override
void Function(input.PhoneNumber? value)? onSubmit;
@override
void onSaved(input.PhoneNumber? value) {
this.value = value;
}
@override
String? onValidate(
input.PhoneNumber? value,
String validationMessage,
) {
if (mandatory) {
if (value == null || value.number == null) {
return validationMessage;
}
}
return null;
}
}

View file

@ -5,19 +5,25 @@
// ignore_for_file: overridden_fields // ignore_for_file: overridden_fields
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
import '../../../../flutter_form.dart';
/// Input for plain text input used in a [FlutterForm]. /// Input for plain text input used in a [FlutterForm].
/// ///
/// Standard controller is [FlutterFormInputPlainTextController]. /// Standard controller is [FlutterFormInputPlainTextController].
class FlutterFormInputPlainText extends FlutterFormInputWidget<String> { class FlutterFormInputPlainText extends FlutterFormInputWidget<String> {
/// Creates a [FlutterFormInputPlainText].
///
/// The [controller] parameter is required.
/// The [key], [focusNode], [label], [decoration], [textAlignVertical],
/// [expands], [maxLines], [scrollPadding], [maxLength], [keyboardType],
/// [enabled], [style], and [textCapitalization] parameters are optional.
const FlutterFormInputPlainText({ const FlutterFormInputPlainText({
Key? key, required super.controller,
required FlutterFormInputController<String> controller, required this.validationMessage,
FocusNode? focusNode, super.key,
Widget? label, super.focusNode,
super.label,
this.decoration, this.decoration,
this.textAlignVertical, this.textAlignVertical,
this.expands = false, this.expands = false,
@ -25,14 +31,11 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget<String> {
this.scrollPadding, this.scrollPadding,
this.maxLength, this.maxLength,
this.keyboardType, this.keyboardType,
this.validator,
this.enabled = true, this.enabled = true,
this.style, this.style,
this.textCapitalization = TextCapitalization.none, this.textCapitalization = TextCapitalization.none,
}) : super( });
key: key,
controller: controller,
focusNode: focusNode,
label: label);
final InputDecoration? decoration; final InputDecoration? decoration;
final TextAlignVertical? textAlignVertical; final TextAlignVertical? textAlignVertical;
@ -45,16 +48,16 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget<String> {
final bool enabled; final bool enabled;
final TextStyle? style; final TextStyle? style;
final TextCapitalization textCapitalization; final TextCapitalization textCapitalization;
final String validationMessage;
final String? Function(String?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
InputDecoration inputDecoration = decoration ?? var inputDecoration = decoration ??
InputDecoration( InputDecoration(
label: label ?? const Text("Plain text"), label: label ?? const Text('Plain text'),
); );
return input.FlutterFormInputPlainText( return input.FlutterFormInputPlainText(
@ -63,8 +66,9 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget<String> {
scrollPadding: scrollPadding ?? const EdgeInsets.all(20.0), scrollPadding: scrollPadding ?? const EdgeInsets.all(20.0),
initialValue: controller.value, initialValue: controller.value,
focusNode: focusNode, focusNode: focusNode,
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
onChanged: (value) => controller.onChanged?.call(value), onChanged: (value) => controller.onChanged?.call(value),
onFieldSubmitted: (value) => controller.onSubmit?.call(value), onFieldSubmitted: (value) => controller.onSubmit?.call(value),
decoration: inputDecoration, decoration: inputDecoration,
@ -87,30 +91,45 @@ class FlutterFormInputPlainText extends FlutterFormInputWidget<String> {
/// MaxCharacters can be set to set a maximum amount of characters. /// MaxCharacters can be set to set a maximum amount of characters.
class FlutterFormInputMultiLine extends StatelessWidget { class FlutterFormInputMultiLine extends StatelessWidget {
const FlutterFormInputMultiLine({ const FlutterFormInputMultiLine({
Key? key,
required this.controller, required this.controller,
required this.validationMessage,
super.key,
this.focusNode, this.focusNode,
this.label, this.label,
this.hint, this.hint,
this.validator,
this.maxCharacters, this.maxCharacters,
this.enabled = true, this.enabled = true,
this.textCapitalization = TextCapitalization.sentences, this.textCapitalization = TextCapitalization.sentences,
}) : super(key: key); });
/// The controller for the multi-line input.
final FlutterFormInputController<String> controller; final FlutterFormInputController<String> controller;
/// The optional label widget for the input.
final Widget? label; final Widget? label;
/// The optional focus node for the input.
final FocusNode? focusNode; final FocusNode? focusNode;
/// The optional hint text displayed inside the input field.
final String? hint; final String? hint;
/// The optional maximum number of characters allowed in the input field.
final int? maxCharacters; final int? maxCharacters;
/// A flag indicating whether the input field is enabled.
final bool enabled; final bool enabled;
/// The capitalization behavior for the input field.
final TextCapitalization textCapitalization; final TextCapitalization textCapitalization;
@override final String validationMessage;
Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
return input.FlutterFormInputMultiLine( final String? Function(String?)? validator;
@override
Widget build(BuildContext context) => input.FlutterFormInputMultiLine(
enabled: enabled, enabled: enabled,
label: label, label: label,
hint: hint, hint: hint,
@ -119,17 +138,24 @@ class FlutterFormInputMultiLine extends StatelessWidget {
maxCharacters: maxCharacters, maxCharacters: maxCharacters,
onChanged: controller.onChanged, onChanged: controller.onChanged,
onSaved: controller.onSaved, onSaved: controller.onSaved,
validator: (v) => controller.onValidate(v, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
textCapitalization: textCapitalization, textCapitalization: textCapitalization,
); );
} }
}
/// Controller for plain text used by a [FlutterFormInputWidget] used in a [FlutterForm]. /// Controller for plain text used by a [FlutterFormInputWidget] used in a
/// [FlutterForm].
/// ///
/// Mainly used by [FlutterFormInputPlainText]. /// Mainly used by [FlutterFormInputPlainText].
class FlutterFormInputPlainTextController class FlutterFormInputPlainTextController
implements FlutterFormInputController<String> { implements FlutterFormInputController<String> {
/// Creates a [FlutterFormInputPlainTextController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription], [onChanged],
/// and [onSubmit] parameters are optional.
FlutterFormInputPlainTextController({ FlutterFormInputPlainTextController({
required this.id, required this.id,
this.mandatory = false, this.mandatory = false,
@ -167,11 +193,13 @@ class FlutterFormInputPlainTextController
} }
@override @override
String? onValidate(String? value, String? onValidate(
String Function(String, {List<String>? params}) translator) { String? value,
String validationMessage,
) {
if (mandatory) { if (mandatory) {
if (value == null || value.isEmpty) { if (value == null || value.isEmpty) {
return translator('Field can not be empty'); return validationMessage;
} }
} }

View file

@ -3,50 +3,61 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
import '../../../../../flutter_form.dart'; /// Input for a number value between two values via a slider. Used in a
/// [FlutterForm].
/// Input for a number value between two values via a slider. Used in a [FlutterForm].
/// ///
/// Standard controller is [FlutterFormInputSliderController]. /// Standard controller is [FlutterFormInputSliderController].
class FlutterFormInputSlider extends FlutterFormInputWidget<double> { class FlutterFormInputSlider extends FlutterFormInputWidget<double> {
/// Creates a [FlutterFormInputPassword].
///
/// The [controller] parameter is required.
/// The [focusNode] parameter specifies the focus node of the input field.
/// The [label] parameter specifies the label of the input field.
/// The [enabled] parameter specifies whether the input field is enabled.
const FlutterFormInputSlider({ const FlutterFormInputSlider({
Key? key, required super.controller,
required FlutterFormInputController<double> controller, required this.validationMessage,
FocusNode? focusNode, super.key,
Widget? label, super.focusNode,
super.label,
this.validator,
this.minValue = 0, this.minValue = 0,
this.maxValue = 100, this.maxValue = 100,
}) : assert(minValue < maxValue), }) : assert(minValue < maxValue, 'minValue must be less than maxValue');
super(
key: key,
controller: controller,
focusNode: focusNode,
label: label);
final int minValue; final int minValue;
final int maxValue; final int maxValue;
final String validationMessage;
final String? Function(double?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
return input.FlutterFormInputSlider( return input.FlutterFormInputSlider(
focusNode: focusNode, focusNode: focusNode,
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
); );
} }
} }
/// Controller for slider used by a [FlutterFormInputWidget] used in a [FlutterForm]. /// Controller for slider used by a [FlutterFormInputWidget] used in a
/// [FlutterForm].
/// ///
/// Mainly used by [FlutterFormInputSlider]. /// Mainly used by [FlutterFormInputSlider].
class FlutterFormInputSliderController class FlutterFormInputSliderController
implements FlutterFormInputController<double> { implements FlutterFormInputController<double> {
/// Creates a [FlutterFormInputPasswordController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription], [onChanged],
/// and [onSubmit] parameters are optional.
FlutterFormInputSliderController({ FlutterFormInputSliderController({
required this.id, required this.id,
this.mandatory = true, this.mandatory = true,
@ -83,8 +94,10 @@ class FlutterFormInputSliderController
} }
@override @override
String? onValidate(double? value, String? onValidate(
String Function(String, {List<String>? params}) translator) { double? value,
String validationMessage,
) {
if (mandatory) {} if (mandatory) {}
return null; return null;

View file

@ -2,6 +2,8 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// ignore_for_file: avoid_positional_boolean_parameters
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart'; import 'package:flutter_form_wizard/flutter_form.dart';
import 'package:flutter_input_library/flutter_input_library.dart' as input; import 'package:flutter_input_library/flutter_input_library.dart' as input;
@ -11,38 +13,45 @@ import 'package:flutter_input_library/flutter_input_library.dart' as input;
/// Standard controller is [FlutterFormInputSwitchController]. /// Standard controller is [FlutterFormInputSwitchController].
class FlutterFormInputSwitch extends FlutterFormInputWidget<bool> { class FlutterFormInputSwitch extends FlutterFormInputWidget<bool> {
const FlutterFormInputSwitch({ const FlutterFormInputSwitch({
Key? key, required super.controller,
required FlutterFormInputController<bool> controller, required this.validationMessage,
FocusNode? focusNode, super.key,
Widget? label, super.focusNode,
}) : super( super.label,
key: key, this.validator,
controller: controller, });
focusNode: focusNode,
label: label); final String validationMessage;
final String? Function(bool?)? validator;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String Function(String, {List<String>? params}) _ = getTranslator(context);
super.registerController(context); super.registerController(context);
return input.FlutterFormInputBool( return input.FlutterFormInputBool(
focusNode: focusNode, focusNode: focusNode,
onSaved: (value) => controller.onSaved(value), onSaved: controller.onSaved,
onChanged: controller.onChanged, onChanged: controller.onChanged,
validator: (value) => controller.onValidate(value, _), validator: validator ??
(value) => controller.onValidate(value, validationMessage),
initialValue: controller.value ?? false, initialValue: controller.value ?? false,
widgetType: input.BoolWidgetType.switchWidget, widgetType: input.BoolWidgetType.switchWidget,
); );
} }
} }
/// Controller for the switch used by a [FlutterFormInputWidget] used in a [FlutterForm]. /// Controller for the switch used by a [FlutterFormInputWidget] used in a
/// [FlutterForm].
/// ///
/// Mainly used by [FlutterFormInputSwitch]. /// Mainly used by [FlutterFormInputSwitch].
class FlutterFormInputSwitchController class FlutterFormInputSwitchController
implements FlutterFormInputController<bool> { implements FlutterFormInputController<bool> {
/// Creates a [FlutterFormInputSwitchController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription],
/// and [onChanged] parameters are optional.
FlutterFormInputSwitchController({ FlutterFormInputSwitchController({
required this.id, required this.id,
this.mandatory = true, this.mandatory = true,
@ -80,7 +89,8 @@ class FlutterFormInputSwitchController
@override @override
String? onValidate( String? onValidate(
bool? value, String Function(String, {List<String>? params}) translator) { bool? value,
return null; String validationMessage,
} ) =>
null;
} }

View file

@ -2,11 +2,17 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
export 'package:flutter_input_library/flutter_input_library.dart'
show PhoneNumber;
export 'input_carousel/input_carousel.dart'; export 'input_carousel/input_carousel.dart';
export 'input_date_picker/input_date_picker.dart';
export 'input_dropdown.dart';
export 'input_email.dart'; export 'input_email.dart';
export 'input_number_picker/input_number_picker.dart'; export 'input_number_picker/input_number_picker.dart';
export 'input_password/input_password.dart'; export 'input_password/input_password.dart';
export 'input_phone.dart';
export 'input_plain_text.dart'; export 'input_plain_text.dart';
export 'input_slider/input_slider.dart'; export 'input_slider/input_slider.dart';
export 'input_switch/input_switch.dart'; export 'input_switch/input_switch.dart';
export 'input_date_picker/input_date_picker.dart'; export 'multiple_choice.dart';

View file

@ -0,0 +1,159 @@
import 'package:flutter/material.dart';
import 'package:flutter_form_wizard/flutter_form.dart';
class FlutterFormInputMultipleChoice extends FlutterFormInputWidget<String> {
/// Creates a [FlutterFormInputMultipleChoice].
///
/// The [controller], [options], [builder], [validationMessage] parameters
/// are required.
/// The [key], [focusNode], [label] are optional.
const FlutterFormInputMultipleChoice({
required super.controller,
required this.options,
required this.builder,
required this.validationMessage,
super.focusNode,
super.label,
super.key,
this.mainAxisExtent,
this.childAspectRatio = 1,
this.mainAxisSpacing = 0,
this.crossAxisSpacing = 0,
this.crossAxisCount = 3,
this.height,
this.shrinkwrap = true,
this.validator,
});
final List<String> options;
final double? mainAxisExtent;
final double childAspectRatio;
final double mainAxisSpacing;
final double crossAxisSpacing;
final int crossAxisCount;
final double? height;
final bool shrinkwrap;
final Widget Function(
BuildContext context,
int index,
ValueNotifier<int?> selectedIndex,
FlutterFormInputController controller,
List<String> options,
FormFieldState<String> state,
) builder;
final String? Function(String? value, String validationMessage)? validator;
final String validationMessage;
@override
Widget build(BuildContext context) {
super.registerController(context);
var selectedIndex = ValueNotifier<int?>(null);
return FormField<String>(
onSaved: controller.onSaved,
validator: (value) =>
validator?.call(value, validationMessage) ??
controller.onValidate(value, validationMessage),
builder: (state) => SizedBox(
height: height,
child: Column(
children: [
GridView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: options.length,
shrinkWrap: shrinkwrap,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
mainAxisExtent: mainAxisExtent,
childAspectRatio: childAspectRatio,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
crossAxisCount: crossAxisCount,
),
itemBuilder: (context, index) => ListenableBuilder(
listenable: selectedIndex,
builder: (context, widget) => builder.call(
context,
index,
selectedIndex,
controller,
options,
state,
),
),
),
if (state.hasError)
Text(
state.errorText!,
style: const TextStyle(
color: Color(0xFFAD3645),
fontSize: 14.0,
fontWeight: FontWeight.bold,
),
)
else
const SizedBox.shrink(),
],
),
),
);
}
}
class FlutterFormInputMultipleChoiceController
implements FlutterFormInputController<String> {
/// Creates a [FlutterFormInputMultipleChoiceController].
///
/// The [id] parameter specifies the unique identifier for the controller.
/// The [mandatory] parameter specifies whether the input is mandatory.
/// The [value], [checkPageTitle], [checkPageDescription], [onChanged],
/// and [onSubmit] parameters are optional.
FlutterFormInputMultipleChoiceController({
required this.id,
this.mandatory = false,
this.value,
this.checkPageTitle,
this.checkPageDescription,
this.onChanged,
this.onSubmit,
});
@override
String? id;
@override
String? value;
@override
bool mandatory;
@override
String Function(String? value)? checkPageTitle;
@override
String Function(String? value)? checkPageDescription;
@override
void Function(String? value)? onChanged;
@override
void Function(String? value)? onSubmit;
@override
void onSaved(String? value) {
this.value = value;
}
@override
String? onValidate(
String? value,
String validationMessage,
) {
if (mandatory) {
if (value == null || value.isEmpty) {
return validationMessage;
}
}
return null;
}
}

View file

@ -2,27 +2,47 @@
// //
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// ignore_for_file: avoid_positional_boolean_parameters
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// The options used to set parameters to a [FlutterForm]. /// The options used to set parameters to a [FlutterForm].
/// ///
/// The pages determine what pages the pageview will contain via a [List] of [FlutterFormPage]s. /// The pages determine what pages the pageview will contain via a [List] of
/// [FlutterFormPage]s.
/// ///
/// Using a checkpage gives the ability for the user to check all input values before commiting by [CheckPage]. /// Using a checkpage gives the ability for the user to check all input values
/// before commiting by [CheckPage].
/// If [checkPage] is null no check page will be shown. /// If [checkPage] is null no check page will be shown.
/// ///
/// [nextButton] and [backButton] are both a way to give controls to user. /// [nextButton] and [backButton] are both a way to give controls to user.
/// Both are just plain widgets used in a [Stack]. So the widgets can be aligned where ever. /// Both are just plain widgets used in a [Stack]. So the widgets can be
/// aligned where ever.
/// The formcontroller of [FlutterForm] should be used to give control to the widgets/buttons. /// The formcontroller of [FlutterForm] should be used to give control to the widgets/buttons.
/// ///
/// [onFinished] and [onNext] are both callbacks which give the users results. /// [onFinished] and [onNext] are both callbacks which give the users results.
/// [onNext] is called when the user goes to the next page. /// [onNext] is called when the user goes to the next page.
/// [onFinished] is called when the form is finished. When checkpage is set [onFinished] is called when the checkpage is finished. /// [onBack] is called when the user goes back a page.
/// [onFinished] is called when the form is finished. When checkpage is set
/// [onFinished] is called when the checkpage is finished.
/// ///
/// [scrollDirection] can be set to change the Axis on which the pageview slides. Defaults to horizontal. /// [scrollDirection] can be set to change the Axis on which the pageview
/// slides. Defaults to horizontal.
/// ///
/// [scrollPhysics] can be set to set the scroll phyisics of the scroll views in each page. Default to [ClampingScrollPhysics]. /// [scrollPhysics] can be set to set the scroll phyisics of the scroll views
/// in each page. Default to [ClampingScrollPhysics].
class FlutterFormOptions { class FlutterFormOptions {
const FlutterFormOptions({
required this.pages,
required this.onFinished,
required this.onNext,
this.onBack,
this.checkPage,
this.nextButton,
this.backButton,
this.scrollDirection = Axis.horizontal,
this.scrollPhysics,
});
final List<FlutterFormPage> pages; final List<FlutterFormPage> pages;
final CheckPage? checkPage; final CheckPage? checkPage;
@ -31,32 +51,26 @@ class FlutterFormOptions {
backButton; backButton;
final void Function(Map<int, Map<String, dynamic>>) onFinished; final void Function(Map<int, Map<String, dynamic>>) onFinished;
final void Function(int pageNumber, Map<String, dynamic>) onNext; final void Function(int pageNumber, Map<String, dynamic>) onNext;
/// [onBack] is called when the user goes back a page. The [pageNumber] is the
/// page the user is going back to. Not the page that the user was on when the
/// user pressed the back button.
final void Function(int pageNumber)? onBack;
final Axis scrollDirection; final Axis scrollDirection;
final ScrollPhysics? scrollPhysics; final ScrollPhysics? scrollPhysics;
const FlutterFormOptions({
required this.pages,
this.checkPage,
this.nextButton,
this.backButton,
required this.onFinished,
required this.onNext,
this.scrollDirection = Axis.horizontal,
this.scrollPhysics,
});
} }
/// The defines every page in a [FlutterForm]. /// The defines every page in a [FlutterForm].
class FlutterFormPage { class FlutterFormPage {
final Widget child;
FlutterFormPage({ FlutterFormPage({
required this.child, required this.child,
}); });
final Widget child;
} }
/// [CheckPage] is used to set a check page at the end of a [FlutterForm]. /// [CheckPage] is used to set a check page at the end of a [FlutterForm].
/// A [CheckPage] is a page where the user can check all input values before commiting. /// A [CheckPage] is a page where the user can check all input values before
/// commiting.
/// ///
/// [title] is the widget shown at the top of the page. /// [title] is the widget shown at the top of the page.
/// ///
@ -64,22 +78,28 @@ class FlutterFormPage {
/// ///
/// [inputCheckWidget] determines how every input is represented on the page. /// [inputCheckWidget] determines how every input is represented on the page.
/// [title] is the value given in the input. /// [title] is the value given in the input.
/// This input can be modified by setting the [checkPageTitle] of that input controller. /// This input can be modified by setting the [checkPageTitle] of that input
/// controller.
/// ///
/// Same for the [description] but if the description is not set in the input controller no description will be given. /// Same for the [description] but if the description is not set in the input
/// controller no description will be given.
/// ///
/// [onPressed] can be set so that when the user triggers it the user will be sent back to the page including the input. /// [onPressed] can be set so that when the user triggers it the user will be
/// Here the user can modify the input and save it. Afterwards the user will be sent back to the check page. /// sent back to the page including the input.
/// Here the user can modify the input and save it. Afterwards the user will be
/// sent back to the check page.
class CheckPage { class CheckPage {
final Widget? title;
final MainAxisAlignment mainAxisAlignment;
final Widget Function(
String id, String title, String? description, Function onPressed)?
inputCheckWidget;
const CheckPage({ const CheckPage({
this.title, this.title,
this.inputCheckWidget, this.inputCheckWidget,
this.mainAxisAlignment = MainAxisAlignment.start, this.mainAxisAlignment = MainAxisAlignment.start,
}); });
final Widget? title;
final MainAxisAlignment mainAxisAlignment;
final Widget Function(
String id,
String title,
String? description,
Function onPressed,
)? inputCheckWidget;
} }

View file

@ -1,53 +0,0 @@
// SPDX-FileCopyrightText: 2022 Iconica
//
// SPDX-License-Identifier: BSD-3-Clause
import 'package:flutter/material.dart';
abstract class TranslationService {
TranslationService._();
String translate(
BuildContext context,
String key, {
List<String>? params,
});
String number(double value);
}
typedef Translator = String Function(
String, {
List<String>? params,
});
class ShellTranslationService implements TranslationService {
@override
String number(double value) {
return value.toStringAsFixed(2);
}
@override
String translate(BuildContext context, String key, {List<String>? params}) {
return key;
}
}
Translator getTranslator(BuildContext context) {
try {
var translator = ShellTranslationService().translate;
return (
String key, {
List<String>? params,
}) {
return translator(context, key, params: params);
};
} catch (e) {
return (
String key, {
List<String>? params,
}) {
return key;
};
}
}

View file

@ -1,12 +1,12 @@
name: flutter_form_wizard name: flutter_form_wizard
description: A new Flutter package project. description: A new Flutter package project.
version: 6.2.2 version: 6.6.0
homepage: https://github.com/Iconica-Development/flutter_form_wizard homepage: https://github.com/Iconica-Development/flutter_form_wizard
publish_to: none publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub
environment: environment:
sdk: ">=2.18.0 <3.0.0" sdk: ">=3.0.0 <4.0.0"
flutter: ">=1.17.0" flutter: ">=1.17.0"
dependencies: dependencies:
@ -14,46 +14,16 @@ dependencies:
sdk: flutter sdk: flutter
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
intl: ^0.18.0 intl: ">=0.18.0 <1.0.0"
flutter_input_library: flutter_input_library:
git: hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub
url: https://github.com/Iconica-Development/flutter_input_library version: ^3.4.0
ref: 3.0.0
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: 6.0.0
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

View file

@ -8,7 +8,7 @@ import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
testWidgets('Normal walk through without check page', (tester) async { testWidgets('Normal walk through without check page', (tester) async {
FlutterFormController formController = FlutterFormController(); var formController = FlutterFormController();
var testField1Controller = FlutterFormInputPlainTextController( var testField1Controller = FlutterFormInputPlainTextController(
id: 'Field1', id: 'Field1',
@ -28,21 +28,21 @@ void main() {
home: Material( home: Material(
child: FlutterForm( child: FlutterForm(
options: FlutterFormOptions( options: FlutterFormOptions(
nextButton: (pageNumber, checkingPages) { nextButton: (pageNumber, checkingPages) => Align(
return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
await formController.autoNextStep(); await formController.autoNextStep();
}, },
child: Text(pageNumber == 0 child: Text(
pageNumber == 0
? 'next1' ? 'next1'
: pageNumber == 1 : pageNumber == 1
? 'next2' ? 'next2'
: 'finish'), : 'finish',
),
),
), ),
);
},
onFinished: (Map<int, Map<String, dynamic>> results) { onFinished: (Map<int, Map<String, dynamic>> results) {
onFinishResults = results; onFinishResults = results;
}, },
@ -56,6 +56,7 @@ void main() {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text('Field1Label'), label: const Text('Field1Label'),
controller: testField1Controller, controller: testField1Controller,
validationMessage: 'Please fill in this field',
), ),
), ),
), ),
@ -64,6 +65,7 @@ void main() {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text('Field2Label'), label: const Text('Field2Label'),
controller: testField2Controller, controller: testField2Controller,
validationMessage: 'Please fill in this field',
), ),
), ),
), ),
@ -76,7 +78,9 @@ void main() {
); );
await tester.enterText( await tester.enterText(
find.widgetWithText(TextFormField, 'Field1Label'), 'Field1Input'); find.widgetWithText(TextFormField, 'Field1Label'),
'Field1Input',
);
await tester.tap(find.widgetWithText(ElevatedButton, 'next1')); await tester.tap(find.widgetWithText(ElevatedButton, 'next1'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
@ -84,21 +88,26 @@ void main() {
expect({'Field1': 'Field1Input'}, onNextResults); expect({'Field1': 'Field1Input'}, onNextResults);
await tester.enterText( await tester.enterText(
find.widgetWithText(TextFormField, 'Field2Label'), 'Field2Input'); find.widgetWithText(TextFormField, 'Field2Label'),
'Field2Input',
);
await tester.tap(find.widgetWithText(ElevatedButton, 'next2')); await tester.tap(find.widgetWithText(ElevatedButton, 'next2'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(1, onNextPageNumber); expect(1, onNextPageNumber);
expect({'Field2': 'Field2Input'}, onNextResults); expect({'Field2': 'Field2Input'}, onNextResults);
expect({ expect(
{
0: {'Field1': 'Field1Input'}, 0: {'Field1': 'Field1Input'},
1: {'Field2': 'Field2Input'} 1: {'Field2': 'Field2Input'},
}, onFinishResults); },
onFinishResults,
);
}); });
testWidgets('Normal walk through with check page', (tester) async { testWidgets('Normal walk through with check page', (tester) async {
FlutterFormController formController = FlutterFormController(); var formController = FlutterFormController();
var testField1Controller = FlutterFormInputPlainTextController( var testField1Controller = FlutterFormInputPlainTextController(
id: 'Field1', id: 'Field1',
@ -119,21 +128,21 @@ void main() {
child: FlutterForm( child: FlutterForm(
options: FlutterFormOptions( options: FlutterFormOptions(
checkPage: const CheckPage(), checkPage: const CheckPage(),
nextButton: (pageNumber, checkingPages) { nextButton: (pageNumber, checkingPages) => Align(
return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
await formController.autoNextStep(); await formController.autoNextStep();
}, },
child: Text(pageNumber == 0 child: Text(
pageNumber == 0
? 'next1' ? 'next1'
: pageNumber == 1 : pageNumber == 1
? 'next2' ? 'next2'
: 'finish'), : 'finish',
),
),
), ),
);
},
onFinished: (Map<int, Map<String, dynamic>> results) { onFinished: (Map<int, Map<String, dynamic>> results) {
onFinishResults = results; onFinishResults = results;
}, },
@ -147,6 +156,7 @@ void main() {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text('Field1Label'), label: const Text('Field1Label'),
controller: testField1Controller, controller: testField1Controller,
validationMessage: 'Please fill in this field',
), ),
), ),
), ),
@ -155,6 +165,7 @@ void main() {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text('Field2Label'), label: const Text('Field2Label'),
controller: testField2Controller, controller: testField2Controller,
validationMessage: 'Please fill in this field',
), ),
), ),
), ),
@ -167,7 +178,9 @@ void main() {
); );
await tester.enterText( await tester.enterText(
find.widgetWithText(TextFormField, 'Field1Label'), 'Field1Input'); find.widgetWithText(TextFormField, 'Field1Label'),
'Field1Input',
);
await tester.tap(find.widgetWithText(ElevatedButton, 'next1')); await tester.tap(find.widgetWithText(ElevatedButton, 'next1'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
@ -175,7 +188,9 @@ void main() {
expect({'Field1': 'Field1Input'}, onNextResults); expect({'Field1': 'Field1Input'}, onNextResults);
await tester.enterText( await tester.enterText(
find.widgetWithText(TextFormField, 'Field2Label'), 'Field2Input'); find.widgetWithText(TextFormField, 'Field2Label'),
'Field2Input',
);
await tester.tap(find.widgetWithText(ElevatedButton, 'next2')); await tester.tap(find.widgetWithText(ElevatedButton, 'next2'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
@ -186,24 +201,29 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.enterText( await tester.enterText(
find.widgetWithText(TextFormField, 'Field1Label'), 'Field1Input2'); find.widgetWithText(TextFormField, 'Field1Label'),
'Field1Input2',
);
await tester.tap(find.widgetWithText(ElevatedButton, 'next1')); await tester.tap(find.widgetWithText(ElevatedButton, 'next1'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(0, onNextPageNumber); expect(0, onNextPageNumber);
expect({'Field1': 'Field1Input2'}, onNextResults); expect({'Field1': 'Field1Input2'}, onNextResults);
await tester.tap(find.widgetWithText(ElevatedButton, "finish")); await tester.tap(find.widgetWithText(ElevatedButton, 'finish'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect({ expect(
{
0: {'Field1': 'Field1Input2'}, 0: {'Field1': 'Field1Input2'},
1: {'Field2': 'Field2Input'} 1: {'Field2': 'Field2Input'},
}, onFinishResults); },
onFinishResults,
);
}); });
testWidgets('Wrong input with mandatory validator', (tester) async { testWidgets('Wrong input with mandatory validator', (tester) async {
FlutterFormController formController = FlutterFormController(); var formController = FlutterFormController();
var testField1Controller = FlutterFormInputPlainTextController( var testField1Controller = FlutterFormInputPlainTextController(
id: 'Field1', id: 'Field1',
@ -220,8 +240,7 @@ void main() {
home: Material( home: Material(
child: FlutterForm( child: FlutterForm(
options: FlutterFormOptions( options: FlutterFormOptions(
nextButton: (pageNumber, checkingPages) { nextButton: (pageNumber, checkingPages) => Align(
return Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
@ -229,8 +248,7 @@ void main() {
}, },
child: const Text('finish'), child: const Text('finish'),
), ),
); ),
},
onFinished: (Map<int, Map<String, dynamic>> results) { onFinished: (Map<int, Map<String, dynamic>> results) {
// print('finished results: $results'); // print('finished results: $results');
onFinishResults = results; onFinishResults = results;
@ -246,6 +264,7 @@ void main() {
child: FlutterFormInputPlainText( child: FlutterFormInputPlainText(
label: const Text('Field1Label'), label: const Text('Field1Label'),
controller: testField1Controller, controller: testField1Controller,
validationMessage: 'Field can not be empty',
), ),
), ),
), ),
@ -263,20 +282,25 @@ void main() {
expect(null, onNextPageNumber); expect(null, onNextPageNumber);
expect(null, onNextResults); expect(null, onNextResults);
final errorMessageFinder = find.text('Field can not be empty'); var errorMessageFinder = find.text('Field can not be empty');
expect(errorMessageFinder, findsOneWidget); expect(errorMessageFinder, findsOneWidget);
await tester.enterText( await tester.enterText(
find.widgetWithText(TextFormField, 'Field1Label'), 'Field1Input'); find.widgetWithText(TextFormField, 'Field1Label'),
'Field1Input',
);
await tester.tap(find.widgetWithText(ElevatedButton, 'finish')); await tester.tap(find.widgetWithText(ElevatedButton, 'finish'));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(0, onNextPageNumber); expect(0, onNextPageNumber);
expect({'Field1': 'Field1Input'}, onNextResults); expect({'Field1': 'Field1Input'}, onNextResults);
expect({ expect(
{
0: {'Field1': 'Field1Input'}, 0: {'Field1': 'Field1Input'},
}, onFinishResults); },
onFinishResults,
);
}); });
} }