mirror of
https://github.com/Iconica-Development/flutter_registration.git
synced 2025-05-19 05:23:43 +02:00
Merge pull request #13 from Iconica-Development/feat/buttons_and_fields
FInish flutter_registration for traffic university
This commit is contained in:
commit
0472ebc4c5
14 changed files with 518 additions and 197 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -3,6 +3,21 @@ SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
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
|
# 1.2.0
|
||||||
|
|
||||||
- feat: Added the ability to have an async register function so you can call it asynchronous.
|
- feat: Added the ability to have an async register function so you can call it asynchronous.
|
||||||
|
|
|
@ -26,6 +26,6 @@ subprojects {
|
||||||
project.evaluationDependsOn(':app')
|
project.evaluationDependsOn(':app')
|
||||||
}
|
}
|
||||||
|
|
||||||
task clean(type: Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,27 +2,81 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_registration/flutter_registration.dart';
|
import 'package:flutter_registration/flutter_registration.dart';
|
||||||
import 'example_registration_repository.dart';
|
import 'example_registration_repository.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(
|
runApp(
|
||||||
const MaterialApp(
|
MaterialApp(
|
||||||
home: FlutterRegistrationDemo(),
|
theme: ThemeData(
|
||||||
|
inputDecorationTheme: const InputDecorationTheme(
|
||||||
|
errorStyle: TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
home: const FlutterRegistrationDemo(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlutterRegistrationDemo extends StatelessWidget {
|
class FlutterRegistrationDemo extends StatefulWidget {
|
||||||
const FlutterRegistrationDemo({Key? key}) : super(key: key);
|
const FlutterRegistrationDemo({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FlutterRegistrationDemo> createState() =>
|
||||||
|
_FlutterRegistrationDemoState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FlutterRegistrationDemoState extends State<FlutterRegistrationDemo> {
|
||||||
|
late List<AuthStep> steps;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
steps = RegistrationOptions.getDefaultSteps();
|
||||||
|
|
||||||
|
steps[1].fields.add(
|
||||||
|
AuthBoolField(
|
||||||
|
name: 'termsConditions',
|
||||||
|
widgetType: BoolWidgetType.checkbox,
|
||||||
|
validators: [
|
||||||
|
(value) {
|
||||||
|
if (value == null || !value) {
|
||||||
|
return 'Required';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
rightWidget: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: 'I agree with the ',
|
||||||
|
children: <TextSpan>[
|
||||||
|
TextSpan(
|
||||||
|
text: 'terms & conditions',
|
||||||
|
style: const TextStyle(
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () {
|
||||||
|
debugPrint('Open terms and conditions');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return RegistrationScreen(
|
return RegistrationScreen(
|
||||||
registrationOptions: RegistrationOptions(
|
registrationOptions: RegistrationOptions(
|
||||||
registrationRepository: ExampleRegistrationRepository(),
|
registrationRepository: ExampleRegistrationRepository(),
|
||||||
registrationSteps: RegistrationOptions.getDefaultSteps(),
|
registrationSteps: steps,
|
||||||
afterRegistration: () {
|
afterRegistration: () {
|
||||||
debugPrint('Registered!');
|
debugPrint('Registered!');
|
||||||
},
|
},
|
||||||
|
|
|
@ -37,18 +37,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
|
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.17.2"
|
version: "1.18.0"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.6"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -62,14 +62,23 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: flutter_lints
|
name: flutter_lints
|
||||||
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.3"
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -81,7 +90,7 @@ packages:
|
||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.5.0"
|
version: "1.2.0"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -99,10 +108,10 @@ packages:
|
||||||
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:
|
||||||
|
@ -123,10 +132,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
|
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.10.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -152,18 +161,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stack_trace
|
name: stack_trace
|
||||||
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.0"
|
version: "1.11.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -184,10 +193,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
|
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.1"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -200,10 +209,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
|
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4-beta"
|
version: "0.3.0"
|
||||||
sdks:
|
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"
|
flutter: ">=1.17.0"
|
||||||
|
|
|
@ -10,5 +10,10 @@ export 'src/model/auth_exception.dart';
|
||||||
export 'src/model/auth_field.dart';
|
export 'src/model/auth_field.dart';
|
||||||
export 'src/model/auth_step.dart';
|
export 'src/model/auth_step.dart';
|
||||||
export 'src/model/auth_text_field.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/registration_screen.dart';
|
||||||
export 'src/service/registration_repository.dart';
|
export 'src/service/registration_repository.dart';
|
||||||
|
export 'package:flutter_input_library/flutter_input_library.dart'
|
||||||
|
show BoolWidgetType;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'package:flutter_registration/flutter_registration.dart';
|
||||||
|
|
||||||
class AuthScreen extends StatefulWidget {
|
class AuthScreen extends StatefulWidget {
|
||||||
const AuthScreen({
|
const AuthScreen({
|
||||||
required this.title,
|
required this.appBarTitle,
|
||||||
required this.steps,
|
required this.steps,
|
||||||
required this.submitBtnTitle,
|
required this.submitBtnTitle,
|
||||||
required this.nextBtnTitle,
|
required this.nextBtnTitle,
|
||||||
|
@ -19,12 +19,18 @@ class AuthScreen extends StatefulWidget {
|
||||||
this.customBackgroundColor,
|
this.customBackgroundColor,
|
||||||
this.nextButtonBuilder,
|
this.nextButtonBuilder,
|
||||||
this.previousButtonBuilder,
|
this.previousButtonBuilder,
|
||||||
|
this.titleWidget,
|
||||||
|
this.loginButton,
|
||||||
|
this.titleFlex,
|
||||||
|
this.formFlex,
|
||||||
|
this.beforeTitleFlex,
|
||||||
|
this.afterTitleFlex,
|
||||||
super.key,
|
super.key,
|
||||||
}) : assert(steps.length > 0, 'At least one step is required');
|
}) : assert(steps.length > 0, 'At least one step is required');
|
||||||
|
|
||||||
final String title;
|
final String appBarTitle;
|
||||||
final Future<void> Function({
|
final Future<void> Function({
|
||||||
required HashMap<String, String> values,
|
required HashMap<String, dynamic> values,
|
||||||
required void Function(int? pageToReturn) onError,
|
required void Function(int? pageToReturn) onError,
|
||||||
}) onFinish;
|
}) onFinish;
|
||||||
final List<AuthStep> steps;
|
final List<AuthStep> steps;
|
||||||
|
@ -33,8 +39,16 @@ class AuthScreen extends StatefulWidget {
|
||||||
final String previousBtnTitle;
|
final String previousBtnTitle;
|
||||||
final AppBar? customAppBar;
|
final AppBar? customAppBar;
|
||||||
final Color? customBackgroundColor;
|
final Color? customBackgroundColor;
|
||||||
final Widget Function(Future<void> Function(), String)? nextButtonBuilder;
|
final Widget Function(Future<void> Function()? onPressed, String label,
|
||||||
final Widget Function(VoidCallback, String)? previousButtonBuilder;
|
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
|
@override
|
||||||
State<AuthScreen> createState() => _AuthScreenState();
|
State<AuthScreen> createState() => _AuthScreenState();
|
||||||
|
@ -45,14 +59,17 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
final _pageController = PageController();
|
final _pageController = PageController();
|
||||||
final _animationDuration = const Duration(milliseconds: 300);
|
final _animationDuration = const Duration(milliseconds: 300);
|
||||||
final _animationCurve = Curves.ease;
|
final _animationCurve = Curves.ease;
|
||||||
|
bool _formValid = false;
|
||||||
|
|
||||||
AppBar get _appBar =>
|
AppBar get _appBar =>
|
||||||
widget.customAppBar ??
|
widget.customAppBar ??
|
||||||
AppBar(
|
AppBar(
|
||||||
title: Text(widget.title),
|
title: Text(widget.appBarTitle),
|
||||||
);
|
);
|
||||||
|
|
||||||
void onPrevious() {
|
void onPrevious() {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
_validate(_pageController.page!.toInt() - 1);
|
||||||
_pageController.previousPage(
|
_pageController.previousPage(
|
||||||
duration: _animationDuration,
|
duration: _animationDuration,
|
||||||
curve: _animationCurve,
|
curve: _animationCurve,
|
||||||
|
@ -69,12 +86,11 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
|
|
||||||
if (widget.steps.last == step) {
|
if (widget.steps.last == step) {
|
||||||
var values = HashMap<String, String>();
|
var values = HashMap<String, dynamic>();
|
||||||
|
|
||||||
for (var step in widget.steps) {
|
for (var step in widget.steps) {
|
||||||
for (var field in step.fields) {
|
for (var field in step.fields) {
|
||||||
values[field.name] =
|
values[field.name] = field.value;
|
||||||
(field as AuthTextField).textController.value.text;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +105,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
_validate(_pageController.page!.toInt() + 1);
|
||||||
_pageController.nextPage(
|
_pageController.nextPage(
|
||||||
duration: _animationDuration,
|
duration: _animationDuration,
|
||||||
curve: _animationCurve,
|
curve: _animationCurve,
|
||||||
|
@ -96,6 +113,29 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -107,21 +147,36 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
controller: _pageController,
|
controller: _pageController,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
for (AuthStep step in widget.steps)
|
for (var i = 0; i < widget.steps.length; i++)
|
||||||
Column(
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Flexible(
|
if (widget.titleWidget != null) ...[
|
||||||
child: Center(
|
Expanded(
|
||||||
child: ListView(
|
flex: widget.titleFlex ?? 1,
|
||||||
physics: const ClampingScrollPhysics(),
|
child: Column(
|
||||||
shrinkWrap: true,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
padding: const EdgeInsets.symmetric(
|
mainAxisSize: MainAxisSize.min,
|
||||||
vertical: 8.0,
|
|
||||||
horizontal: 30.0,
|
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
for (AuthField field in step.fields)
|
Expanded(
|
||||||
|
flex: widget.beforeTitleFlex ?? 3,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
widget.titleWidget!,
|
||||||
|
Expanded(
|
||||||
|
flex: widget.afterTitleFlex ?? 2,
|
||||||
|
child: Container(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Expanded(
|
||||||
|
flex: widget.formFlex ?? 3,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
for (AuthField field in widget.steps[i].fields)
|
||||||
Align(
|
Align(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -129,31 +184,46 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
if (field.title != null) ...[
|
if (field.title != null) ...[
|
||||||
field.title!,
|
field.title!,
|
||||||
],
|
],
|
||||||
field.build(),
|
field.build(context, () {
|
||||||
|
_validate(i);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: 15.0,
|
top: 15.0,
|
||||||
bottom: 30.0,
|
bottom: 30.0,
|
||||||
left: 30.0,
|
left: widget.previousButtonBuilder == null &&
|
||||||
right: 30.0,
|
widget.steps.first != widget.steps[i]
|
||||||
|
? 30.0
|
||||||
|
: 0.0,
|
||||||
|
right: widget.nextButtonBuilder == null &&
|
||||||
|
widget.previousButtonBuilder == null
|
||||||
|
? 30.0
|
||||||
|
: 0.0,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Column(
|
||||||
mainAxisAlignment: widget.steps.first != step
|
|
||||||
? MainAxisAlignment.spaceBetween
|
|
||||||
: MainAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
if (widget.steps.first != step)
|
Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
(widget.previousButtonBuilder != null &&
|
||||||
widget.previousButtonBuilder?.call(
|
widget.previousButtonBuilder?.call(
|
||||||
onPrevious,
|
onPrevious,
|
||||||
widget.previousBtnTitle,
|
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(
|
ElevatedButton(
|
||||||
onPressed: onPrevious,
|
onPressed: onPrevious,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
@ -163,28 +233,41 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
size: 18,
|
size: 18,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 4.0),
|
padding:
|
||||||
|
const EdgeInsets.only(left: 4.0),
|
||||||
child: Text(widget.previousBtnTitle),
|
child: Text(widget.previousBtnTitle),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
] else if (widget.previousButtonBuilder?.call(
|
||||||
|
onPrevious, widget.previousBtnTitle, i) !=
|
||||||
|
null) ...[
|
||||||
|
widget.previousButtonBuilder!
|
||||||
|
.call(onPrevious, widget.previousBtnTitle, i)!
|
||||||
|
],
|
||||||
widget.nextButtonBuilder?.call(
|
widget.nextButtonBuilder?.call(
|
||||||
() async {
|
!_formValid
|
||||||
await onNext(step);
|
? null
|
||||||
|
: () async {
|
||||||
|
await onNext(widget.steps[i]);
|
||||||
},
|
},
|
||||||
widget.steps.last == step
|
widget.steps.last == widget.steps[i]
|
||||||
? widget.submitBtnTitle
|
? widget.submitBtnTitle
|
||||||
: widget.nextBtnTitle,
|
: widget.nextBtnTitle,
|
||||||
|
i,
|
||||||
|
_formValid,
|
||||||
) ??
|
) ??
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: !_formValid
|
||||||
await onNext(step);
|
? null
|
||||||
|
: () async {
|
||||||
|
await onNext(widget.steps[i]);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.steps.last == step
|
widget.steps.last == widget.steps[i]
|
||||||
? widget.submitBtnTitle
|
? widget.submitBtnTitle
|
||||||
: widget.nextBtnTitle,
|
: widget.nextBtnTitle,
|
||||||
),
|
),
|
||||||
|
@ -200,7 +283,14 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
if (widget.loginButton != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 20.0),
|
||||||
|
child: widget.loginButton!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -18,6 +18,8 @@ class RegistrationOptions {
|
||||||
this.nextButtonBuilder,
|
this.nextButtonBuilder,
|
||||||
this.previousButtonBuilder,
|
this.previousButtonBuilder,
|
||||||
this.backgroundColor,
|
this.backgroundColor,
|
||||||
|
this.titleWidget,
|
||||||
|
this.loginButton,
|
||||||
});
|
});
|
||||||
|
|
||||||
final RegistrationTranslations registrationTranslations;
|
final RegistrationTranslations registrationTranslations;
|
||||||
|
@ -26,11 +28,13 @@ class RegistrationOptions {
|
||||||
final VoidCallback afterRegistration;
|
final VoidCallback afterRegistration;
|
||||||
final RegistrationRepository registrationRepository;
|
final RegistrationRepository registrationRepository;
|
||||||
final AppBar Function(String title)? customAppbarBuilder;
|
final AppBar Function(String title)? customAppbarBuilder;
|
||||||
final Widget Function(Future<void> Function() onPressed, String label)?
|
final Widget Function(Future<void> Function()? onPressed, String label,
|
||||||
nextButtonBuilder;
|
int step, bool enabled)? nextButtonBuilder;
|
||||||
final Widget Function(VoidCallback onPressed, String label)?
|
final Widget? Function(VoidCallback onPressed, String label, int step)?
|
||||||
previousButtonBuilder;
|
previousButtonBuilder;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
|
Widget? titleWidget;
|
||||||
|
Widget? loginButton;
|
||||||
|
|
||||||
static List<AuthStep> getDefaultSteps({
|
static List<AuthStep> getDefaultSteps({
|
||||||
TextEditingController? emailController,
|
TextEditingController? emailController,
|
||||||
|
@ -69,8 +73,10 @@ class RegistrationOptions {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
textFieldDecoration: InputDecoration(
|
||||||
label: labelBuilder?.call(translations.defaultEmailLabel),
|
label: labelBuilder?.call(translations.defaultEmailLabel),
|
||||||
hintText: translations.defaultEmailHint,
|
hintText: translations.defaultEmailHint,
|
||||||
|
),
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
validators: [
|
validators: [
|
||||||
(email) => (email == null || email.isEmpty)
|
(email) => (email == null || email.isEmpty)
|
||||||
|
@ -87,7 +93,7 @@ class RegistrationOptions {
|
||||||
),
|
),
|
||||||
AuthStep(
|
AuthStep(
|
||||||
fields: [
|
fields: [
|
||||||
AuthTextField(
|
AuthPassField(
|
||||||
name: 'password1',
|
name: 'password1',
|
||||||
textEditingController: pass1Controller,
|
textEditingController: pass1Controller,
|
||||||
title: titleBuilder?.call(
|
title: titleBuilder?.call(
|
||||||
|
@ -105,10 +111,11 @@ class RegistrationOptions {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
textFieldDecoration: InputDecoration(
|
||||||
label: labelBuilder?.call(translations.defaultPassword1Label),
|
label: labelBuilder?.call(translations.defaultPassword1Label),
|
||||||
hintText: translations.defaultPassword1Hint,
|
hintText: translations.defaultPassword1Hint,
|
||||||
|
),
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
obscureText: true,
|
|
||||||
validators: [
|
validators: [
|
||||||
(value) => (value == null || value.isEmpty)
|
(value) => (value == null || value.isEmpty)
|
||||||
? translations.defaultPassword1ValidatorMessage
|
? translations.defaultPassword1ValidatorMessage
|
||||||
|
@ -117,12 +124,8 @@ class RegistrationOptions {
|
||||||
onChange: (value) {
|
onChange: (value) {
|
||||||
password1 = value;
|
password1 = value;
|
||||||
},
|
},
|
||||||
hidden: pass1Hidden,
|
|
||||||
onPassChanged: (value) {
|
|
||||||
passHideOnChange?.call(true, value);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
AuthTextField(
|
AuthPassField(
|
||||||
name: 'password2',
|
name: 'password2',
|
||||||
textEditingController: pass2Controller,
|
textEditingController: pass2Controller,
|
||||||
title: titleBuilder?.call(
|
title: titleBuilder?.call(
|
||||||
|
@ -140,10 +143,11 @@ class RegistrationOptions {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
textFieldDecoration: InputDecoration(
|
||||||
label: labelBuilder?.call(translations.defaultPassword2Label),
|
label: labelBuilder?.call(translations.defaultPassword2Label),
|
||||||
hintText: translations.defaultPassword2Hint,
|
hintText: translations.defaultPassword2Hint,
|
||||||
|
),
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
obscureText: true,
|
|
||||||
validators: [
|
validators: [
|
||||||
(value) {
|
(value) {
|
||||||
if (pass1Controller != null) {
|
if (pass1Controller != null) {
|
||||||
|
@ -158,10 +162,6 @@ class RegistrationOptions {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
hidden: pass2Hidden,
|
|
||||||
onPassChanged: (value) {
|
|
||||||
passHideOnChange?.call(false, value);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
48
lib/src/model/auth_bool_field.dart
Normal file
48
lib/src/model/auth_bool_field.dart
Normal file
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
60
lib/src/model/auth_drop_down.dart
Normal file
60
lib/src/model/auth_drop_down.dart
Normal file
|
@ -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<String> 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<String>(
|
||||||
|
icon: icon,
|
||||||
|
style: textStyle,
|
||||||
|
value: selectedValue,
|
||||||
|
decoration: dropdownDecoration,
|
||||||
|
items: items.map((String value) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,18 +4,21 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
abstract class AuthField {
|
abstract class AuthField<T> {
|
||||||
AuthField({
|
AuthField({
|
||||||
required this.name,
|
required this.name,
|
||||||
|
required this.value,
|
||||||
|
this.onValueChanged,
|
||||||
this.title,
|
this.title,
|
||||||
this.validators = const [],
|
this.validators = const [],
|
||||||
this.value = '',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final Widget? title;
|
final Widget? title;
|
||||||
List<String? Function(String?)> validators;
|
List<String? Function(T?)> validators;
|
||||||
String value;
|
T value;
|
||||||
|
|
||||||
Widget build();
|
final Function(T)? onValueChanged; // Callback for value changes
|
||||||
|
|
||||||
|
Widget build(BuildContext context, Function onValueChanged);
|
||||||
}
|
}
|
||||||
|
|
55
lib/src/model/auth_pass_field.dart
Normal file
55
lib/src/model/auth_pass_field.dart
Normal file
|
@ -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;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,61 +12,33 @@ class AuthTextField extends AuthField {
|
||||||
super.title,
|
super.title,
|
||||||
super.validators = const [],
|
super.validators = const [],
|
||||||
super.value = '',
|
super.value = '',
|
||||||
this.obscureText = false,
|
|
||||||
this.hintText,
|
|
||||||
this.label,
|
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.onChange,
|
this.onChange,
|
||||||
this.hidden,
|
this.textFieldDecoration,
|
||||||
this.onPassChanged,
|
this.padding = const EdgeInsets.all(8.0),
|
||||||
}) {
|
}) {
|
||||||
textController =
|
textController =
|
||||||
textEditingController ?? TextEditingController(text: value);
|
textEditingController ?? TextEditingController(text: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
late TextEditingController textController;
|
late TextEditingController textController;
|
||||||
final bool obscureText;
|
|
||||||
final String? hintText;
|
|
||||||
final Widget? label;
|
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
final Function(String value)? onChange;
|
final Function(String value)? onChange;
|
||||||
final bool? hidden;
|
final InputDecoration? textFieldDecoration;
|
||||||
final Function(bool value)? onPassChanged;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build() {
|
Widget build(BuildContext context, Function onValueChanged) {
|
||||||
Widget? suffix;
|
return Padding(
|
||||||
|
padding: padding,
|
||||||
if (hidden != null) {
|
child: TextFormField(
|
||||||
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,
|
style: textStyle,
|
||||||
decoration: InputDecoration(
|
decoration: textFieldDecoration,
|
||||||
label: label,
|
|
||||||
hintText: hintText,
|
|
||||||
suffix: suffix,
|
|
||||||
),
|
|
||||||
controller: textController,
|
controller: textController,
|
||||||
obscureText: hidden ?? obscureText,
|
|
||||||
onChanged: (v) {
|
onChanged: (v) {
|
||||||
value = v;
|
value = v;
|
||||||
onChange?.call(value);
|
onChange?.call(value);
|
||||||
|
onValueChanged();
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
for (var validator in validators) {
|
for (var validator in validators) {
|
||||||
|
@ -78,6 +50,7 @@ class AuthTextField extends AuthField {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class RegistrationScreen extends StatelessWidget {
|
||||||
final RegistrationOptions registrationOptions;
|
final RegistrationOptions registrationOptions;
|
||||||
|
|
||||||
Future<void> register({
|
Future<void> register({
|
||||||
required HashMap<String, String> values,
|
required HashMap<String, dynamic> values,
|
||||||
required void Function(int? pageToReturn) onError,
|
required void Function(int? pageToReturn) onError,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
|
@ -46,13 +46,15 @@ class RegistrationScreen extends StatelessWidget {
|
||||||
translations.title,
|
translations.title,
|
||||||
),
|
),
|
||||||
onFinish: register,
|
onFinish: register,
|
||||||
title: translations.title,
|
appBarTitle: translations.title,
|
||||||
submitBtnTitle: translations.registerBtn,
|
submitBtnTitle: translations.registerBtn,
|
||||||
nextBtnTitle: translations.nextStepBtn,
|
nextBtnTitle: translations.nextStepBtn,
|
||||||
previousBtnTitle: translations.previousStepBtn,
|
previousBtnTitle: translations.previousStepBtn,
|
||||||
nextButtonBuilder: registrationOptions.nextButtonBuilder,
|
nextButtonBuilder: registrationOptions.nextButtonBuilder,
|
||||||
previousButtonBuilder: registrationOptions.previousButtonBuilder,
|
previousButtonBuilder: registrationOptions.previousButtonBuilder,
|
||||||
customBackgroundColor: registrationOptions.backgroundColor,
|
customBackgroundColor: registrationOptions.backgroundColor,
|
||||||
|
titleWidget: registrationOptions.titleWidget,
|
||||||
|
loginButton: registrationOptions.loginButton,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,21 @@
|
||||||
|
|
||||||
name: flutter_registration
|
name: flutter_registration
|
||||||
description: A Flutter Registration package
|
description: A Flutter Registration package
|
||||||
version: 1.2.0
|
version: 2.0.0
|
||||||
repository: https://github.com/Iconica-Development/flutter_registration
|
repository: https://github.com/Iconica-Development/flutter_registration
|
||||||
|
|
||||||
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.18.0 <3.0.0"
|
sdk: ">=2.18.0 <3.0.0"
|
||||||
flutter: ">=1.17.0"
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
flutter_input_library:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_input_library
|
||||||
|
ref: 3.0.1
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
|
|
Loading…
Reference in a new issue