diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml new file mode 100644 index 0000000..50bb90a --- /dev/null +++ b/.github/workflows/flutter.yml @@ -0,0 +1,32 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: + - master + - feature/* + - bugfix/* + - hotfix/* + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.gradle/wrapper + /opt/hostedtoolcache/flutter + key: ${{ runner.OS }}-flutter-install-cache + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Flutter pub get + run: flutter pub get + - name: Flutter format + run: flutter format -o none --set-exit-if-changed . + - name: Flutter analyze + run: flutter analyze diff --git a/README.md b/README.md index 111dc25..0bf7d56 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,15 @@ +[![pub package](https://img.shields.io/pub/v/flutter_introduction_widget.svg)](https://github.com/Iconica-Development) [![Build status](https://img.shields.io/github/workflow/status/Iconica-Development/flutter_login_widget/CI)](https://github.com/Iconica-Development/flutter_login_widget/actions/new) [![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](https://github.com/tenhobi/effective_dart) + +# Login Widget A package facilitating the basic ingredients for creating functional yet customizable login pages -## Features +[Login GIF](flutter_login.gif) -Create a login screen for email and password logins -Create a forgot password screen by passing in the email from the login +## Setup -## Getting started +To use this package, add `flutter_login_widget` as a dependency in your pubspec.yaml file. -1. install the package by adding the following to your pubspec.yaml - ``` - flutter_login: - git: - url: https://github.com/Iconica-Development/flutter_login.git - ref: 1.0.0 - ``` - -## Usage +## How to use ```dart final loginOptions = LoginOptions( @@ -93,3 +87,17 @@ class ForgotPasswordScreen extends StatelessWidget { } ``` + +See the [Example Code](example/lib/main.dart) for an example on how to use this package. + +## Issues + +Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_login_widget) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl). + +## Want to contribute + +If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_login_widget/pulls). + +## Author + +This `flutter_login_widget` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 1dcb826..84cfa9f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,8 +9,10 @@ final loginOptions = LoginOptions( ), emailInputPrefix: const Icon(Icons.email), passwordInputPrefix: const Icon(Icons.password), - title: const Text('Login'), - image: const FlutterLogo(), + title: const Text('Login Demo'), + image: const FlutterLogo( + size: 200, + ), requestForgotPasswordButtonBuilder: ( context, onPressed, diff --git a/flutter_login.gif b/flutter_login.gif new file mode 100644 index 0000000..3e881c5 Binary files /dev/null and b/flutter_login.gif differ diff --git a/lib/src/widgets/email_password_login.dart b/lib/src/widgets/email_password_login.dart index a044818..1cb1330 100644 --- a/lib/src/widgets/email_password_login.dart +++ b/lib/src/widgets/email_password_login.dart @@ -65,140 +65,153 @@ class _EmailPasswordLoginFormState extends State { Widget build(BuildContext context) { var theme = Theme.of(context); var options = widget.options; - return Column( - children: [ - if (options.title != null) ...[ - const SizedBox( - height: 60, - ), - Align( - alignment: Alignment.topCenter, - child: _wrapWithDefaultStyle( - options.title, - theme.textTheme.headline6, - ), - ) - ], - if (options.subtitle != null) ...[ - const SizedBox( - height: 10, - ), - Align( - alignment: Alignment.topCenter, - child: _wrapWithDefaultStyle( - options.subtitle, - theme.textTheme.subtitle1, - ), - ) - ], - if (options.image != null) ...[ - Padding( - padding: const EdgeInsets.all(16), - child: options.image, - ), - ], - Expanded( - child: ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 300, - ), - child: Form( - key: _formKey, - child: Align( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - options.emailInputContainerBuilder( - TextFormField( - onChanged: _updateCurrentEmail, - validator: widget.options.validations.validateEmail, - initialValue: options.initialEmail, - keyboardType: TextInputType.emailAddress, - textInputAction: TextInputAction.next, - decoration: options.decoration.copyWith( - hintText: options.emailHintText, - prefixIcon: options.emailInputPrefix, - label: options.emailLabel, - ), - ), - ), - const SizedBox(height: 24), - options.passwordInputContainerBuilder( - TextFormField( - obscureText: _obscurePassword, - onChanged: _updateCurrentPassword, - validator: widget.options.validations.validatePassword, - initialValue: options.initialPassword, - keyboardType: TextInputType.visiblePassword, - textInputAction: TextInputAction.done, - onFieldSubmitted: (_) => _handleLogin(), - decoration: options.decoration.copyWith( - hintText: options.passwordHintText, - label: options.passwordLabel, - prefixIcon: options.passwordInputPrefix, - suffixIcon: IconButton( - onPressed: () { - setState(() { - _obscurePassword = !_obscurePassword; - }); - }, - icon: Icon( - _obscurePassword - ? Icons.visibility - : Icons.visibility_off, + return CustomScrollView( + physics: MediaQuery.of(context).viewInsets.bottom == 0 + ? const NeverScrollableScrollPhysics() + : null, + slivers: [ + SliverFillRemaining( + fillOverscroll: true, + hasScrollBody: false, + child: Column( + children: [ + if (options.title != null) ...[ + const SizedBox( + height: 60, + ), + Align( + alignment: Alignment.topCenter, + child: _wrapWithDefaultStyle( + options.title, + theme.textTheme.headline6, + ), + ) + ], + if (options.subtitle != null) ...[ + const SizedBox( + height: 10, + ), + Align( + alignment: Alignment.topCenter, + child: _wrapWithDefaultStyle( + options.subtitle, + theme.textTheme.subtitle1, + ), + ) + ], + if (options.image != null) ...[ + Padding( + padding: const EdgeInsets.all(16), + child: options.image, + ), + ], + Expanded( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 300, + ), + child: Form( + key: _formKey, + child: Align( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + options.emailInputContainerBuilder( + TextFormField( + onChanged: _updateCurrentEmail, + validator: + widget.options.validations.validateEmail, + initialValue: options.initialEmail, + keyboardType: TextInputType.emailAddress, + textInputAction: TextInputAction.next, + decoration: options.decoration.copyWith( + hintText: options.emailHintText, + prefixIcon: options.emailInputPrefix, + label: options.emailLabel, + ), ), ), - ), + const SizedBox(height: 24), + options.passwordInputContainerBuilder( + TextFormField( + obscureText: _obscurePassword, + onChanged: _updateCurrentPassword, + validator: + widget.options.validations.validatePassword, + initialValue: options.initialPassword, + keyboardType: TextInputType.visiblePassword, + textInputAction: TextInputAction.done, + onFieldSubmitted: (_) => _handleLogin(), + decoration: options.decoration.copyWith( + hintText: options.passwordHintText, + label: options.passwordLabel, + prefixIcon: options.passwordInputPrefix, + suffixIcon: IconButton( + onPressed: () { + setState(() { + _obscurePassword = !_obscurePassword; + }); + }, + icon: Icon( + _obscurePassword + ? Icons.visibility + : Icons.visibility_off, + ), + ), + ), + ), + ), + const SizedBox(height: 24), + if (widget.onForgotPassword != null) ...[ + Align( + alignment: Alignment.topRight, + child: options.forgotPasswordButtonBuilder( + context, + () { + widget.onForgotPassword?.call(_currentEmail); + }, + false, + () {}, + options, + ), + ), + ], + const SizedBox(height: 8), + AnimatedBuilder( + animation: _formValid, + builder: (context, _) { + return options.loginButtonBuilder( + context, + _handleLogin, + !_formValid.value, + () { + _formKey.currentState?.validate(); + }, + options, + ); + }, + ), + if (widget.onRegister != null) ...[ + options.registrationButtonBuilder( + context, + () { + widget.onRegister?.call( + _currentEmail, + _currentPassword, + ); + }, + false, + () {}, + options, + ), + ] + ], ), ), - const SizedBox(height: 24), - if (widget.onForgotPassword != null) ...[ - Align( - alignment: Alignment.topRight, - child: options.forgotPasswordButtonBuilder( - context, - () { - widget.onForgotPassword?.call(_currentEmail); - }, - false, - () {}, - options, - ), - ), - ], - const SizedBox(height: 8), - AnimatedBuilder( - animation: _formValid, - builder: (context, _) { - return options.loginButtonBuilder( - context, - _handleLogin, - !_formValid.value, - () { - _formKey.currentState?.validate(); - }, - options, - ); - }, - ), - if (widget.onRegister != null) ...[ - options.registrationButtonBuilder( - context, - () { - widget.onRegister?.call( - _currentEmail, - _currentPassword, - ); - }, - false, - () {}, - options, - ), - ] - ], + ), ), ), - ), + ], ), ), ],