diff --git a/README.md b/README.md index 9ffb02c..d571116 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# GestiónUH +# Gestión UH A UH managment app. -[![tests](https://github.com/rmarticedeno/gestionapp/actions/workflows/tests.yml/badge.svg)](https://github.com/rmarticedeno/gestionapp/actions/workflows/tests.yml) +[![tests](https://github.com/NODO-UH/gestionapp/actions/workflows/tests.yml/badge.svg)](https://github.com/NODO-UH/gestionapp/actions/workflows/tests.yml) [![Codemagic build status](https://api.codemagic.io/apps/607a1a18d35b7a12ffb6c7dc/607a1a18d35b7a12ffb6c7db/status_badge.svg)](https://codemagic.io/apps/607a1a18d35b7a12ffb6c7dc/607a1a18d35b7a12ffb6c7db/latest_build) diff --git a/analysis_options.yaml b/analysis_options.yaml index f23403b..ed0d062 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,6 +2,7 @@ include: package:lint/analysis_options.yaml linter: rules: + always_use_package_imports: true avoid_classes_with_only_static_members: false avoid_positional_boolean_parameters: false constant_identifier_names: false diff --git a/android/app/build.gradle b/android/app/build.gradle index 9799334..b467d7a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -25,6 +25,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + android { compileSdkVersion 29 @@ -37,7 +43,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "cu.uh.gestionuh" minSdkVersion 18 targetSdkVersion 29 @@ -45,13 +50,20 @@ android { versionName flutterVersionName } - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + + buildTypes { + release { + signingConfig signingConfigs.release + } + } } flutter { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 14c5d1d..c07c30f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - gestionuh + Gestión UH CFBundlePackageType APPL CFBundleShortVersionString diff --git a/lib/app.dart b/lib/app.dart index aab982d..acdcf5d 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -1,16 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'deps_injector.dart'; -import 'src/data/repository/auth_repository/auth_repository.dart'; -import 'src/presentation/blocs.dart'; -import 'src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart'; -import 'src/presentation/pages.dart'; -import 'src/presentation/pages/about_page.dart'; -import 'src/presentation/pages/register_page.dart'; -import 'src/presentation/theme.dart'; -import 'src/utils/constants.dart'; -import 'src/utils/constants/routes.dart'; +import 'package:gestionuh/deps_injector.dart'; +import 'package:gestionuh/src/data/repository/auth_repository/auth_repository.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; +import 'package:gestionuh/src/presentation/pages.dart'; +import 'package:gestionuh/src/presentation/pages/about_page.dart'; +import 'package:gestionuh/src/presentation/pages/register_page.dart'; +import 'package:gestionuh/src/presentation/theme.dart'; +import 'package:gestionuh/src/utils/constants.dart'; +import 'package:gestionuh/src/utils/constants/routes.dart'; class GestionUhApp extends StatelessWidget { @override @@ -67,9 +65,15 @@ class GestionUhApp extends StatelessWidget { ); case REGISTER_ROUTE_NAME: return MaterialPageRoute( - builder: (_) => BlocProvider( - create: (_) => di()..add(QuestionsRequestedRegister()), - child: const RegisterPage(), + builder: (_) => Overlay( + initialEntries: [ + OverlayEntry(builder: (context) { + return BlocProvider( + create: (_) => di()..add(QuestionsRequestedRegister()), + child: const RegisterPage(), + ); + }), + ], ), ); case ABOUT_ROUTE_NAME: diff --git a/lib/deps_injector.dart b/lib/deps_injector.dart index 8e8f7d3..1dff131 100644 --- a/lib/deps_injector.dart +++ b/lib/deps_injector.dart @@ -1,13 +1,11 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:gestionuh/src/data/api/api.dart'; +import 'package:gestionuh/src/data/local.dart'; +import 'package:gestionuh/src/data/repository.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; import 'package:get_it/get_it.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'src/data/api/api.dart'; -import 'src/data/local.dart'; -import 'src/data/repository.dart'; -import 'src/presentation/blocs.dart'; -import 'src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart'; - final di = GetIt.instance; Future init() async { diff --git a/lib/main.dart b/lib/main.dart index a4643f6..76adcfe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; - -import 'app.dart'; -import 'deps_injector.dart'; -import 'src/data/repository/auth_repository/auth_repository.dart'; +import 'package:gestionuh/app.dart'; +import 'package:gestionuh/deps_injector.dart'; +import 'package:gestionuh/src/data/repository/auth_repository/auth_repository.dart'; Future main() async { await initialize(); diff --git a/lib/src/data/api/api.dart b/lib/src/data/api/api.dart index 12d68bf..0e4119b 100644 --- a/lib/src/data/api/api.dart +++ b/lib/src/data/api/api.dart @@ -1,9 +1,8 @@ import 'dart:convert'; import 'package:dio/dio.dart'; - -import '../../utils/constants.dart'; -import '../models.dart'; +import 'package:gestionuh/src/data/models.dart'; +import 'package:gestionuh/src/utils/constants.dart'; typedef ClassBuilder = T Function( Map json); @@ -88,7 +87,7 @@ class GestionApi { ); } - UserData response = await apiRequest( + final UserData response = await apiRequest( Constants.userDataUrl, () => UserData(), null, @@ -252,16 +251,16 @@ class GestionApi { queryParameters: queryParams, ); } catch (error) { - target.error = error.toString(); + target.error = Errors.retrieveError(error.toString()); return target; } if (builder != null) { try { if (response.statusCode! >= 300) { - Error error = Error.fromJson( + final Error error = Error.fromJson( jsonDecode(response.data!) as Map); - target.error = error.message; + target.error = error.code.toString(); } else { target = builder(jsonDecode(response.data!) as Map); } @@ -270,6 +269,10 @@ class GestionApi { } } + if (target.error != null) { + target.error = Errors.retrieveError(target.error!); + } + return target; } } diff --git a/lib/src/data/models/auth.dart b/lib/src/data/models/auth.dart index e4be754..33ca826 100644 --- a/lib/src/data/models/auth.dart +++ b/lib/src/data/models/auth.dart @@ -12,5 +12,6 @@ class Auth extends BaseModel { static Auth fromJson(Map json) => _$AuthFromJson(json); + @override Map toJson() => _$AuthToJson(this); } diff --git a/lib/src/data/models/error.dart b/lib/src/data/models/error.dart index 5493e6d..cfef5e1 100644 --- a/lib/src/data/models/error.dart +++ b/lib/src/data/models/error.dart @@ -12,5 +12,6 @@ class Error extends BaseModel { static Error fromJson(Map json) => _$ErrorFromJson(json); + @override Map toJson() => _$ErrorToJson(this); } diff --git a/lib/src/data/models/login.dart b/lib/src/data/models/login.dart index 379c4c6..8dadb47 100644 --- a/lib/src/data/models/login.dart +++ b/lib/src/data/models/login.dart @@ -12,5 +12,6 @@ class Login extends BaseModel { static Login fromJson(Map json) => _$LoginFromJson(json); + @override Map toJson() => _$LoginToJson(this); } diff --git a/lib/src/data/models/mail_quota.dart b/lib/src/data/models/mail_quota.dart index 7e81579..df8ad54 100644 --- a/lib/src/data/models/mail_quota.dart +++ b/lib/src/data/models/mail_quota.dart @@ -13,5 +13,6 @@ class MailQuota extends BaseModel { static MailQuota fromJson(Map json) => _$MailQuotaFromJson(json); + @override Map toJson() => _$MailQuotaToJson(this); } diff --git a/lib/src/data/models/pass_reset.dart b/lib/src/data/models/pass_reset.dart index 5d5105f..8e66386 100644 --- a/lib/src/data/models/pass_reset.dart +++ b/lib/src/data/models/pass_reset.dart @@ -13,5 +13,6 @@ class PassReset extends BaseModel { static PassReset fromJson(Map json) => _$PassResetFromJson(json); + @override Map toJson() => _$PassResetToJson(this); } diff --git a/lib/src/data/models/password_edit_data.dart b/lib/src/data/models/password_edit_data.dart index bafc070..b507bb0 100644 --- a/lib/src/data/models/password_edit_data.dart +++ b/lib/src/data/models/password_edit_data.dart @@ -15,5 +15,6 @@ class PasswordEditData extends BaseModel { static PasswordEditData fromJson(Map json) => _$PasswordEditDataFromJson(json); + @override Map toJson() => _$PasswordEditDataToJson(this); } diff --git a/lib/src/data/models/password_reset_data.dart b/lib/src/data/models/password_reset_data.dart index 3499045..3b0cf4b 100644 --- a/lib/src/data/models/password_reset_data.dart +++ b/lib/src/data/models/password_reset_data.dart @@ -15,5 +15,6 @@ class PasswordResetData extends BaseModel { static PasswordResetData fromJson(Map json) => _$PasswordResetDataFromJson(json); + @override Map toJson() => _$PasswordResetDataToJson(this); } diff --git a/lib/src/data/models/password_reset_user_id.dart b/lib/src/data/models/password_reset_user_id.dart index 89e2fa4..e93c51e 100644 --- a/lib/src/data/models/password_reset_user_id.dart +++ b/lib/src/data/models/password_reset_user_id.dart @@ -12,5 +12,6 @@ class PasswordResetUserId extends BaseModel { static PasswordResetUserId fromJson(Map json) => _$PasswordResetUserIdFromJson(json); + @override Map toJson() => _$PasswordResetUserIdToJson(this); } diff --git a/lib/src/data/models/quota.dart b/lib/src/data/models/quota.dart index 314af8f..e8eeff7 100644 --- a/lib/src/data/models/quota.dart +++ b/lib/src/data/models/quota.dart @@ -13,5 +13,6 @@ class Quota extends BaseModel { static Quota fromJson(Map json) => _$QuotaFromJson(json); + @override Map toJson() => _$QuotaToJson(this); } diff --git a/lib/src/data/models/security_questions.dart b/lib/src/data/models/security_questions.dart index ee220db..2aa4a69 100644 --- a/lib/src/data/models/security_questions.dart +++ b/lib/src/data/models/security_questions.dart @@ -12,5 +12,6 @@ class SecurityQuestions extends BaseModel { static SecurityQuestions fromJson(Map json) => _$SecurityQuestionsFromJson(json); + @override Map toJson() => _$SecurityQuestionsToJson(this); } diff --git a/lib/src/data/models/user_ci.dart b/lib/src/data/models/user_ci.dart index c1d20ea..10a6beb 100644 --- a/lib/src/data/models/user_ci.dart +++ b/lib/src/data/models/user_ci.dart @@ -11,5 +11,6 @@ class UserCi extends BaseModel { static UserCi fromJson(Map json) => _$UserCiFromJson(json); + @override Map toJson() => _$UserCiToJson(this); } diff --git a/lib/src/data/models/user_data.dart b/lib/src/data/models/user_data.dart index 98bf6bd..4112b0e 100644 --- a/lib/src/data/models/user_data.dart +++ b/lib/src/data/models/user_data.dart @@ -28,5 +28,6 @@ class UserData extends BaseModel { static UserData fromJson(Map json) => _$UserDataFromJson(json); + @override Map toJson() => _$UserDataToJson(this); } diff --git a/lib/src/data/models/user_id.dart b/lib/src/data/models/user_id.dart index e17b127..da65fc3 100644 --- a/lib/src/data/models/user_id.dart +++ b/lib/src/data/models/user_id.dart @@ -11,5 +11,6 @@ class UserId extends BaseModel { static UserId fromJson(Map json) => _$UserIdFromJson(json); + @override Map toJson() => _$UserIdToJson(this); } diff --git a/lib/src/data/repository/auth_repository/auth_repository.dart b/lib/src/data/repository/auth_repository/auth_repository.dart index 38a1db7..382b648 100644 --- a/lib/src/data/repository/auth_repository/auth_repository.dart +++ b/lib/src/data/repository/auth_repository/auth_repository.dart @@ -1,10 +1,10 @@ import 'dart:developer'; -import '../../../utils/constants/storage_keys.dart'; -import '../../api/api.dart'; -import '../../local/local_storage.dart'; -import '../../models.dart'; -import '../../models/status.dart'; +import 'package:gestionuh/src/data/api/api.dart'; +import 'package:gestionuh/src/data/local/local_storage.dart'; +import 'package:gestionuh/src/data/models.dart'; +import 'package:gestionuh/src/data/models/status.dart'; +import 'package:gestionuh/src/utils/constants/storage_keys.dart'; class AuthRepository { final GestionApi api; diff --git a/lib/src/presentation/blocs/login_bloc/login_bloc.dart b/lib/src/presentation/blocs/login_bloc/login_bloc.dart index 40cf59f..88e2ec7 100644 --- a/lib/src/presentation/blocs/login_bloc/login_bloc.dart +++ b/lib/src/presentation/blocs/login_bloc/login_bloc.dart @@ -1,6 +1,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../../data/repository.dart'; +import 'package:gestionuh/src/data/repository.dart'; +import 'package:gestionuh/src/utils/constants.dart'; part 'login_event.dart'; part 'login_state.dart'; @@ -28,7 +28,7 @@ class LoginBloc extends Bloc { ); if (result == null) { yield LoginAttemptInitial( - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else if (result.error != null) { yield LoginAttemptInitial( diff --git a/lib/src/presentation/blocs/mail_quota_bloc/mail_quota_bloc.dart b/lib/src/presentation/blocs/mail_quota_bloc/mail_quota_bloc.dart index b62aeaa..77f8cca 100644 --- a/lib/src/presentation/blocs/mail_quota_bloc/mail_quota_bloc.dart +++ b/lib/src/presentation/blocs/mail_quota_bloc/mail_quota_bloc.dart @@ -1,6 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gestionuh/src/data/models.dart'; import 'package:gestionuh/src/data/repository.dart'; +import 'package:gestionuh/src/utils/constants.dart'; part 'mail_quota_event.dart'; part 'mail_quota_state.dart'; @@ -25,7 +26,7 @@ class MailQuotaBloc extends Bloc { final result = await mailQuotasRepository.getQuota(); if (result == null) { yield MailQuotaLoadedFailure( - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else if (result.error != null) { yield MailQuotaLoadedFailure( diff --git a/lib/src/presentation/blocs/profile_bloc/profile_bloc.dart b/lib/src/presentation/blocs/profile_bloc/profile_bloc.dart index fb3c1df..bac8cbd 100644 --- a/lib/src/presentation/blocs/profile_bloc/profile_bloc.dart +++ b/lib/src/presentation/blocs/profile_bloc/profile_bloc.dart @@ -1,6 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gestionuh/src/data/models.dart'; import 'package:gestionuh/src/data/repository.dart'; +import 'package:gestionuh/src/utils/constants.dart'; part 'profile_event.dart'; part 'profile_state.dart'; @@ -25,7 +26,7 @@ class ProfileBloc extends Bloc { final result = await profileRepository.getUserData(); if (result == null) { yield ProfileLoadedFailure( - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else if (result.error != null) { yield ProfileLoadedFailure( diff --git a/lib/src/presentation/blocs/quota_bloc/quota_bloc.dart b/lib/src/presentation/blocs/quota_bloc/quota_bloc.dart index 80174dd..4ec5bf5 100644 --- a/lib/src/presentation/blocs/quota_bloc/quota_bloc.dart +++ b/lib/src/presentation/blocs/quota_bloc/quota_bloc.dart @@ -1,6 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gestionuh/src/data/models.dart'; import 'package:gestionuh/src/data/repository.dart'; +import 'package:gestionuh/src/utils/constants.dart'; part 'quota_event.dart'; part 'quota_state.dart'; @@ -24,7 +25,7 @@ class QuotaBloc extends Bloc { final result = await quotasRepository.getQuota(); if (result == null) { yield QuotaLoadedFailure( - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else if (result.error != null) { yield QuotaLoadedFailure( diff --git a/lib/src/presentation/blocs/recover_password_bloc/recover_password_bloc.dart b/lib/src/presentation/blocs/recover_password_bloc/recover_password_bloc.dart index a123b7f..16da32c 100644 --- a/lib/src/presentation/blocs/recover_password_bloc/recover_password_bloc.dart +++ b/lib/src/presentation/blocs/recover_password_bloc/recover_password_bloc.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:gestionuh/src/data/models.dart'; import 'package:gestionuh/src/data/repository.dart'; import 'package:meta/meta.dart'; +import 'package:gestionuh/src/utils/constants.dart'; part 'recover_password_event.dart'; part 'recover_password_state.dart'; @@ -38,7 +39,7 @@ class RecoverPasswordBloc if (result == null) { yield RecoverPasswordCIError( ci: event.state.ci, - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else if (result.error != null) { yield RecoverPasswordCIError( @@ -48,7 +49,7 @@ class RecoverPasswordBloc } else if (result.questions == null) { yield RecoverPasswordCIError( ci: event.state.ci, - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else { yield RecoverPasswordQuestions( @@ -83,7 +84,7 @@ class RecoverPasswordBloc questions: event.state.questions, answers: event.state.answers, password: event.state.password, - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else if (result.error != null) { yield RecoverPasswordQuestionsError( @@ -99,7 +100,7 @@ class RecoverPasswordBloc questions: event.state.questions, answers: event.state.answers, password: event.state.password, - error: 'Ha ocurrido un error inesperado.', + error: Errors.DefaultError, ); } else { yield RecoverPasswordSuccess(userId: result.userId!); diff --git a/lib/src/presentation/blocs/register_bloc/register_bloc.dart b/lib/src/presentation/blocs/register_bloc/register_bloc.dart index 5f6142a..e09599f 100644 --- a/lib/src/presentation/blocs/register_bloc/register_bloc.dart +++ b/lib/src/presentation/blocs/register_bloc/register_bloc.dart @@ -1,9 +1,8 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; - -import '../../../data/models/password_edit_data.dart'; -import '../../../data/repository/auth_repository/auth_repository.dart'; +import 'package:gestionuh/src/data/models/password_edit_data.dart'; +import 'package:gestionuh/src/data/repository/auth_repository/auth_repository.dart'; part 'register_event.dart'; part 'register_state.dart'; diff --git a/lib/src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart b/lib/src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart index 959c90e..c2737c3 100644 --- a/lib/src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart +++ b/lib/src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart @@ -1,8 +1,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; - -import '../../../data/repository.dart'; +import 'package:gestionuh/src/data/repository.dart'; part 'resetpassword_event.dart'; part 'resetpassword_state.dart'; diff --git a/lib/src/presentation/pages/about_page.dart b/lib/src/presentation/pages/about_page.dart index d579fcb..fe1a8cb 100644 --- a/lib/src/presentation/pages/about_page.dart +++ b/lib/src/presentation/pages/about_page.dart @@ -1,9 +1,8 @@ -import 'package:flash/flash.dart'; import 'package:flutter/material.dart'; +import 'package:gestionuh/src/presentation/widgets/flash_helper.dart'; +import 'package:gestionuh/src/utils/constants.dart'; import 'package:url_launcher/url_launcher.dart'; -import '../../utils/constants.dart'; - class AboutInformationPage extends StatelessWidget { const AboutInformationPage({Key? key}) : super(key: key); @@ -89,9 +88,9 @@ class AboutInformationPage extends StatelessWidget { if (await canLaunch(e.link!)) { await launch(e.link!); } else { - _showCenterFlash( + FlashHelper.errorBar( context, - error: + message: 'No puede acceder a ${e.link}', ); } @@ -126,9 +125,9 @@ class AboutInformationPage extends StatelessWidget { if (await canLaunch(url)) { await launch(url); } else { - _showCenterFlash( + FlashHelper.errorBar( context, - error: 'No puede acceder a $url', + message: 'No puede acceder a $url', ); } }, @@ -168,41 +167,6 @@ class AboutInformationPage extends StatelessWidget { ), ); } - - void _showCenterFlash( - BuildContext context, { - String? error, - FlashPosition position = FlashPosition.top, - FlashStyle style = FlashStyle.floating, - Alignment? alignment, - }) { - showFlash( - context: context, - duration: const Duration(seconds: 5), - builder: (_, controller) { - return Flash( - controller: controller, - backgroundColor: Colors.black87, - borderRadius: BorderRadius.circular(8.0), - borderColor: Colors.blue, - position: position, - style: style, - alignment: alignment, - enableDrag: false, - onTap: () => controller.dismiss(), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: DefaultTextStyle( - style: const TextStyle(color: Colors.white), - child: Text( - error!, - ), - ), - ), - ); - }, - ); - } } class DeveloperInfo { diff --git a/lib/src/presentation/pages/login_page.dart b/lib/src/presentation/pages/login_page.dart index 8295410..bbdeb4c 100644 --- a/lib/src/presentation/pages/login_page.dart +++ b/lib/src/presentation/pages/login_page.dart @@ -1,13 +1,12 @@ -import 'package:flash/flash.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../../deps_injector.dart'; -import '../../data/repository.dart'; -import '../../utils/constants/routes.dart'; -import '../blocs.dart'; -import '../widgets.dart'; -import '../widgets/bottom_sheet.dart'; +import 'package:gestionuh/deps_injector.dart'; +import 'package:gestionuh/src/data/repository.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; +import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/widgets/flash_helper.dart'; +import 'package:gestionuh/src/utils/constants/routes.dart'; class LoginPage extends StatefulWidget { const LoginPage({Key? key}) : super(key: key); @@ -61,11 +60,7 @@ class _LoginPageState extends State { Navigator.of(context).pushReplacementNamed(PROFILE_ROUTE_NAME); } if (state is LoginAttemptInitial) { - _showCenterFlash( - error: state.error, - position: FlashPosition.top, - style: FlashStyle.floating, - ); + FlashHelper.errorBar(context, message: state.error); } }, builder: (context, state) { @@ -188,38 +183,4 @@ class _LoginPageState extends State { ), ); } - - void _showCenterFlash({ - required String error, - FlashPosition? position, - FlashStyle? style, - Alignment? alignment, - }) { - showFlash( - context: context, - duration: const Duration(seconds: 5), - builder: (_, controller) { - return Flash( - controller: controller, - backgroundColor: Colors.black87, - borderRadius: BorderRadius.circular(8.0), - borderColor: Colors.blue, - position: position, - style: style, - alignment: alignment, - enableDrag: false, - onTap: () => controller.dismiss(), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: DefaultTextStyle( - style: const TextStyle(color: Colors.white), - child: Text( - error, - ), - ), - ), - ); - }, - ); - } } diff --git a/lib/src/presentation/pages/mail_quotas_page.dart b/lib/src/presentation/pages/mail_quotas_page.dart index df3bc50..a54b113 100644 --- a/lib/src/presentation/pages/mail_quotas_page.dart +++ b/lib/src/presentation/pages/mail_quotas_page.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../blocs.dart'; -import '../widgets.dart'; -import '../widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; +import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/bottom_sheet.dart'; class MailQuotaPage extends StatefulWidget { const MailQuotaPage({Key? key}) : super(key: key); diff --git a/lib/src/presentation/pages/profile_page.dart b/lib/src/presentation/pages/profile_page.dart index 663f476..01fc82b 100644 --- a/lib/src/presentation/pages/profile_page.dart +++ b/lib/src/presentation/pages/profile_page.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../blocs.dart'; -import '../widgets.dart'; -import '../widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; +import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/bottom_sheet.dart'; class ProfilePage extends StatefulWidget { const ProfilePage({Key? key}) : super(key: key); diff --git a/lib/src/presentation/pages/quota_page.dart b/lib/src/presentation/pages/quota_page.dart index 2b96743..aed4694 100644 --- a/lib/src/presentation/pages/quota_page.dart +++ b/lib/src/presentation/pages/quota_page.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../blocs.dart'; -import '../widgets.dart'; -import '../widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; +import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/bottom_sheet.dart'; class QuotaPage extends StatefulWidget { const QuotaPage({Key? key}) : super(key: key); diff --git a/lib/src/presentation/pages/recover_password_page.dart b/lib/src/presentation/pages/recover_password_page.dart index e894342..185da32 100644 --- a/lib/src/presentation/pages/recover_password_page.dart +++ b/lib/src/presentation/pages/recover_password_page.dart @@ -1,8 +1,8 @@ -import 'package:flash/flash.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gestionuh/src/presentation/blocs.dart'; import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/flash_helper.dart'; import 'package:gestionuh/src/utils/validators.dart'; class RecoverPasswordPage extends StatelessWidget { @@ -19,11 +19,7 @@ class RecoverPasswordPage extends StatelessWidget { body: BlocConsumer( listener: (context, state) { if (state is RecoverPasswordError) { - _showCenterFlash( - context: context, - message: state.error, - borderColor: Colors.red, - ); + FlashHelper.errorBar(context, message: state.error); } }, builder: (context, state) { @@ -219,40 +215,4 @@ class RecoverPasswordPage extends StatelessWidget { }, )); } - - void _showCenterFlash({ - required BuildContext context, - required String message, - FlashPosition position = FlashPosition.top, - FlashStyle style = FlashStyle.floating, - Alignment? alignment, - Color? borderColor, - }) { - showFlash( - context: context, - duration: const Duration(seconds: 5), - builder: (_, controller) { - return Flash( - controller: controller, - backgroundColor: Colors.black87, - borderRadius: BorderRadius.circular(8.0), - borderColor: borderColor ?? Colors.black, - position: position, - style: style, - alignment: alignment, - enableDrag: false, - onTap: () => controller.dismiss(), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: DefaultTextStyle( - style: const TextStyle(color: Colors.white), - child: Text( - message, - ), - ), - ), - ); - }, - ); - } } diff --git a/lib/src/presentation/pages/register_page.dart b/lib/src/presentation/pages/register_page.dart index 5722c37..4b1f967 100644 --- a/lib/src/presentation/pages/register_page.dart +++ b/lib/src/presentation/pages/register_page.dart @@ -1,14 +1,13 @@ import 'package:collection/collection.dart' show IterableExtension; -import 'package:flash/flash.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../utils/constants.dart'; -import '../../utils/pair.dart'; -import '../../utils/validators.dart'; -import '../blocs.dart'; -import '../widgets.dart'; -import '../widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/blocs.dart'; +import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/widgets/flash_helper.dart'; +import 'package:gestionuh/src/utils/constants.dart'; +import 'package:gestionuh/src/utils/pair.dart'; +import 'package:gestionuh/src/utils/validators.dart'; class RegisterPage extends StatefulWidget { const RegisterPage({Key? key}) : super(key: key); @@ -95,10 +94,7 @@ class _RegisterPageState extends State { body: BlocConsumer( listener: (context, state) async { if (state is RegisterUserFailure) { - _showCenterFlash( - message: state.error, - borderColor: Colors.red, - ); + FlashHelper.errorBar(context, message: state.error); } else if (state is LoadInitialDataFailure) { Future.delayed( const Duration(seconds: 2), @@ -107,16 +103,8 @@ class _RegisterPageState extends State { .add(QuestionsRequestedRegister()), ); } else if (state is RegisterUserSuccess) { - _showCenterFlash( - message: - 'Operación Completada. Su correo electrónico es ${state.userEmail}.', - borderColor: Colors.green, - ); - Future.delayed( - const Duration(seconds: 4), - () => Navigator.of(context) - ..popUntil((_) => Navigator.of(context).canPop()) - ..pushNamed(LOGIN_ROUTE_NAME)); + FlashHelper.infoBar(context, + message: 'El usuario fue registrado correctamente.'); } }, builder: (context, state) { @@ -127,129 +115,164 @@ class _RegisterPageState extends State { .toList(); questionsTaken = List.filled(questions.length, -1); } - return SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.only( - top: 30, bottom: 9, left: 18, right: 18), - child: Form( - key: _formKey, - autovalidateMode: AutovalidateMode.disabled, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Todos los campos son obligatorios.*', - style: Theme.of(context) - .textTheme - .headline6! - .copyWith(fontSize: 14, color: Colors.black45), - textAlign: TextAlign.center, - ), - const SizedBox( - height: 30, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Número de Carnet De Identidad', - style: headlineTextsTheme, - ), - GestionUhDefaultTextField( - hintText: '###########', - autovalidateMode: AutovalidateMode.disabled, - controller: ciController, - validator: identityNumberCIValidator, - keyboardType: TextInputType.number, - ), - ]), - const SizedBox( - height: 15, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Contraseña', - style: headlineTextsTheme, - ), - GestionUhDefaultTextField( - hintText: '********', - autovalidateMode: AutovalidateMode.disabled, - controller: passwordFirstController, - validator: safetyPasswordValidator, - keyboardType: TextInputType.visiblePassword, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - bottomLeft: Radius.circular(5), + if (state is RegisterUserFailure || + state is LoadInitialDataSuccess || + state is LoadInitialDataInProgress || + state is LoadInitialDataFailure) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.only( + top: 30, bottom: 9, left: 18, right: 18), + child: Form( + key: _formKey, + autovalidateMode: AutovalidateMode.disabled, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Todos los campos son obligatorios.*', + style: Theme.of(context) + .textTheme + .headline6! + .copyWith(fontSize: 14, color: Colors.black45), + textAlign: TextAlign.center, + ), + const SizedBox( + height: 30, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Número de Carnet De Identidad', + style: headlineTextsTheme, ), - ), - ]), - const SizedBox( - height: 20, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, + GestionUhDefaultTextField( + hintText: '###########', + autovalidateMode: AutovalidateMode.disabled, + controller: ciController, + validator: identityNumberCIValidator, + keyboardType: TextInputType.number, + ), + ]), + const SizedBox( + height: 15, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Contraseña', + style: headlineTextsTheme, + ), + GestionUhDefaultTextField( + hintText: '********', + autovalidateMode: AutovalidateMode.disabled, + controller: passwordFirstController, + validator: safetyPasswordValidator, + keyboardType: TextInputType.visiblePassword, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(5), + bottomLeft: Radius.circular(5), + ), + ), + ]), + const SizedBox( + height: 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Repetir Contraseña', + style: headlineTextsTheme, + ), + GestionUhDefaultTextField( + hintText: '********', + autovalidateMode: AutovalidateMode.disabled, + controller: passwordSecondController, + validator: safetyPasswordValidator, + keyboardType: TextInputType.visiblePassword, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(5), + bottomLeft: Radius.circular(5), + ), + ), + ]), + const SizedBox( + height: 20, + ), + Text( + 'Introduzca respuesta para las preguntas de seguridad de su preferencia.', + style: Theme.of(context) + .textTheme + .headline6! + .copyWith(fontSize: 14, color: Colors.black45), + textAlign: TextAlign.center, + ), + Builder( + builder: (BuildContext context) { + final childrenQuest = []; + const length = NUMBER_OF_SECURITY_QUESTIONS_NEEDED; + for (int i = 0; i < length; i++) { + childrenQuest.add(buildQuestionZone(i)); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: childrenQuest, + ); + }, + ), + const SizedBox(height: 10), + GestionUhDefaultButton( + text: 'Finalizar', + onPressed: _onRegisterAction, + ), + const SizedBox(height: 30), + ], + ), + ), + ), + ); + } else if (state is RegisterUserSuccess) { + return Container( + margin: const EdgeInsets.all(30), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: Theme.of(context).textTheme.subtitle1, children: [ - Text( - 'Repetir Contraseña', - style: headlineTextsTheme, + const TextSpan( + text: 'Se ha registrado correctamente ' + 'su correo es ', ), - GestionUhDefaultTextField( - hintText: '********', - autovalidateMode: AutovalidateMode.disabled, - controller: passwordSecondController, - validator: safetyPasswordValidator, - keyboardType: TextInputType.visiblePassword, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(5), - bottomLeft: Radius.circular(5), - ), + TextSpan( + text: '"${state.userEmail}"', + style: Theme.of(context) + .textTheme + .subtitle1 + ?.copyWith(color: Colors.red), + ), + const TextSpan( + text: ', anótelo de ser necesario ' + 'no se mostrará otra vez', ), ]), - const SizedBox( - height: 20, - ), - Text( - 'Introduzca respuesta para las preguntas de seguridad de su preferencia.', - style: Theme.of(context) - .textTheme - .headline6! - .copyWith(fontSize: 14, color: Colors.black45), - textAlign: TextAlign.center, - ), - Builder( - builder: (BuildContext context) { - final childrenQuest = []; - const length = NUMBER_OF_SECURITY_QUESTIONS_NEEDED; - for (int i = 0; i < length; i++) { - childrenQuest.add(buildQuestionZone(i)); - } - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: childrenQuest, - ); - }, - ), - const SizedBox(height: 10), - GestionUhDefaultButton( - text: 'Finalizar', - onPressed: _onRegisterAction, - ), - const SizedBox(height: 30), - ], - ), + ), + const SizedBox(height: 30), + GestionUhDefaultButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Ok'), + ), + ], ), - ), - ); - // return Center( - // child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // GestionUhLoadingIndicator(), - // ], - // ), - // ); + ); + } else { + return Container(); + } }, ), ); @@ -313,39 +336,4 @@ class _RegisterPageState extends State { ), ); } - - void _showCenterFlash({ - required String message, - FlashPosition position = FlashPosition.top, - FlashStyle style = FlashStyle.floating, - Alignment? alignment, - Color? borderColor, - }) { - showFlash( - context: context, - duration: const Duration(seconds: 5), - builder: (_, controller) { - return Flash( - controller: controller, - backgroundColor: Colors.black87, - borderRadius: BorderRadius.circular(8.0), - borderColor: borderColor ?? Colors.black, - position: position, - style: style, - alignment: alignment, - enableDrag: false, - onTap: () => controller.dismiss(), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: DefaultTextStyle( - style: const TextStyle(color: Colors.white), - child: Text( - message, - ), - ), - ), - ); - }, - ); - } } diff --git a/lib/src/presentation/pages/reset_password_page.dart b/lib/src/presentation/pages/reset_password_page.dart index 4bf9f06..26067f1 100644 --- a/lib/src/presentation/pages/reset_password_page.dart +++ b/lib/src/presentation/pages/reset_password_page.dart @@ -1,13 +1,12 @@ import 'dart:developer'; -import 'package:flash/flash.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../utils/validators.dart'; -import '../blocs/reset_password_bloc/resetpassword_bloc.dart'; -import '../widgets.dart'; -import '../widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/blocs/reset_password_bloc/resetpassword_bloc.dart'; +import 'package:gestionuh/src/presentation/widgets.dart'; +import 'package:gestionuh/src/presentation/widgets/bottom_sheet.dart'; +import 'package:gestionuh/src/presentation/widgets/flash_helper.dart'; +import 'package:gestionuh/src/utils/validators.dart'; class ResetPasswordPage extends StatefulWidget { const ResetPasswordPage({Key? key}) : super(key: key); @@ -39,16 +38,11 @@ class _ResetPasswordPageState extends State { body: BlocConsumer( listener: (context, state) { if (state is ResetPasswordInitial) { - _showCenterFlash( - message: state.error, - borderColor: Colors.red, - ); + FlashHelper.errorBar(context, message: state.error); } if (state is ResetPasswordSuccess) { - _showCenterFlash( - message: 'Operación Completada.', - borderColor: Colors.green, - ); + FlashHelper.infoBar(context, + message: 'La contraseña ha sido actualizada correctamente.'); } }, builder: (context, state) { @@ -165,39 +159,4 @@ class _ResetPasswordPageState extends State { ); } } - - void _showCenterFlash({ - required String message, - FlashPosition position = FlashPosition.top, - FlashStyle style = FlashStyle.floating, - Alignment? alignment, - Color? borderColor, - }) { - showFlash( - context: context, - duration: const Duration(seconds: 5), - builder: (_, controller) { - return Flash( - controller: controller, - backgroundColor: Colors.black87, - borderRadius: BorderRadius.circular(8.0), - borderColor: borderColor ?? Colors.black, - position: position, - style: style, - alignment: alignment, - enableDrag: false, - onTap: () => controller.dismiss(), - child: Padding( - padding: const EdgeInsets.all(12.0), - child: DefaultTextStyle( - style: const TextStyle(color: Colors.white), - child: Text( - message, - ), - ), - ), - ); - }, - ); - } } diff --git a/lib/src/presentation/theme/theme_data.dart b/lib/src/presentation/theme/theme_data.dart index 83250f8..ed914d1 100644 --- a/lib/src/presentation/theme/theme_data.dart +++ b/lib/src/presentation/theme/theme_data.dart @@ -10,10 +10,10 @@ const titlesAndParagraphsColor = Color(0xff5d5d5d); const subtitlesColor = Color(0xffababab); const linesColor = Color(0xffdfe1e1); //per background -const backgroundColor = Color(0xfdfdfdfd); +const backgroundColor = Color(0xfffdfdfd); const backgroundAccentColor = Color(0xffffffff); const backgroundCategoriesColor = Color(0xffcccfce); -const backgroundSecundaryColor = Color(0xfdfdfdfd); +const backgroundSecundaryColor = Color(0xfffdfdfd); final ThemeData light = ThemeData.light(); diff --git a/lib/src/presentation/widgets/bottom_sheet.dart b/lib/src/presentation/widgets/bottom_sheet.dart index 0eb82bb..db9a523 100644 --- a/lib/src/presentation/widgets/bottom_sheet.dart +++ b/lib/src/presentation/widgets/bottom_sheet.dart @@ -8,22 +8,26 @@ class GestionUHBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { - return Center( - heightFactor: 2.2, - child: RichText( - text: TextSpan( - style: Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 11), - children: [ - const TextSpan(text: '\u00a9 2021'), - TextSpan( - text: Constants.copyRight, - style: Theme.of(context).textTheme.bodyText1!.copyWith( - fontSize: 11, - color: Theme.of(context).primaryColor, - )) - ], + return SafeArea( + minimum: const EdgeInsets.only(bottom: 3), + child: Center( + heightFactor: 2.2, + child: RichText( + text: TextSpan( + style: + Theme.of(context).textTheme.bodyText1!.copyWith(fontSize: 11), + children: [ + const TextSpan(text: '\u00a9 2021'), + TextSpan( + text: Constants.copyRight, + style: Theme.of(context).textTheme.bodyText1!.copyWith( + fontSize: 11, + color: Theme.of(context).primaryColor, + )) + ], + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, ), ); } diff --git a/lib/src/presentation/widgets/flash_helper.dart b/lib/src/presentation/widgets/flash_helper.dart new file mode 100644 index 0000000..62c87c2 --- /dev/null +++ b/lib/src/presentation/widgets/flash_helper.dart @@ -0,0 +1,404 @@ +import 'dart:async'; +import 'dart:collection'; + +import 'package:flash/flash.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +class _MessageItem { + final String message; + Completer> completer; + + _MessageItem(this.message) : completer = Completer>(); +} + +class FlashHelper { + static Completer _buildCompleter = Completer(); + static final Queue<_MessageItem> _messageQueue = Queue<_MessageItem>(); + static Completer? _previousCompleter; + + static void init(BuildContext context) { + if (_buildCompleter.isCompleted == false) { + _buildCompleter.complete(context); + } + } + + static void dispose() { + _messageQueue.clear(); + + if (_buildCompleter.isCompleted == false) { + _buildCompleter.completeError('NotInitialize'); + } + _buildCompleter = Completer(); + } + + static Future toast(String message) async { + final context = await _buildCompleter.future; + + // Wait previous toast dismissed. + if (_previousCompleter?.isCompleted == false) { + final item = _MessageItem(message); + _messageQueue.add(item); + return await item.completer.future; + } + + _previousCompleter = Completer(); + + Future showToast(String message) { + return showFlash( + context: context, + builder: (context, controller) { + return SafeArea( + child: Flash.dialog( + controller: controller, + alignment: const Alignment(0, 0.5), + margin: + const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + enableDrag: false, + backgroundColor: Colors.black87, + child: DefaultTextStyle( + style: const TextStyle(fontSize: 16.0, color: Colors.white), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 8.0), + child: Text(message), + ), + ), + ), + ); + }, + duration: const Duration(seconds: 3), + ).whenComplete(() { + if (_messageQueue.isNotEmpty) { + final item = _messageQueue.removeFirst(); + item.completer.complete(showToast(item.message)); + } else { + _previousCompleter?.complete(); + } + }); + } + + return showToast(message); + } + + static Color _backgroundColor(BuildContext context) { + final theme = Theme.of(context); + return theme.dialogTheme.backgroundColor ?? theme.dialogBackgroundColor; + } + + static TextStyle _titleStyle(BuildContext context, [Color? color]) { + final theme = Theme.of(context); + return (theme.dialogTheme.titleTextStyle ?? theme.textTheme.headline6) + ?.copyWith(color: color) ?? + const TextStyle(fontSize: 21.0, fontWeight: FontWeight.w700); + } + + static TextStyle _contentStyle(BuildContext context, [Color? color]) { + final theme = Theme.of(context); + return (theme.dialogTheme.contentTextStyle ?? theme.textTheme.bodyText2) + ?.copyWith(color: color) ?? + const TextStyle(fontSize: 15.0, fontWeight: FontWeight.w400); + } + + static Future infoBar( + BuildContext context, { + String? title, + required String message, + Duration duration = const Duration(seconds: 3), + }) { + return showFlash( + context: context, + duration: duration, + builder: (context, controller) { + return SafeArea( + child: Flash( + controller: controller, + horizontalDismissDirection: HorizontalDismissDirection.horizontal, + backgroundColor: Colors.black87, + child: FlashBar( + title: title == null + ? null + : Text(title, style: _titleStyle(context, Colors.white)), + message: + Text(message, style: _contentStyle(context, Colors.white)), + icon: Icon(Icons.info_outline, color: Colors.green[300]), + leftBarIndicatorColor: Colors.green[300], + ), + ), + ); + }, + ); + } + + static Future successBar( + BuildContext context, { + String? title, + required String message, + Duration duration = const Duration(seconds: 3), + }) { + return showFlash( + context: context, + duration: duration, + builder: (context, controller) { + return SafeArea( + child: Flash( + controller: controller, + horizontalDismissDirection: HorizontalDismissDirection.horizontal, + backgroundColor: Colors.black87, + child: FlashBar( + title: title == null + ? null + : Text(title, style: _titleStyle(context, Colors.white)), + message: + Text(message, style: _contentStyle(context, Colors.white)), + icon: Icon(Icons.check_circle, color: Colors.blue[300]), + leftBarIndicatorColor: Colors.blue[300], + ), + ), + ); + }, + ); + } + + static Future errorBar( + BuildContext context, { + String? title, + required String message, + ChildBuilder? primaryAction, + Duration duration = const Duration(seconds: 3), + }) { + return showFlash( + context: context, + duration: duration, + builder: (context, controller) { + return StatefulBuilder(builder: (context, setState) { + return SafeArea( + child: Flash( + controller: controller, + horizontalDismissDirection: HorizontalDismissDirection.horizontal, + backgroundColor: Colors.black87, + child: FlashBar( + title: title == null + ? null + : Text(title, style: _titleStyle(context, Colors.white)), + message: + Text(message, style: _contentStyle(context, Colors.white)), + primaryAction: + primaryAction?.call(context, controller, setState), + icon: Icon(Icons.warning, color: Colors.red[300]), + leftBarIndicatorColor: Colors.red[300], + ), + ), + ); + }); + }, + ); + } + + static Future actionBar( + BuildContext context, { + String? title, + required String message, + required ChildBuilder primaryAction, + Duration duration = const Duration(seconds: 3), + }) { + return showFlash( + context: context, + duration: duration, + builder: (context, controller) { + return StatefulBuilder(builder: (context, setState) { + return SafeArea( + child: Flash( + controller: controller, + horizontalDismissDirection: HorizontalDismissDirection.horizontal, + backgroundColor: Colors.black87, + child: FlashBar( + title: title == null + ? null + : Text(title, style: _titleStyle(context, Colors.white)), + message: + Text(message, style: _contentStyle(context, Colors.white)), + primaryAction: + primaryAction.call(context, controller, setState), + ), + ), + ); + }); + }, + ); + } + + static Future simpleDialog( + BuildContext context, { + String? title, + required String message, + Color? messageColor, + ChildBuilder? negativeAction, + ChildBuilder? positiveAction, + }) { + return showFlash( + context: context, + persistent: false, + onWillPop: () async => false, + builder: (_context, controller) { + return StatefulBuilder( + builder: (context, setState) { + return SafeArea( + child: Flash.dialog( + boxShadows: kElevationToShadow[6], + barrierBlur: 1, + controller: controller, + backgroundColor: _backgroundColor(_context), + margin: const EdgeInsets.only(left: 40.0, right: 40.0), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + child: FlashBar( + title: title == null + ? null + : Text(title, style: _titleStyle(_context)), + message: Text(message, + style: _contentStyle(_context, messageColor)), + actions: [ + if (negativeAction != null) + negativeAction(_context, controller, setState), + if (positiveAction != null) + positiveAction(_context, controller, setState), + ], + ), + ), + ); + }, + ); + }, + ); + } + + static Future customDialog( + BuildContext context, { + ChildBuilder? titleBuilder, + required ChildBuilder messageBuilder, + ChildBuilder? negativeAction, + ChildBuilder? positiveAction, + }) { + return showFlash( + context: context, + persistent: false, + builder: (context, controller) { + return StatefulBuilder( + builder: (context, setState) { + return SafeArea( + child: Flash.dialog( + controller: controller, + backgroundColor: _backgroundColor(context), + margin: const EdgeInsets.only(left: 40.0, right: 40.0), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + child: FlashBar( + title: titleBuilder == null + ? null + : DefaultTextStyle( + style: _titleStyle(context), + child: + titleBuilder.call(context, controller, setState), + ), + message: DefaultTextStyle( + style: _contentStyle(context), + child: messageBuilder.call(context, controller, setState), + ), + actions: [ + if (negativeAction != null) + negativeAction(context, controller, setState), + if (positiveAction != null) + positiveAction(context, controller, setState), + ], + ), + ), + ); + }, + ); + }, + ); + } + + static Future blockDialog( + BuildContext context, { + required Completer dismissCompleter, + }) { + final controller = FlashController( + context, + (context, FlashController controller) { + return SafeArea( + child: Flash.dialog( + controller: controller, + barrierDismissible: false, + backgroundColor: Colors.black87, + margin: const EdgeInsets.only(left: 40.0, right: 40.0), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + child: const Padding( + padding: EdgeInsets.all(16.0), + child: CircularProgressIndicator(strokeWidth: 2.0), + ), + ), + ); + }, + persistent: false, + onWillPop: () => Future.value(false), + ); + dismissCompleter.future.then((value) => controller.dismiss(value)); + return controller.show(); + } + + static Future inputDialog( + BuildContext context, { + String? title, + String? message, + String? defaultValue, + bool persistent = true, + WillPopCallback? onWillPop, + }) { + final editingController = TextEditingController(text: defaultValue); + return showFlash( + context: context, + persistent: persistent, + onWillPop: onWillPop, + builder: (context, controller) { + final theme = Theme.of(context); + return SafeArea( + child: Flash.bar( + controller: controller, + barrierColor: Colors.black54, + borderWidth: 3, + borderRadius: + const BorderRadius.vertical(top: Radius.circular(8.0)), + child: FlashBar( + title: title == null + ? null + : Text(title, style: const TextStyle(fontSize: 24.0)), + message: Column( + children: [ + if (message != null) Text(message), + Form( + child: TextFormField( + controller: editingController, + autofocus: true, + ), + ), + ], + ), + leftBarIndicatorColor: theme.primaryColor, + primaryAction: IconButton( + onPressed: () { + final message = editingController.text; + controller.dismiss(message); + }, + icon: Icon(Icons.send, color: theme.colorScheme.secondary), + ), + ), + ), + ); + }, + ); + } +} + +typedef ChildBuilder = Widget Function( + BuildContext context, FlashController controller, StateSetter setState); diff --git a/lib/src/utils/constants.dart b/lib/src/utils/constants.dart index 77e64ea..eaec5ff 100644 --- a/lib/src/utils/constants.dart +++ b/lib/src/utils/constants.dart @@ -1,5 +1,6 @@ export 'constants/about.dart'; export 'constants/constants.dart'; +export 'constants/errors.dart'; export 'constants/misc.dart'; export 'constants/routes.dart'; export 'constants/sample_data.dart'; diff --git a/lib/src/utils/constants/errors.dart b/lib/src/utils/constants/errors.dart new file mode 100644 index 0000000..0049080 --- /dev/null +++ b/lib/src/utils/constants/errors.dart @@ -0,0 +1,14 @@ +class Errors { + static const Messages = {1: 'Datos inválidos.', 2: 'Credenciales inválidas.'}; + + static const DefaultError = 'Ha ocurrido un error.'; + + static String? retrieveError(String message) { + try { + final index = int.parse(message); + return Messages[index]; + } catch (_) {} + + return DefaultError; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index d74cadb..17a2a7d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,7 @@ flutter: flutter_native_splash: image: assets/images/splash2.png - color: '#5b0101' + color: '#fdfdfd' fill: false flutter_icons: