diff --git a/CHANGELOG.md b/CHANGELOG.md index ee688f2..ee28167 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,10 @@ SPDX-FileCopyrightText: 2022 Iconica SPDX-License-Identifier: GPL-3.0-or-later --> - +# 3.0.0 +- fix: fixed the issue with the scrollController when the `pageToReturnTo` is null. +- feat: Added default styling and theme. +- feat: Added Iconica linter. # 2.0.4 - feat: added maxFormWidth to AuthScreen diff --git a/analysis_options.yaml b/analysis_options.yaml index 872a1eb..0736605 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,4 +1,9 @@ -include: package:flutter_lints/flutter.yaml +include: package:flutter_iconica_analysis/components_options.yaml -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options \ No newline at end of file +# Possible to overwrite the rules from the package + +analyzer: + exclude: + +linter: + rules: diff --git a/example/lib/main.dart b/example/lib/main.dart index 2894316..57811d5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -21,7 +21,7 @@ void main() { } class FlutterRegistrationDemo extends StatefulWidget { - const FlutterRegistrationDemo({Key? key}) : super(key: key); + const FlutterRegistrationDemo({super.key}); @override State createState() => @@ -86,8 +86,7 @@ class _FlutterRegistrationDemoState extends State { } class ProtectedScreen extends StatelessWidget { - const ProtectedScreen({Key? key}) : super(key: key); - + const ProtectedScreen({super.key}); @override Widget build(BuildContext context) { return const Scaffold( diff --git a/example/pubspec.lock b/example/pubspec.lock index c355743..a45ae63 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -66,10 +66,10 @@ packages: dependency: transitive description: name: flutter_input_library - sha256: "346823caa633d47ad21d8ce92f3c2ed460619fd5447999c35cb171da5e86af06" + sha256: db8d9d57c31f166ed7c4ec3d060d18891533c57877a30c6c2668231efa1a44f8 url: "https://forgejo.internal.iconica.nl/api/packages/internal/pub/" source: hosted - version: "3.3.1" + version: "3.6.0" flutter_lints: dependency: "direct dev" description: diff --git a/lib/flutter_registration.dart b/lib/flutter_registration.dart index d4d9e9a..94cf329 100644 --- a/lib/flutter_registration.dart +++ b/lib/flutter_registration.dart @@ -1,19 +1,21 @@ // SPDX-FileCopyrightText: 2022 Iconica // // SPDX-License-Identifier: BSD-3-Clause -/// Flutter registration component that provides a registration screen with multiple registration steps. +/// Flutter registration component that provides a registration +/// screen with multiple registration steps. library flutter_registration; -export 'src/config/registration_options.dart'; -export 'src/config/registration_translations.dart'; -export 'src/model/auth_exception.dart'; -export 'src/model/auth_field.dart'; -export 'src/model/auth_step.dart'; -export 'src/model/auth_text_field.dart'; -export 'src/model/auth_bool_field.dart'; -export 'src/model/auth_drop_down.dart'; -export 'src/model/auth_pass_field.dart'; -export 'src/registration_screen.dart'; -export 'src/service/registration_repository.dart'; -export 'package:flutter_input_library/flutter_input_library.dart' +export "package:flutter_input_library/flutter_input_library.dart" show BoolWidgetType; + +export "src/config/registration_options.dart"; +export "src/config/registration_translations.dart"; +export "src/model/auth_bool_field.dart"; +export "src/model/auth_drop_down.dart"; +export "src/model/auth_exception.dart"; +export "src/model/auth_field.dart"; +export "src/model/auth_pass_field.dart"; +export "src/model/auth_step.dart"; +export "src/model/auth_text_field.dart"; +export "src/registration_screen.dart"; +export "src/service/registration_repository.dart"; diff --git a/lib/src/auth_screen.dart b/lib/src/auth_screen.dart index 6e986b6..a652f01 100644 --- a/lib/src/auth_screen.dart +++ b/lib/src/auth_screen.dart @@ -2,10 +2,10 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'dart:async'; -import 'dart:collection'; -import 'package:flutter/material.dart'; -import 'package:flutter_registration/flutter_registration.dart'; +import "dart:async"; +import "dart:collection"; +import "package:flutter/material.dart"; +import "package:flutter_registration/flutter_registration.dart"; /// A widget for handling multi-step authentication processes. class AuthScreen extends StatefulWidget { @@ -13,7 +13,8 @@ class AuthScreen extends StatefulWidget { /// /// [appBarTitle] specifies the title of the app bar. /// - /// [onFinish] is a function called upon completion of the authentication process. + /// [onFinish] is a function called upon + /// completion of the authentication process. /// /// [steps] is a list of authentication steps to be completed. /// @@ -33,7 +34,8 @@ class AuthScreen extends StatefulWidget { /// /// [previousButtonBuilder] allows customization of the previous button. /// - /// [titleWidget] specifies a custom widget to be displayed at the top of the screen. + /// [titleWidget] specifies a custom widget + /// to be displayed at the top of the screen. /// /// [loginButton] specifies a custom login button widget. /// @@ -44,8 +46,7 @@ class AuthScreen extends StatefulWidget { /// [beforeTitleFlex] specifies the flex value before the title widget. /// /// [afterTitleFlex] specifies the flex value after the title widget. - /// - /// [isLoading] indicates whether the screen is in a loading state. + const AuthScreen({ required this.appBarTitle, required this.steps, @@ -64,35 +65,72 @@ class AuthScreen extends StatefulWidget { this.formFlex, this.beforeTitleFlex, this.afterTitleFlex, - this.isLoading = false, this.maxFormWidth, - Key? key, - }) : assert(steps.length > 0, 'At least one step is required'), - super(key: key); + super.key, + }) : assert(steps.length > 0, "At least one step is required"); + /// The title of the app bar. final String appBarTitle; + + /// A function called upon completion of the authentication process. final Future Function({ required HashMap values, required void Function(int? pageToReturn) onError, }) onFinish; + + /// The authentication steps to be completed. final List steps; + + /// The title of the submit button. final String submitBtnTitle; + + /// The title of the next button. final String nextBtnTitle; + + /// The title of the previous button. final String previousBtnTitle; + + /// A custom app bar widget. final AppBar? customAppBar; + + /// The alignment of the buttons. final MainAxisAlignment? buttonMainAxisAlignment; + + /// The background color of the screen. final Color? customBackgroundColor; - final Widget Function(Future Function()? onPressed, String label, - int step, bool enabled)? nextButtonBuilder; + + /// A custom widget for the button. + final Widget Function( + Future Function()? onPressed, + String label, + int step, + // ignore: avoid_positional_boolean_parameters + bool enabled, + )? nextButtonBuilder; + + /// A custom widget for the button. final Widget? Function(VoidCallback onPressed, String label, int step)? previousButtonBuilder; + + /// A custom widget for the title. final Widget? titleWidget; + + /// A custom widget for the login button. final Widget? loginButton; + + /// The flex value for the title widget. final int? titleFlex; + + /// The flex value for the form widget. final int? formFlex; + + /// The flex value before the title widget. final int? beforeTitleFlex; + + /// The flex value after the title widget. final int? afterTitleFlex; - final bool isLoading; + + /// The maximum width of the form. final double? maxFormWidth; @override @@ -119,9 +157,11 @@ class _AuthScreenState extends State { void onPrevious() { FocusScope.of(context).unfocus(); _validate(_pageController.page!.toInt() - 1); - _pageController.previousPage( - duration: _animationDuration, - curve: _animationCurve, + unawaited( + _pageController.previousPage( + duration: _animationDuration, + curve: _animationCurve, + ), ); } @@ -146,31 +186,38 @@ class _AuthScreenState extends State { await widget.onFinish( values: values, - onError: (int? pageToReturn) => _pageController.animateToPage( - pageToReturn ?? 0, - duration: _animationDuration, - curve: _animationCurve, - ), + onError: (int? pageToReturn) { + if (pageToReturn == null) { + return; + } + _pageController.animateToPage( + pageToReturn, + duration: _animationDuration, + curve: _animationCurve, + ); + }, ); return; } else { _validate(_pageController.page!.toInt() + 1); - _pageController.nextPage( - duration: _animationDuration, - curve: _animationCurve, + unawaited( + _pageController.nextPage( + duration: _animationDuration, + curve: _animationCurve, + ), ); } } /// Validates the current step. void _validate(int currentPage) { - bool isStepValid = true; + var isStepValid = true; // Loop through each field in the current step for (var field in widget.steps[currentPage].fields) { for (var validator in field.validators) { - String? validationResult = validator(field.value); + var validationResult = validator(field.value); if (validationResult != null) { // If any validator returns an error, mark step as invalid and break isStepValid = false; @@ -189,203 +236,186 @@ class _AuthScreenState extends State { @override Widget build(BuildContext context) { - return widget.isLoading - ? const Center( - child: SizedBox( - height: 120, - width: 120, - child: CircularProgressIndicator(), - ), - ) - : Scaffold( - backgroundColor: - widget.customBackgroundColor ?? const Color(0xffFAF9F6), - appBar: _appBar, - body: SafeArea( - child: Form( - key: _formKey, - child: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _pageController, - children: [ - for (var i = 0; i < widget.steps.length; i++) - Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (widget.titleWidget != null) ...[ + var theme = Theme.of(context); + return Scaffold( + backgroundColor: widget.customBackgroundColor ?? const Color(0xffFAF9F6), + appBar: _appBar, + body: SafeArea( + child: Form( + key: _formKey, + child: PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _pageController, + children: [ + for (var currentStep = 0; + currentStep < widget.steps.length; + currentStep++) + Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (widget.titleWidget != null) ...[ + Expanded( + flex: widget.titleFlex ?? 1, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ Expanded( - flex: widget.titleFlex ?? 1, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - flex: widget.beforeTitleFlex ?? 2, - child: Container(), - ), - widget.titleWidget!, - Expanded( - flex: widget.afterTitleFlex ?? 2, - child: Container(), - ), - ], - ), + flex: widget.beforeTitleFlex ?? 2, + child: Container(), + ), + widget.titleWidget!, + Expanded( + flex: widget.afterTitleFlex ?? 2, + child: Container(), ), ], - Expanded( - flex: widget.formFlex ?? 3, - child: Align( - alignment: Alignment.topCenter, - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: widget.maxFormWidth ?? 300, - ), - child: Column( - children: [ - for (AuthField field - in widget.steps[i].fields) ...[ - if (field.title != null) ...[ - field.title!, - ], - field.build(context, () { - _validate(i); - }) - ] - ], - ), - ), - ), + ), + ), + ], + Expanded( + flex: widget.formFlex ?? 3, + child: Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: widget.maxFormWidth ?? 300, ), - Column( + child: Column( children: [ - Row( - mainAxisAlignment: widget - .buttonMainAxisAlignment != - null - ? widget.buttonMainAxisAlignment! - : (widget.previousButtonBuilder != null && - widget.previousButtonBuilder?.call( - onPrevious, - widget.previousBtnTitle, - i, - ) == - null) - ? MainAxisAlignment.start - : widget.steps.first != widget.steps[i] - ? MainAxisAlignment.center - : MainAxisAlignment.end, - children: [ - if (widget.previousButtonBuilder == null) ...[ - if (widget.steps.first != widget.steps[i]) - Padding( - padding: const EdgeInsets.only( - left: 16, bottom: 10, right: 8), - child: InkWell( - onTap: onPrevious, - child: Container( - width: 180, - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(20), - border: Border.all( - color: const Color( - 0xff979797, - ), - ), - ), - child: Center( - child: Padding( - padding: - const EdgeInsets.symmetric( - vertical: 2.0), - child: Text( - widget.previousBtnTitle, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - ), - ), - ] 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, - ) ?? - Padding( - padding: const EdgeInsets.only( - right: 16, bottom: 10, left: 8), - child: InkWell( - onTap: !_formValid - ? null - : () async { - await onNext(widget.steps[i]); - }, - child: Container( - width: 180, - decoration: BoxDecoration( - borderRadius: - BorderRadius.circular(20), - border: Border.all( - color: const Color( - 0xff979797, - ), - ), - ), - child: Center( - child: Padding( - padding: - const EdgeInsets.symmetric( - vertical: 2.0), - child: Text( - widget.steps.last == - widget.steps[i] - ? widget.submitBtnTitle - : widget.nextBtnTitle, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - ), - ), - ), - ), - ), + for (AuthField field + in widget.steps[currentStep].fields) ...[ + if (field.title != null) ...[ + wrapWithDefaultStyle( + style: theme.textTheme.headlineLarge!, + widget: field.title!, + ), ], - ), - if (widget.loginButton != null) - Padding( - padding: const EdgeInsets.only(top: 20.0), - child: widget.loginButton!, - ), + field.build(context, () { + _validate(currentStep); + }), + ], ], ), - ], + ), ), + ), + Column( + children: [ + SizedBox( + width: MediaQuery.of(context).size.width, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, + ), + child: Row( + mainAxisAlignment: widget.steps.first != + widget.steps[currentStep] + ? MainAxisAlignment.spaceBetween + : MainAxisAlignment.end, + children: [ + if (widget.steps.first != + widget.steps[currentStep]) ...[ + widget.previousButtonBuilder?.call( + onPrevious, + widget.previousBtnTitle, + currentStep, + ) ?? + _stepButton( + buttonText: widget.previousBtnTitle, + onTap: () async => onPrevious(), + ), + const SizedBox( + width: 8, + ), + ], + widget.nextButtonBuilder?.call( + !_formValid + ? null + : () async { + await onNext( + widget.steps[currentStep], + ); + }, + widget.steps.last == + widget.steps[currentStep] + ? widget.submitBtnTitle + : widget.nextBtnTitle, + currentStep, + _formValid, + ) ?? + _stepButton( + buttonText: widget.steps.last == + widget.steps[currentStep] + ? widget.submitBtnTitle + : widget.nextBtnTitle, + onTap: () async { + await onNext( + widget.steps[currentStep], + ); + }, + ), + ], + ), + ), + ), + const SizedBox( + height: 8, + ), + if (widget.loginButton != null) + Padding( + padding: const EdgeInsets.only(top: 20.0), + child: widget.loginButton, + ), + ], + ), ], ), + ], + ), + ), + ), + ); + } + + Widget _stepButton({ + required String buttonText, + required Future Function()? onTap, + }) { + var theme = Theme.of(context); + return Flexible( + child: InkWell( + onTap: onTap, + child: Container( + width: double.infinity, + constraints: const BoxConstraints( + maxWidth: 180, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: const Color( + 0xff979797, ), ), - ); + ), + child: Padding( + padding: const EdgeInsets.all(4), + child: Text( + buttonText, + style: theme.textTheme.bodyMedium, + textAlign: TextAlign.center, + ), + ), + ), + ), + ); } + + Widget wrapWithDefaultStyle({ + required Widget widget, + required TextStyle style, + }) => + DefaultTextStyle(style: style, child: widget); } diff --git a/lib/src/config/example_registration_repository.dart b/lib/src/config/example_registration_repository.dart index afc2421..9b9a679 100644 --- a/lib/src/config/example_registration_repository.dart +++ b/lib/src/config/example_registration_repository.dart @@ -1,11 +1,12 @@ -import 'dart:collection'; -import 'package:flutter/material.dart'; -import 'package:flutter_registration/flutter_registration.dart'; +import "dart:collection"; +import "package:flutter/material.dart"; +import "package:flutter_registration/flutter_registration.dart"; +/// A registration repository that does nothing. class ExampleRegistrationRepository with RegistrationRepository { @override Future register(HashMap values) { - debugPrint('register $values'); + debugPrint("register $values"); return Future.value(null); } } diff --git a/lib/src/config/registration_options.dart b/lib/src/config/registration_options.dart index 8823abc..fb6b9a5 100644 --- a/lib/src/config/registration_options.dart +++ b/lib/src/config/registration_options.dart @@ -2,18 +2,20 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'dart:async'; +import "dart:async"; -import 'package:flutter/material.dart'; -import 'package:flutter_registration/flutter_registration.dart'; -import 'package:flutter_registration/src/config/example_registration_repository.dart'; +import "package:flutter/material.dart"; +import "package:flutter_registration/flutter_registration.dart"; +import "package:flutter_registration/src/config/example_registration_repository.dart"; -/// A set of options for configuring the registration process in a Flutter application. +/// A set of options for configuring the +/// registration process in a Flutter application. class RegistrationOptions { + /// Registration options Constructor RegistrationOptions({ + required this.afterRegistration, this.registrationRepository, this.registrationSteps, - required this.afterRegistration, this.titleFlex, this.formFlex, this.beforeTitleFlex, @@ -28,6 +30,7 @@ class RegistrationOptions { this.titleWidget, this.loginButton, this.maxFormWidth, + this.beforeRegistration, }) { if (registrationSteps == null || registrationSteps!.isEmpty) { steps = RegistrationOptions.getDefaultSteps(); @@ -43,6 +46,7 @@ class RegistrationOptions { /// The steps involved in the registration process. final List? registrationSteps; + /// The steps involved in the registration process. List steps = []; /// A function that handles errors during registration. @@ -58,8 +62,13 @@ class RegistrationOptions { final AppBar Function(String title)? customAppbarBuilder; /// A function for customizing the "Next" button. - final Widget Function(Future Function()? onPressed, String label, - int step, bool enabled)? nextButtonBuilder; + final Widget Function( + Future Function()? onPressed, + String label, + int step, + // ignore: avoid_positional_boolean_parameters + bool enabled, + )? nextButtonBuilder; /// A function for customizing the "Previous" button. final Widget? Function(VoidCallback onPressed, String label, int step)? @@ -92,6 +101,9 @@ class RegistrationOptions { /// The maximum width of the form. Defaults to 300. final double? maxFormWidth; + /// This function gets called before the user gets registered. + final Future Function()? beforeRegistration; + /// Generates default registration steps. /// /// [emailController] controller for email input. @@ -119,94 +131,98 @@ class RegistrationOptions { TextEditingController? emailController, TextEditingController? passController, bool passHidden = true, + // ignore: avoid_positional_boolean_parameters Function(bool mainPass, bool value)? passHideOnChange, RegistrationTranslations translations = const RegistrationTranslations.empty(), Function(String title)? titleBuilder, Function(String label)? labelBuilder, TextStyle? textStyle, + TextStyle? hintStyle, String? initialEmail, - }) { - return [ - AuthStep( - fields: [ - AuthTextField( - name: 'email', - textEditingController: emailController, - value: initialEmail ?? '', - title: titleBuilder?.call(translations.defaultEmailTitle) ?? - const Padding( - padding: EdgeInsets.only(top: 180), - child: Text( - 'Enter your e-mail', - style: TextStyle( - color: Color(0xff71C6D1), - fontWeight: FontWeight.w800, - fontSize: 24, + }) => + [ + AuthStep( + fields: [ + AuthTextField( + name: "email", + textEditingController: emailController, + value: initialEmail ?? "", + title: titleBuilder?.call(translations.defaultEmailTitle) ?? + Padding( + padding: const EdgeInsets.only(top: 180), + child: Text( + translations.defaultEmailTitle, ), ), - ), - textFieldDecoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric(horizontal: 8), - label: labelBuilder?.call(translations.defaultEmailLabel), - hintText: translations.defaultEmailHint, - border: const OutlineInputBorder(), + textInputType: TextInputType.emailAddress, + textFieldDecoration: InputDecoration( + hintStyle: hintStyle, + contentPadding: const EdgeInsets.symmetric(horizontal: 8), + label: labelBuilder?.call(translations.defaultEmailLabel), + hintText: translations.defaultEmailHint, + border: const OutlineInputBorder(), + focusedBorder: const OutlineInputBorder(), + ), + textStyle: textStyle, + padding: const EdgeInsets.symmetric(vertical: 20), + validators: [ + // ignore: avoid_dynamic_calls + (email) => (email == null || email.isEmpty) + ? translations.defaultEmailEmpty + : null, + (email) => + RegExp(r"""(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])""") + .hasMatch(email!) + ? null + : translations.defaultEmailValidatorMessage, + ], ), - textStyle: textStyle, - padding: const EdgeInsets.symmetric(vertical: 20), - validators: [ - (email) => (email == null || email.isEmpty) - ? translations.defaultEmailEmpty - : null, - (email) => - RegExp(r"""(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])""") - .hasMatch(email!) - ? null - : translations.defaultEmailValidatorMessage, - ], - ), - ], - ), - AuthStep( - fields: [ - AuthPassField( - name: 'password', - textEditingController: passController, - title: titleBuilder?.call(translations.defaultPasswordTitle) ?? - Padding( - padding: const EdgeInsets.only(top: 180), - child: Text( - translations.defaultPasswordTitle, - style: const TextStyle( - color: Color(0xff71C6D1), - fontWeight: FontWeight.w800, - fontSize: 24, + ], + ), + AuthStep( + fields: [ + AuthPassField( + name: "password", + textEditingController: passController, + title: titleBuilder?.call(translations.defaultPasswordTitle) ?? + Padding( + padding: const EdgeInsets.only(top: 180), + child: Text( + translations.defaultPasswordTitle, ), ), - ), - textFieldDecoration: InputDecoration( - contentPadding: const EdgeInsets.symmetric(horizontal: 8), - label: labelBuilder?.call(translations.defaultPasswordLabel), - hintText: translations.defaultPasswordHint, - border: const OutlineInputBorder(), + textFieldDecoration: InputDecoration( + hintStyle: hintStyle, + contentPadding: const EdgeInsets.symmetric(horizontal: 8), + label: labelBuilder?.call(translations.defaultPasswordLabel), + hintText: translations.defaultPasswordHint, + border: const OutlineInputBorder(), + focusedBorder: const OutlineInputBorder(), + ), + padding: const EdgeInsets.symmetric(vertical: 20), + textStyle: textStyle, + validators: [ + (value) { + // ignore: avoid_dynamic_calls + if (value == null || value.isEmpty) { + return translations.defaultPasswordValidatorMessage; + } + // ignore: avoid_dynamic_calls + if (value.length < 6) { + return translations.defaultPasswordToShortValidatorMessage; + } + return null; + }, + ], ), - padding: const EdgeInsets.symmetric(vertical: 20), - textStyle: textStyle, - validators: [ - (value) => (value == null || value.isEmpty) - ? translations.defaultPasswordValidatorMessage - : null, - ], - ), - ], - ), - ]; - } + ], + ), + ]; } -AppBar _createCustomAppBar(String title) { - return AppBar( - title: Text(title), - backgroundColor: const Color(0xffFAF9F6), - ); -} +AppBar _createCustomAppBar(String title) => AppBar( + iconTheme: const IconThemeData(color: Colors.black, size: 16), + title: Text(title), + backgroundColor: Colors.transparent, + ); diff --git a/lib/src/config/registration_translations.dart b/lib/src/config/registration_translations.dart index f492670..f57985b 100644 --- a/lib/src/config/registration_translations.dart +++ b/lib/src/config/registration_translations.dart @@ -2,8 +2,10 @@ // // SPDX-License-Identifier: BSD-3-Clause -/// Holds all the translations for the standard elements on the registration screen. +/// Holds all the translations for the standard elements +/// on the registration screen. class RegistrationTranslations { + /// Constructs a [RegistrationTranslations] object. const RegistrationTranslations({ required this.title, required this.registerBtn, @@ -19,40 +21,74 @@ class RegistrationTranslations { required this.defaultPasswordLabel, required this.defaultPasswordHint, required this.defaultPasswordValidatorMessage, + required this.defaultPasswordToShortValidatorMessage, }); + /// Constructs a [RegistrationTranslations] object with empty strings. const RegistrationTranslations.empty() - : title = '', - registerBtn = 'Register', - previousStepBtn = 'Previous', - nextStepBtn = 'Next', - closeBtn = 'Close', - defaultEmailTitle = 'What is your email?', - defaultEmailLabel = '', - defaultEmailHint = 'Email address', - defaultEmailEmpty = 'Please enter your email address.', - defaultEmailValidatorMessage = 'Please enter a valid email address.', - defaultPasswordTitle = 'Choose a password', - defaultPasswordLabel = 'password', - defaultPasswordHint = '', - defaultPasswordValidatorMessage = 'Enter a valid password'; + : title = "", + registerBtn = "Register", + previousStepBtn = "Previous", + nextStepBtn = "Next", + closeBtn = "Close", + defaultEmailTitle = "enter your email address", + defaultEmailLabel = "", + defaultEmailHint = "Email address", + defaultEmailEmpty = "Please enter your email address.", + defaultEmailValidatorMessage = "Please enter a valid email address.", + defaultPasswordTitle = "choose a password", + defaultPasswordLabel = "", + defaultPasswordHint = "Password", + defaultPasswordValidatorMessage = "Enter a valid password", + defaultPasswordToShortValidatorMessage = + "Password needs to be at least 6 characters long"; + /// The title of the registration screen. final String title; + + /// The text for the registration button. final String registerBtn; + + /// The text for the previous step button. final String previousStepBtn; + + /// The text for the next step button. final String nextStepBtn; + + /// The text for the close button. final String closeBtn; + + /// The title for the default email field. final String defaultEmailTitle; + + /// The label for the default email field. final String defaultEmailLabel; + + /// The hint for the default email field. final String defaultEmailHint; + + /// The message for an empty default email field. final String defaultEmailEmpty; + + /// The message for an invalid default email field. final String defaultEmailValidatorMessage; + + /// The title for the default password field. final String defaultPasswordTitle; + + /// The label for the default password field. final String defaultPasswordLabel; + + /// The hint for the default password field. final String defaultPasswordHint; + + /// The message for an invalid default password field. final String defaultPasswordValidatorMessage; - // create a copywith + /// The message for a default password that is too short. + final String defaultPasswordToShortValidatorMessage; + + /// create a copywith RegistrationTranslations copyWith({ String? title, String? registerBtn, @@ -68,24 +104,27 @@ class RegistrationTranslations { String? defaultPasswordLabel, String? defaultPasswordHint, String? defaultPasswordValidatorMessage, - }) { - return RegistrationTranslations( - title: title ?? this.title, - registerBtn: registerBtn ?? this.registerBtn, - previousStepBtn: previousStepBtn ?? this.previousStepBtn, - nextStepBtn: nextStepBtn ?? this.nextStepBtn, - closeBtn: closeBtn ?? this.closeBtn, - defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle, - defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel, - defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint, - defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty, - defaultEmailValidatorMessage: - defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage, - defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle, - defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel, - defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint, - defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ?? - this.defaultPasswordValidatorMessage, - ); - } + String? defaultPasswordToShortValidatorMessage, + }) => + RegistrationTranslations( + title: title ?? this.title, + registerBtn: registerBtn ?? this.registerBtn, + previousStepBtn: previousStepBtn ?? this.previousStepBtn, + nextStepBtn: nextStepBtn ?? this.nextStepBtn, + closeBtn: closeBtn ?? this.closeBtn, + defaultEmailTitle: defaultEmailTitle ?? this.defaultEmailTitle, + defaultEmailLabel: defaultEmailLabel ?? this.defaultEmailLabel, + defaultEmailHint: defaultEmailHint ?? this.defaultEmailHint, + defaultEmailEmpty: defaultEmailEmpty ?? this.defaultEmailEmpty, + defaultEmailValidatorMessage: + defaultEmailValidatorMessage ?? this.defaultEmailValidatorMessage, + defaultPasswordTitle: defaultPasswordTitle ?? this.defaultPasswordTitle, + defaultPasswordLabel: defaultPasswordLabel ?? this.defaultPasswordLabel, + defaultPasswordHint: defaultPasswordHint ?? this.defaultPasswordHint, + defaultPasswordValidatorMessage: defaultPasswordValidatorMessage ?? + this.defaultPasswordValidatorMessage, + defaultPasswordToShortValidatorMessage: + defaultPasswordToShortValidatorMessage ?? + this.defaultPasswordToShortValidatorMessage, + ); } diff --git a/lib/src/model/auth_action.dart b/lib/src/model/auth_action.dart index 547d973..5a65bca 100644 --- a/lib/src/model/auth_action.dart +++ b/lib/src/model/auth_action.dart @@ -2,14 +2,19 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter/material.dart'; +import "package:flutter/material.dart"; +/// An action that can be performed during authentication. class AuthAction { + /// Constructs an [AuthAction] object. AuthAction({ required this.title, required this.onPress, }); + /// The title of the action. final String title; + + /// A callback function triggered when the action is pressed. final VoidCallback onPress; } diff --git a/lib/src/model/auth_bool_field.dart b/lib/src/model/auth_bool_field.dart index fd2cbd4..829a9bf 100644 --- a/lib/src/model/auth_bool_field.dart +++ b/lib/src/model/auth_bool_field.dart @@ -2,9 +2,9 @@ // // 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'; +import "package:flutter/material.dart"; +import "package:flutter_input_library/flutter_input_library.dart"; +import "package:flutter_registration/flutter_registration.dart"; /// A field for capturing boolean values in a Flutter form. /// @@ -22,11 +22,14 @@ class AuthBoolField extends AuthField { /// /// [value] specifies the initial value of the field (default is false). /// - /// [leftWidget] is a widget to be displayed on the left side of the boolean widget. + /// [leftWidget] is a widget to be displayed on the + /// left side of the boolean widget. /// - /// [rightWidget] is a widget to be displayed on the right side of the boolean widget. + /// [rightWidget] is a widget to be displayed on the + /// right side of the boolean widget. /// - /// [onChange] is a callback function triggered when the value of the field changes. + /// [onChange] is a callback function triggered when + /// the value of the field changes. AuthBoolField({ required super.name, required this.widgetType, @@ -38,31 +41,38 @@ class AuthBoolField extends AuthField { this.onChange, }); + /// A widget to be displayed on the left side of the boolean widget. final Widget? leftWidget; + + /// A widget to be displayed on the right side of the boolean widget. final Widget? rightWidget; + + /// The type of boolean widget to use. final BoolWidgetType widgetType; + + /// A callback function triggered when the value of the field changes. final Function(String value)? onChange; @override - Widget build(BuildContext context, Function onValueChanged) { - 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; + Widget build(BuildContext context, Function onValueChanged) => + FlutterFormInputBool( + widgetType: widgetType, + onChanged: (v) { + value = v; + onChange?.call(value); + // ignore: avoid_dynamic_calls + onValueChanged(); + }, + validator: (value) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } } - } - return null; - }, - leftWidget: leftWidget, - rightWidget: rightWidget, - ); - } + return null; + }, + leftWidget: leftWidget, + rightWidget: rightWidget, + ); } diff --git a/lib/src/model/auth_drop_down.dart b/lib/src/model/auth_drop_down.dart index 14bb405..e06f74a 100644 --- a/lib/src/model/auth_drop_down.dart +++ b/lib/src/model/auth_drop_down.dart @@ -1,5 +1,5 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_registration/flutter_registration.dart'; +import "package:flutter/material.dart"; +import "package:flutter_registration/flutter_registration.dart"; /// A field for capturing dropdown selections in a Flutter form. /// @@ -10,11 +10,11 @@ class AuthDropdownField extends AuthField { required super.name, required this.items, required this.onChanged, + required super.value, this.dropdownDecoration, this.padding = const EdgeInsets.all(8.0), this.textStyle, this.icon = const Icon(Icons.keyboard_arrow_down), - required super.value, }) { selectedValue = value ?? items.first; } @@ -41,37 +41,38 @@ class AuthDropdownField extends AuthField { 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; + Widget build(BuildContext context, Function onValueChanged) => Padding( + padding: padding, + child: DropdownButtonFormField( + icon: icon, + style: textStyle, + value: selectedValue, + decoration: dropdownDecoration, + items: items + .map( + (String value) => DropdownMenuItem( + value: value, + child: Text(value), + ), + ) + .toList(), + onChanged: (newValue) { + selectedValue = newValue; + onChanged(newValue); + // ignore: avoid_dynamic_calls + onValueChanged(); + }, + validator: (value) { + if (validators.isNotEmpty) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } } } - } - return null; - }, - ), - ); - } + return null; + }, + ), + ); } diff --git a/lib/src/model/auth_exception.dart b/lib/src/model/auth_exception.dart index 670fae8..ef7476e 100644 --- a/lib/src/model/auth_exception.dart +++ b/lib/src/model/auth_exception.dart @@ -2,12 +2,14 @@ // // SPDX-License-Identifier: BSD-3-Clause +/// An exception thrown when an authentication error occurs. class AuthException implements Exception { + /// Constructs an [AuthException] object. AuthException(this.message); + + /// The error message. final String message; @override - String toString() { - return message; - } + String toString() => message; } diff --git a/lib/src/model/auth_field.dart b/lib/src/model/auth_field.dart index 105a848..27fcfaf 100644 --- a/lib/src/model/auth_field.dart +++ b/lib/src/model/auth_field.dart @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter/material.dart'; +import "package:flutter/material.dart"; /// An abstract class representing a field in a Flutter form. /// @@ -14,11 +14,13 @@ abstract class AuthField { /// /// [value] specifies the initial value of the field. /// - /// [onValueChanged] is a callback function triggered when the value of the field changes (optional). + /// [onValueChanged] is a callback function triggered when the + /// value of the field changes (optional). /// /// [title] specifies the title widget of the field (optional). /// - /// [validators] defines a list of validation functions for the field (optional). + /// [validators] defines a list of validation + /// functions for the field (optional). AuthField({ required this.name, required this.value, @@ -46,6 +48,7 @@ abstract class AuthField { /// /// [context] The build context. /// - /// [onValueChanged] A function to be called when the value of the field changes. + /// [onValueChanged] A function to be called when + /// the value of the field 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 index ea52b58..c985182 100644 --- a/lib/src/model/auth_pass_field.dart +++ b/lib/src/model/auth_pass_field.dart @@ -2,9 +2,9 @@ // // 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'; +import "package:flutter/material.dart"; +import "package:flutter_input_library/flutter_input_library.dart"; +import "package:flutter_registration/flutter_registration.dart"; /// A field for capturing password inputs in a Flutter form. /// @@ -18,25 +18,31 @@ class AuthPassField extends AuthField { /// /// [title] specifies the title widget of the field (optional). /// - /// [validators] defines a list of validation functions for the field (optional). + /// [validators] defines a list of validation + /// functions for the field (optional). /// - /// [value] specifies the initial value of the field (default is an empty string). + /// [value] specifies the initial value of the + /// field (default is an empty string). /// /// [textStyle] defines the text style for the password input. /// - /// [onChange] is a callback function triggered when the value of the field changes. + /// [onChange] is a callback function triggered when + /// the value of the field changes. /// - /// [iconSize] specifies the size of the icon displayed with the password input (optional). + /// [iconSize] specifies the size of the icon displayed + /// with the password input (optional). /// - /// [textFieldDecoration] defines the decoration for the password input field (optional). + /// [textFieldDecoration] defines the decoration for the + /// password input field (optional). /// - /// [padding] defines the padding around the password input field (default is EdgeInsets.all(8.0)). + /// [padding] defines the padding around the password input + /// field (default is EdgeInsets.all(8.0)). AuthPassField({ required super.name, - TextEditingController? textEditingController, + this.textEditingController, super.title, super.validators = const [], - super.value = '', + super.value = "", this.textStyle, this.onChange, this.iconSize, @@ -44,36 +50,47 @@ class AuthPassField extends AuthField { this.padding = const EdgeInsets.all(8.0), }); + /// The text style for the password input. final TextStyle? textStyle; + + /// The size of the icon displayed with the password input. final double? iconSize; + + /// A callback function triggered when the value of the field changes. final Function(String value)? onChange; + + /// The decoration for the password input field. final InputDecoration? textFieldDecoration; + + /// The padding around the password input field. final EdgeInsets padding; - @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; - } - } + /// The controller for the password input. + final TextEditingController? textEditingController; - return null; - }, - ), - ); - } + @override + Widget build(BuildContext context, Function onValueChanged) => Padding( + padding: padding, + child: FlutterFormInputPassword( + style: textStyle, + iconSize: iconSize ?? 24.0, + decoration: textFieldDecoration, + onChanged: (v) { + value = v; + onChange?.call(value); + // ignore: avoid_dynamic_calls + onValueChanged(); + }, + validator: (value) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } + } + + return null; + }, + ), + ); } diff --git a/lib/src/model/auth_step.dart b/lib/src/model/auth_step.dart index abcbdd2..0832887 100644 --- a/lib/src/model/auth_step.dart +++ b/lib/src/model/auth_step.dart @@ -2,12 +2,15 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter_registration/src/model/auth_field.dart'; +import "package:flutter_registration/src/model/auth_field.dart"; +/// A step in the authentication process. class AuthStep { + /// Constructs an [AuthStep] object. AuthStep({ required this.fields, }); + /// The fields in the step. List fields; } diff --git a/lib/src/model/auth_text_field.dart b/lib/src/model/auth_text_field.dart index f4a2248..cf98cb8 100644 --- a/lib/src/model/auth_text_field.dart +++ b/lib/src/model/auth_text_field.dart @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'package:flutter/material.dart'; -import 'package:flutter_registration/flutter_registration.dart'; +import "package:flutter/material.dart"; +import "package:flutter_registration/flutter_registration.dart"; /// A field for capturing text inputs in a Flutter form. /// @@ -17,62 +17,80 @@ class AuthTextField extends AuthField { /// /// [title] specifies the title widget of the field (optional). /// - /// [validators] defines a list of validation functions for the field (optional). + /// [validators] defines a list of validation + /// functions for the field (optional). /// - /// [value] specifies the initial value of the field (default is an empty string). + /// [value] specifies the initial value of the + /// field (default is an empty string). /// /// [textStyle] defines the text style for the text input. /// - /// [onChange] is a callback function triggered when the value of the field changes. + /// [onChange] is a callback function triggered + /// when the value of the field changes. /// - /// [textFieldDecoration] defines the decoration for the text input field (optional). + /// [textFieldDecoration] defines the decoration + /// for the text input field (optional). /// - /// [padding] defines the padding around the text input field (default is EdgeInsets.all(8.0)). + /// [padding] defines the padding around the text + /// input field (default is EdgeInsets.all(8.0)). AuthTextField({ required super.name, TextEditingController? textEditingController, super.title, super.validators = const [], - super.value = '', + super.value = "", this.textStyle, this.onChange, this.textFieldDecoration, this.padding = const EdgeInsets.all(8.0), + this.textInputType, }) { textController = textEditingController ?? TextEditingController(text: value); } + /// The controller for the text input. late TextEditingController textController; + + /// The text style for the text input. final TextStyle? textStyle; + + /// A callback function triggered when the value of the field changes. final Function(String value)? onChange; + + /// The decoration for the text input field. final InputDecoration? textFieldDecoration; + + /// The padding around the text input field. final EdgeInsets padding; - @override - 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; - } - } + /// The type of text input. + final TextInputType? textInputType; - return null; - }, - ), - ); - } + @override + Widget build(BuildContext context, Function onValueChanged) => Padding( + padding: padding, + child: TextFormField( + keyboardType: textInputType, + style: textStyle, + decoration: textFieldDecoration, + controller: textController, + onChanged: (v) { + value = v; + onChange?.call(value); + // ignore: avoid_dynamic_calls + onValueChanged(); + }, + validator: (value) { + for (var validator in validators) { + var output = validator(value); + if (output != null) { + return output; + } + } + + return null; + }, + ), + ); } diff --git a/lib/src/registration_screen.dart b/lib/src/registration_screen.dart index 7a98939..9bd761f 100644 --- a/lib/src/registration_screen.dart +++ b/lib/src/registration_screen.dart @@ -1,8 +1,8 @@ -import 'dart:collection'; +import "dart:collection"; -import 'package:flutter/material.dart'; -import 'package:flutter_registration/flutter_registration.dart'; -import 'package:flutter_registration/src/auth_screen.dart'; +import "package:flutter/material.dart"; +import "package:flutter_registration/flutter_registration.dart"; +import "package:flutter_registration/src/auth_screen.dart"; /// A screen for user registration. class RegistrationScreen extends StatefulWidget { @@ -11,8 +11,8 @@ class RegistrationScreen extends StatefulWidget { /// [registrationOptions] specifies the registration options. const RegistrationScreen({ required this.registrationOptions, - Key? key, - }) : super(key: key); + super.key, + }); /// The registration options. final RegistrationOptions registrationOptions; @@ -23,17 +23,13 @@ class RegistrationScreen extends StatefulWidget { /// The state for [RegistrationScreen]. class RegistrationScreenState extends State { - bool _isLoading = false; - /// Registers the user. Future _register({ required HashMap values, required void Function(int? pageToReturn) onError, }) async { try { - setState(() { - _isLoading = true; - }); + await widget.registrationOptions.beforeRegistration?.call(); var registered = await widget.registrationOptions.registrationRepository! .register(values); @@ -45,12 +41,8 @@ class RegistrationScreenState extends State { onError(pageToReturn); } - } catch (e) { - onError(0); - } finally { - setState(() { - _isLoading = false; - }); + } on Exception catch (_) { + onError(null); } } @@ -75,7 +67,6 @@ class RegistrationScreenState extends State { customBackgroundColor: widget.registrationOptions.backgroundColor, titleWidget: widget.registrationOptions.titleWidget, loginButton: widget.registrationOptions.loginButton, - isLoading: _isLoading, titleFlex: widget.registrationOptions.titleFlex, formFlex: widget.registrationOptions.formFlex, beforeTitleFlex: widget.registrationOptions.beforeTitleFlex, diff --git a/lib/src/service/registration_repository.dart b/lib/src/service/registration_repository.dart index a1295cc..74e16a0 100644 --- a/lib/src/service/registration_repository.dart +++ b/lib/src/service/registration_repository.dart @@ -2,8 +2,10 @@ // // SPDX-License-Identifier: BSD-3-Clause -import 'dart:collection'; +import "dart:collection"; +/// A mixin for a registration repository. mixin RegistrationRepository { + /// Registers a user with the given values. Future register(HashMap values); } diff --git a/pubspec.yaml b/pubspec.yaml index e99f216..0aa9321 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ name: flutter_registration description: A Flutter Registration package -version: 2.0.4 +version: 3.0.0 repository: https://github.com/Iconica-Development/flutter_registration publish_to: https://forgejo.internal.iconica.nl/api/packages/internal/pub @@ -16,7 +16,7 @@ environment: dependencies: flutter_input_library: hosted: https://forgejo.internal.iconica.nl/api/packages/internal/pub - version: ">=3.1.0 <4.0.0" + version: ^3.6.0 flutter: sdk: flutter @@ -26,6 +26,9 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_iconica_analysis: + git: + url: https://github.com/Iconica-Development/flutter_iconica_analysis + ref: 7.0.0 flutter: diff --git a/test/flutter_email_password_login_test.dart b/test/flutter_email_password_login_test.dart index 0a17bd7..d327631 100644 --- a/test/flutter_email_password_login_test.dart +++ b/test/flutter_email_password_login_test.dart @@ -3,10 +3,10 @@ // SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: GPL-3.0-or-later -import 'package:flutter_test/flutter_test.dart'; +import "package:flutter_test/flutter_test.dart"; void main() { - test('test', () { + test("test", () { expect(true, true); }); }