diff --git a/CHANGELOG.md b/CHANGELOG.md index 901e8ec..501e26b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,21 @@ SPDX-FileCopyrightText: 2022 Iconica SPDX-License-Identifier: GPL-3.0-or-later --> +# 2.0.0 +- feat(buttons): Added the possiblity to only have a next button by return zero on the previous button builder +- feat: exposed input decoration in AuthTextField +- feat: added title widget and login button builder +- feat(bool): Add a boolean field. Can be used for accepting terms and conditions +- feat(pass): Add dedicated password screen that manages state internally +- fix: Small refactor and brought back the normal alignment for the screens +- fix: Fixed alignment and spacing when opening keyboard +- feat: add auth drop down field +- fix: added step to button builders +- fix: exported auth_pass_field +- feat: added validation to disable next button +- feat(auth-screen): add flexible spacing between fields +- fix(keyboard-focus): add unfocus for onPrevious + # 1.2.0 - feat: Added the ability to have an async register function so you can call it asynchronous. diff --git a/example/android/build.gradle b/example/android/build.gradle index 83ae220..3cdaac9 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/lib/main.dart b/example/lib/main.dart index b5d639a..2894316 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,27 +2,81 @@ // // SPDX-License-Identifier: BSD-3-Clause +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_registration/flutter_registration.dart'; import 'example_registration_repository.dart'; void main() { runApp( - const MaterialApp( - home: FlutterRegistrationDemo(), + MaterialApp( + theme: ThemeData( + inputDecorationTheme: const InputDecorationTheme( + errorStyle: TextStyle(color: Colors.red), + ), + ), + home: const FlutterRegistrationDemo(), ), ); } -class FlutterRegistrationDemo extends StatelessWidget { +class FlutterRegistrationDemo extends StatefulWidget { const FlutterRegistrationDemo({Key? key}) : super(key: key); + @override + State createState() => + _FlutterRegistrationDemoState(); +} + +class _FlutterRegistrationDemoState extends State { + late List steps; + + @override + void initState() { + super.initState(); + + steps = RegistrationOptions.getDefaultSteps(); + + steps[1].fields.add( + AuthBoolField( + name: 'termsConditions', + widgetType: BoolWidgetType.checkbox, + validators: [ + (value) { + if (value == null || !value) { + return 'Required'; + } + + return null; + }, + ], + rightWidget: Text.rich( + TextSpan( + text: 'I agree with the ', + children: [ + TextSpan( + text: 'terms & conditions', + style: const TextStyle( + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + debugPrint('Open terms and conditions'); + }, + ), + ], + ), + ), + ), + ); + } + @override Widget build(BuildContext context) { return RegistrationScreen( registrationOptions: RegistrationOptions( registrationRepository: ExampleRegistrationRepository(), - registrationSteps: RegistrationOptions.getDefaultSteps(), + registrationSteps: steps, afterRegistration: () { debugPrint('Registered!'); }, diff --git a/example/pubspec.lock b/example/pubspec.lock index d9807c5..e221bae 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,18 +37,18 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.6" fake_async: dependency: transitive description: @@ -62,14 +62,23 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_input_library: + dependency: transitive + description: + path: "." + ref: "3.0.0" + resolved-ref: "7d1880b8e348435fc8dc2d3a11f936b224cbd5b7" + url: "https://github.com/Iconica-Development/flutter_input_library" + source: git + version: "3.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "2.0.3" flutter_localizations: dependency: transitive description: flutter @@ -81,7 +90,7 @@ packages: path: ".." relative: true source: path - version: "0.5.0" + version: "1.2.0" flutter_test: dependency: "direct dev" description: flutter @@ -99,10 +108,10 @@ packages: dependency: transitive description: name: lints - sha256: "5cfd6509652ff5e7fe149b6df4859e687fca9048437857cb2e65c8d780f396e3" + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.1" matcher: dependency: transitive description: @@ -123,10 +132,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -152,18 +161,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -184,10 +193,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -200,10 +209,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=1.17.0" diff --git a/lib/flutter_registration.dart b/lib/flutter_registration.dart index eac231f..d599a3c 100644 --- a/lib/flutter_registration.dart +++ b/lib/flutter_registration.dart @@ -10,5 +10,10 @@ export 'src/model/auth_exception.dart'; export 'src/model/auth_field.dart'; export 'src/model/auth_step.dart'; export 'src/model/auth_text_field.dart'; +export 'src/model/auth_bool_field.dart'; +export 'src/model/auth_drop_down.dart'; +export 'src/model/auth_pass_field.dart'; export 'src/registration_screen.dart'; export 'src/service/registration_repository.dart'; +export 'package:flutter_input_library/flutter_input_library.dart' + show BoolWidgetType; diff --git a/lib/src/auth_screen.dart b/lib/src/auth_screen.dart index d007268..12b2bf1 100644 --- a/lib/src/auth_screen.dart +++ b/lib/src/auth_screen.dart @@ -9,7 +9,7 @@ import 'package:flutter_registration/flutter_registration.dart'; class AuthScreen extends StatefulWidget { const AuthScreen({ - required this.title, + required this.appBarTitle, required this.steps, required this.submitBtnTitle, required this.nextBtnTitle, @@ -19,12 +19,18 @@ class AuthScreen extends StatefulWidget { this.customBackgroundColor, this.nextButtonBuilder, this.previousButtonBuilder, + this.titleWidget, + this.loginButton, + this.titleFlex, + this.formFlex, + this.beforeTitleFlex, + this.afterTitleFlex, super.key, }) : assert(steps.length > 0, 'At least one step is required'); - final String title; + final String appBarTitle; final Future Function({ - required HashMap values, + required HashMap values, required void Function(int? pageToReturn) onError, }) onFinish; final List steps; @@ -33,8 +39,16 @@ class AuthScreen extends StatefulWidget { final String previousBtnTitle; final AppBar? customAppBar; final Color? customBackgroundColor; - final Widget Function(Future Function(), String)? nextButtonBuilder; - final Widget Function(VoidCallback, String)? previousButtonBuilder; + final Widget Function(Future Function()? onPressed, String label, + int step, bool enabled)? nextButtonBuilder; + final Widget? Function(VoidCallback onPressed, String label, int step)? + previousButtonBuilder; + final Widget? titleWidget; + final Widget? loginButton; + final int? titleFlex; + final int? formFlex; + final int? beforeTitleFlex; + final int? afterTitleFlex; @override State createState() => _AuthScreenState(); @@ -45,14 +59,17 @@ class _AuthScreenState extends State { final _pageController = PageController(); final _animationDuration = const Duration(milliseconds: 300); final _animationCurve = Curves.ease; + bool _formValid = false; AppBar get _appBar => widget.customAppBar ?? AppBar( - title: Text(widget.title), + title: Text(widget.appBarTitle), ); void onPrevious() { + FocusScope.of(context).unfocus(); + _validate(_pageController.page!.toInt() - 1); _pageController.previousPage( duration: _animationDuration, curve: _animationCurve, @@ -69,12 +86,11 @@ class _AuthScreenState extends State { FocusScope.of(context).unfocus(); if (widget.steps.last == step) { - var values = HashMap(); + var values = HashMap(); for (var step in widget.steps) { for (var field in step.fields) { - values[field.name] = - (field as AuthTextField).textController.value.text; + values[field.name] = field.value; } } @@ -89,6 +105,7 @@ class _AuthScreenState extends State { return; } else { + _validate(_pageController.page!.toInt() + 1); _pageController.nextPage( duration: _animationDuration, curve: _animationCurve, @@ -96,6 +113,29 @@ class _AuthScreenState extends State { } } + void _validate(int currentPage) { + bool isStepValid = true; + + // Loop through each field in the current step + for (var field in widget.steps[currentPage].fields) { + for (var validator in field.validators) { + String? validationResult = validator(field.value); + if (validationResult != null) { + // If any validator returns an error, mark step as invalid and break + isStepValid = false; + break; + } + } + if (!isStepValid) { + break; // No need to check further fields if one is already invalid + } + } + + setState(() { + _formValid = isStepValid; + }); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -107,100 +147,150 @@ class _AuthScreenState extends State { physics: const NeverScrollableScrollPhysics(), controller: _pageController, children: [ - for (AuthStep step in widget.steps) + for (var i = 0; i < widget.steps.length; i++) Column( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Flexible( - child: Center( - child: ListView( - physics: const ClampingScrollPhysics(), - shrinkWrap: true, - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 30.0, - ), + if (widget.titleWidget != null) ...[ + Expanded( + flex: widget.titleFlex ?? 1, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - for (AuthField field in step.fields) - Align( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (field.title != null) ...[ - field.title!, - ], - field.build(), - ], - ), - ) + Expanded( + flex: widget.beforeTitleFlex ?? 3, + child: Container(), + ), + widget.titleWidget!, + Expanded( + flex: widget.afterTitleFlex ?? 2, + child: Container(), + ), ], ), ), - ), - Padding( - padding: const EdgeInsets.only( - top: 15.0, - bottom: 30.0, - left: 30.0, - right: 30.0, - ), - child: Row( - mainAxisAlignment: widget.steps.first != step - ? MainAxisAlignment.spaceBetween - : MainAxisAlignment.end, + ], + Expanded( + flex: widget.formFlex ?? 3, + child: Column( children: [ - if (widget.steps.first != step) - widget.previousButtonBuilder?.call( - onPrevious, - widget.previousBtnTitle, - ) ?? - ElevatedButton( - onPressed: onPrevious, - child: Row( - children: [ - const Icon( - Icons.arrow_back, - size: 18, - ), - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: Text(widget.previousBtnTitle), - ), - ], - ), - ), - widget.nextButtonBuilder?.call( - () async { - await onNext(step); - }, - widget.steps.last == step - ? widget.submitBtnTitle - : widget.nextBtnTitle, - ) ?? - ElevatedButton( - onPressed: () async { - await onNext(step); - }, - child: Row( - children: [ - Text( - widget.steps.last == step - ? widget.submitBtnTitle - : widget.nextBtnTitle, - ), - const Padding( - padding: EdgeInsets.only(left: 4.0), - child: Icon( - Icons.arrow_forward, - size: 18, - ), - ), + for (AuthField field in widget.steps[i].fields) + Align( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (field.title != null) ...[ + field.title!, ], - ), + field.build(context, () { + _validate(i); + }), + ], ), + ), ], ), - ) + ), + Padding( + padding: EdgeInsets.only( + top: 15.0, + bottom: 30.0, + left: widget.previousButtonBuilder == null && + widget.steps.first != widget.steps[i] + ? 30.0 + : 0.0, + right: widget.nextButtonBuilder == null && + widget.previousButtonBuilder == null + ? 30.0 + : 0.0, + ), + child: Column( + children: [ + Row( + mainAxisAlignment: + (widget.previousButtonBuilder != null && + widget.previousButtonBuilder?.call( + onPrevious, + widget.previousBtnTitle, + i, + ) == + null) + ? MainAxisAlignment.spaceAround + : widget.steps.first != widget.steps[i] + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.end, + children: [ + if (widget.previousButtonBuilder == null) ...[ + if (widget.steps.first != widget.steps[i]) + ElevatedButton( + onPressed: onPrevious, + child: Row( + children: [ + const Icon( + Icons.arrow_back, + size: 18, + ), + Padding( + padding: + const EdgeInsets.only(left: 4.0), + child: Text(widget.previousBtnTitle), + ), + ], + ), + ), + ] else if (widget.previousButtonBuilder?.call( + onPrevious, widget.previousBtnTitle, i) != + null) ...[ + widget.previousButtonBuilder! + .call(onPrevious, widget.previousBtnTitle, i)! + ], + widget.nextButtonBuilder?.call( + !_formValid + ? null + : () async { + await onNext(widget.steps[i]); + }, + widget.steps.last == widget.steps[i] + ? widget.submitBtnTitle + : widget.nextBtnTitle, + i, + _formValid, + ) ?? + ElevatedButton( + onPressed: !_formValid + ? null + : () async { + await onNext(widget.steps[i]); + }, + child: Row( + children: [ + Text( + widget.steps.last == widget.steps[i] + ? widget.submitBtnTitle + : widget.nextBtnTitle, + ), + const Padding( + padding: EdgeInsets.only(left: 4.0), + child: Icon( + Icons.arrow_forward, + size: 18, + ), + ), + ], + ), + ), + ], + ), + if (widget.loginButton != null) + Padding( + padding: const EdgeInsets.only(top: 20.0), + child: widget.loginButton!, + ), + ], + ), + ), ], ), ], diff --git a/lib/src/config/registration_options.dart b/lib/src/config/registration_options.dart index 8ed741b..1d3e9e3 100644 --- a/lib/src/config/registration_options.dart +++ b/lib/src/config/registration_options.dart @@ -18,6 +18,8 @@ class RegistrationOptions { this.nextButtonBuilder, this.previousButtonBuilder, this.backgroundColor, + this.titleWidget, + this.loginButton, }); final RegistrationTranslations registrationTranslations; @@ -26,11 +28,13 @@ class RegistrationOptions { final VoidCallback afterRegistration; final RegistrationRepository registrationRepository; final AppBar Function(String title)? customAppbarBuilder; - final Widget Function(Future Function() onPressed, String label)? - nextButtonBuilder; - final Widget Function(VoidCallback onPressed, String label)? + final Widget Function(Future Function()? onPressed, String label, + int step, bool enabled)? nextButtonBuilder; + final Widget? Function(VoidCallback onPressed, String label, int step)? previousButtonBuilder; final Color? backgroundColor; + Widget? titleWidget; + Widget? loginButton; static List getDefaultSteps({ TextEditingController? emailController, @@ -69,8 +73,10 @@ class RegistrationOptions { ), ), ), - label: labelBuilder?.call(translations.defaultEmailLabel), - hintText: translations.defaultEmailHint, + textFieldDecoration: InputDecoration( + label: labelBuilder?.call(translations.defaultEmailLabel), + hintText: translations.defaultEmailHint, + ), textStyle: textStyle, validators: [ (email) => (email == null || email.isEmpty) @@ -87,7 +93,7 @@ class RegistrationOptions { ), AuthStep( fields: [ - AuthTextField( + AuthPassField( name: 'password1', textEditingController: pass1Controller, title: titleBuilder?.call( @@ -105,10 +111,11 @@ class RegistrationOptions { ), ), ), - label: labelBuilder?.call(translations.defaultPassword1Label), - hintText: translations.defaultPassword1Hint, + textFieldDecoration: InputDecoration( + label: labelBuilder?.call(translations.defaultPassword1Label), + hintText: translations.defaultPassword1Hint, + ), textStyle: textStyle, - obscureText: true, validators: [ (value) => (value == null || value.isEmpty) ? translations.defaultPassword1ValidatorMessage @@ -117,12 +124,8 @@ class RegistrationOptions { onChange: (value) { password1 = value; }, - hidden: pass1Hidden, - onPassChanged: (value) { - passHideOnChange?.call(true, value); - }, ), - AuthTextField( + AuthPassField( name: 'password2', textEditingController: pass2Controller, title: titleBuilder?.call( @@ -140,10 +143,11 @@ class RegistrationOptions { ), ), ), - label: labelBuilder?.call(translations.defaultPassword2Label), - hintText: translations.defaultPassword2Hint, + textFieldDecoration: InputDecoration( + label: labelBuilder?.call(translations.defaultPassword2Label), + hintText: translations.defaultPassword2Hint, + ), textStyle: textStyle, - obscureText: true, validators: [ (value) { if (pass1Controller != null) { @@ -158,10 +162,6 @@ class RegistrationOptions { return null; } ], - hidden: pass2Hidden, - onPassChanged: (value) { - passHideOnChange?.call(false, value); - }, ), ], ), diff --git a/lib/src/model/auth_bool_field.dart b/lib/src/model/auth_bool_field.dart new file mode 100644 index 0000000..3fc243b --- /dev/null +++ b/lib/src/model/auth_bool_field.dart @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2022 Iconica +// +// SPDX-License-Identifier: BSD-3-Clause + +import 'package:flutter/material.dart'; +import 'package:flutter_input_library/flutter_input_library.dart'; +import 'package:flutter_registration/flutter_registration.dart'; + +class AuthBoolField extends AuthField { + AuthBoolField({ + required super.name, + required this.widgetType, + super.title, + super.validators = const [], + super.value = '', + this.leftWidget, + this.rightWidget, + this.onChange, + }); + + final Widget? leftWidget; + final Widget? rightWidget; + final BoolWidgetType widgetType; + final Function(String value)? onChange; + + @override + Widget build(BuildContext context, Function onValueChanged) { + return FlutterFormInputBool( + widgetType: widgetType, + onChanged: (v) { + value = v; + onChange?.call(value); + onValueChanged(); + }, + validator: (value) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } + } + return null; + }, + leftWidget: leftWidget, + rightWidget: rightWidget, + ); + } +} diff --git a/lib/src/model/auth_drop_down.dart b/lib/src/model/auth_drop_down.dart new file mode 100644 index 0000000..9a2acba --- /dev/null +++ b/lib/src/model/auth_drop_down.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_registration/flutter_registration.dart'; + +class AuthDropdownField extends AuthField { + AuthDropdownField({ + required super.name, + required this.items, + required this.onChanged, + this.dropdownDecoration, + this.padding = const EdgeInsets.all(8.0), + this.textStyle, + this.icon = const Icon(Icons.keyboard_arrow_down), + required super.value, + }) { + selectedValue = value ?? items.first; + } + + final List items; + final Function(String?) onChanged; + String? selectedValue; + final InputDecoration? dropdownDecoration; + final EdgeInsets padding; + final TextStyle? textStyle; + final Icon icon; + + @override + Widget build(BuildContext context, Function onValueChanged) { + return Padding( + padding: padding, + child: DropdownButtonFormField( + icon: icon, + style: textStyle, + value: selectedValue, + decoration: dropdownDecoration, + items: items.map((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + onChanged: (newValue) { + selectedValue = newValue; + onChanged(newValue); + onValueChanged(); + }, + validator: (value) { + if (validators.isNotEmpty) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } + } + } + return null; + }, + ), + ); + } +} diff --git a/lib/src/model/auth_field.dart b/lib/src/model/auth_field.dart index 23ad82b..1e219a5 100644 --- a/lib/src/model/auth_field.dart +++ b/lib/src/model/auth_field.dart @@ -4,18 +4,21 @@ import 'package:flutter/material.dart'; -abstract class AuthField { +abstract class AuthField { AuthField({ required this.name, + required this.value, + this.onValueChanged, this.title, this.validators = const [], - this.value = '', }); final String name; final Widget? title; - List validators; - String value; + List validators; + T value; - Widget build(); + final Function(T)? onValueChanged; // Callback for value changes + + Widget build(BuildContext context, Function onValueChanged); } diff --git a/lib/src/model/auth_pass_field.dart b/lib/src/model/auth_pass_field.dart new file mode 100644 index 0000000..6cb978c --- /dev/null +++ b/lib/src/model/auth_pass_field.dart @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2024 Iconica +// +// SPDX-License-Identifier: BSD-3-Clause + +import 'package:flutter/material.dart'; +import 'package:flutter_input_library/flutter_input_library.dart'; +import 'package:flutter_registration/flutter_registration.dart'; + +class AuthPassField extends AuthField { + AuthPassField({ + required super.name, + TextEditingController? textEditingController, + super.title, + super.validators = const [], + super.value = '', + this.textStyle, + this.onChange, + this.iconSize, + this.textFieldDecoration, + this.padding = const EdgeInsets.all(8.0), + }); + + final TextStyle? textStyle; + final double? iconSize; + final Function(String value)? onChange; + final InputDecoration? textFieldDecoration; + final EdgeInsets padding; + + @override + Widget build(BuildContext context, Function onValueChanged) { + return Padding( + padding: padding, + child: FlutterFormInputPassword( + style: textStyle, + iconSize: iconSize ?? 24.0, + decoration: textFieldDecoration, + onChanged: (v) { + value = v; + onChange?.call(value); + onValueChanged(); + }, + validator: (value) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } + } + + return null; + }, + ), + ); + } +} diff --git a/lib/src/model/auth_text_field.dart b/lib/src/model/auth_text_field.dart index 1f5ac89..3c7b7c0 100644 --- a/lib/src/model/auth_text_field.dart +++ b/lib/src/model/auth_text_field.dart @@ -12,72 +12,45 @@ class AuthTextField extends AuthField { super.title, super.validators = const [], super.value = '', - this.obscureText = false, - this.hintText, - this.label, this.textStyle, this.onChange, - this.hidden, - this.onPassChanged, + this.textFieldDecoration, + this.padding = const EdgeInsets.all(8.0), }) { textController = textEditingController ?? TextEditingController(text: value); } late TextEditingController textController; - final bool obscureText; - final String? hintText; - final Widget? label; final TextStyle? textStyle; final Function(String value)? onChange; - final bool? hidden; - final Function(bool value)? onPassChanged; + final InputDecoration? textFieldDecoration; + final EdgeInsets padding; @override - Widget build() { - Widget? suffix; - - if (hidden != null) { - if (hidden!) { - suffix = GestureDetector( - onTap: () { - onPassChanged?.call(!hidden!); - }, - child: const Icon(Icons.visibility), - ); - } else { - suffix = GestureDetector( - onTap: () { - onPassChanged?.call(!hidden!); - }, - child: const Icon(Icons.visibility_off), - ); - } - } - - return TextFormField( - style: textStyle, - decoration: InputDecoration( - label: label, - hintText: hintText, - suffix: suffix, - ), - controller: textController, - obscureText: hidden ?? obscureText, - onChanged: (v) { - value = v; - onChange?.call(value); - }, - validator: (value) { - for (var validator in validators) { - var output = validator(value); - if (output != null) { - return output; + Widget build(BuildContext context, Function onValueChanged) { + return Padding( + padding: padding, + child: TextFormField( + style: textStyle, + decoration: textFieldDecoration, + controller: textController, + onChanged: (v) { + value = v; + onChange?.call(value); + onValueChanged(); + }, + validator: (value) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } } - } - return null; - }, + return null; + }, + ), ); } } diff --git a/lib/src/registration_screen.dart b/lib/src/registration_screen.dart index 83328dd..eb3dece 100644 --- a/lib/src/registration_screen.dart +++ b/lib/src/registration_screen.dart @@ -17,7 +17,7 @@ class RegistrationScreen extends StatelessWidget { final RegistrationOptions registrationOptions; Future register({ - required HashMap values, + required HashMap values, required void Function(int? pageToReturn) onError, }) async { try { @@ -46,13 +46,15 @@ class RegistrationScreen extends StatelessWidget { translations.title, ), onFinish: register, - title: translations.title, + appBarTitle: translations.title, submitBtnTitle: translations.registerBtn, nextBtnTitle: translations.nextStepBtn, previousBtnTitle: translations.previousStepBtn, nextButtonBuilder: registrationOptions.nextButtonBuilder, previousButtonBuilder: registrationOptions.previousButtonBuilder, customBackgroundColor: registrationOptions.backgroundColor, + titleWidget: registrationOptions.titleWidget, + loginButton: registrationOptions.loginButton, ); } } diff --git a/pubspec.yaml b/pubspec.yaml index d318f16..716570e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,14 +4,21 @@ name: flutter_registration description: A Flutter Registration package -version: 1.2.0 +version: 2.0.0 repository: https://github.com/Iconica-Development/flutter_registration +publish_to: none + environment: sdk: ">=2.18.0 <3.0.0" flutter: ">=1.17.0" dependencies: + flutter_input_library: + git: + url: https://github.com/Iconica-Development/flutter_input_library + ref: 3.0.1 + flutter: sdk: flutter flutter_localizations: