diff --git a/lib/config/theme_data.dart b/lib/config/theme_data.dart index b1a3388..632cf90 100644 --- a/lib/config/theme_data.dart +++ b/lib/config/theme_data.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; class LightThemeConfig { - static const Color _primary = Color(0XFF007EA7); - static const Color _white = Color(0XFFFFFFFF); - static const Color _black = Color(0XFF292929); - static const Color _background = Color(0XFFF8F8FA); + static const Color _primary = Color(0xFF007EA7); + static const Color _white = Color(0xFFFFFFFF); + static const Color _black = Color(0xFF292929); + static const Color _background = Color(0xFFF8F8FA); static final ThemeData themeData = ThemeData( backgroundColor: _background, @@ -28,11 +28,11 @@ class LightThemeConfig { static const ColorScheme _colorScheme = ColorScheme( primary: _primary, primaryVariant: _white, - secondary: Color(0XFFD61515), + secondary: Color(0xFFD61515), secondaryVariant: _white, surface: _white, background: _background, - error: Color(0XFFF00505), + error: Color(0xFFF00505), onPrimary: _white, onSecondary: _white, onSurface: _black, @@ -72,9 +72,9 @@ class LightThemeConfig { } class DarkThemeConfig { - static const Color _primary = Color(0XFF007EA7); - static const Color _white = Color(0XFFFFFFFF); - static const Color _background = Color(0XFF202224); + static const Color _primary = Color(0xFF007EA7); + static const Color _white = Color(0xFFFFFFFF); + static const Color _background = Color(0xFF202224); static final ThemeData themeData = ThemeData( backgroundColor: _background, @@ -101,11 +101,11 @@ class DarkThemeConfig { static const ColorScheme _colorScheme = ColorScheme( primary: _primary, primaryVariant: _white, - secondary: Color(0XFFE53939), + secondary: Color(0xFFE53939), secondaryVariant: _white, - surface: Color(0XFF181B1F), + surface: Color(0xFF181B1F), background: _background, - error: Color(0XFFF53B3B), + error: Color(0xFFF53B3B), onPrimary: _white, onSecondary: _white, onSurface: text, @@ -144,78 +144,81 @@ class DarkThemeConfig { ); // Grey colors - static const Color white = Color(0XFFFFFFFF); - static const Color title = Color(0XFFF5F5F5); - static const Color text = Color(0XFFD6D6D6); - static const Color hint = Color(0XFFBBBBBB); - static const Color border = Color(0XFF666666); + static const Color white = Color(0xFFFFFFFF); + static const Color title = Color(0xFFF5F5F5); + static const Color text = Color(0xFFD6D6D6); + static const Color hint = Color(0xFFBBBBBB); + static const Color border = Color(0xFF666666); // Error and success - static const Color errorLight = Color(0XFFF0C9CD); - static const Color error = Color(0XFFF53B3B); - static const Color successLight = Color(0XFFBBD6B4); - static const Color success = Color(0XFF32A64C); + static const Color errorLight = Color(0xFFF0C9CD); + static const Color error = Color(0xFFF53B3B); + static const Color successLight = Color(0xFFBBD6B4); + static const Color success = Color(0xFF32A64C); } extension DidvanColorScheme on ColorScheme { // Secondary colors Color get secondaryDisabled => brightness == Brightness.dark - ? const Color(0XFF703838) - : const Color(0XFFFFC8C8); + ? const Color(0xFF703838) + : const Color(0xFFFFC8C8); - Color get white => const Color(0XFFFFFFFF); + Color get white => const Color(0xFFFFFFFF); Color get focused => brightness == Brightness.dark - ? const Color(0XFF323C47) - : const Color(0XFFE6F3FA); + ? const Color(0xFF323C47) + : const Color(0xFFE6F3FA); Color get navigation => brightness == Brightness.dark - ? const Color(0XFF181B1F) - : const Color(0XFF012348); + ? const Color(0xFF181B1F) + : const Color(0xFF012348); Color get focusedBorder => brightness == Brightness.dark - ? const Color(0XFFC8E0F4) - : const Color(0XFF195D80); + ? const Color(0xFFC8E0F4) + : const Color(0xFF195D80); Color get title => brightness == Brightness.dark - ? const Color(0XFFD6D6D6) - : const Color(0XFF1B3C59); + ? const Color(0xFFD6D6D6) + : const Color(0xFF1B3C59); Color get text => brightness == Brightness.dark - ? const Color(0XFFD6D6D6) - : const Color(0XFF292929); + ? const Color(0xFFD6D6D6) + : const Color(0xFF292929); Color get inputText => brightness == Brightness.dark - ? const Color(0XFFA3A3A3) - : const Color(0XFF3D3D3D); + ? const Color(0xFFA3A3A3) + : const Color(0xFF3D3D3D); Color get caption => brightness == Brightness.dark - ? const Color(0XFFBBBBBB) - : const Color(0XFF666666); - Color get hint => const Color(0XFFBBBBBB); + ? const Color(0xFFBBBBBB) + : const Color(0xFF666666); + Color get hint => const Color(0xFFBBBBBB); Color get disabledText => brightness == Brightness.dark - ? const Color(0XFF666666) - : const Color(0XFFE0E0E0); + ? const Color(0xFF666666) + : const Color(0xFFE0E0E0); Color get border => brightness == Brightness.dark - ? const Color(0XFF666666) - : const Color(0XFFE0E0E0); + ? const Color(0xFF666666) + : const Color(0xFFE0E0E0); Color get cardBorder => brightness == Brightness.dark - ? const Color(0XFF666666) - : const Color(0XFFEBEBEB); + ? const Color(0xFF666666) + : const Color(0xFFEBEBEB); Color get disabledBackground => brightness == Brightness.dark - ? const Color(0XFF1F1F1F) - : const Color(0XFFE0E0E0); + ? const Color(0xFF1F1F1F) + : const Color(0xFFE0E0E0); Color get secondCTA => brightness == Brightness.dark - ? const Color(0XFF474747) - : const Color(0XFFF5F5F5); + ? const Color(0xFF474747) + : const Color(0xFFF5F5F5); Color get splash => brightness == Brightness.dark - ? const Color(0XFF333333) - : const Color(0XFFC8E0F4); + ? const Color(0xFF333333) + : const Color(0xFFC8E0F4); Color get black => brightness == Brightness.dark - ? const Color(0XFF1F1F1F) - : const Color(0XFF292929); + ? const Color(0xFF1F1F1F) + : const Color(0xFF292929); + Color get overlay => brightness == Brightness.dark + ? const Color(0xFF0F1011) + : const Color(0xFF292929); // Error and success colors Color get errorLight => brightness == Brightness.dark - ? const Color(0XFFF0C9CD) - : const Color(0XFFFFF8F8); + ? const Color(0xFFF0C9CD) + : const Color(0xFFFFF8F8); Color get successLight => brightness == Brightness.dark - ? const Color(0XFFBBD6B4) - : const Color(0XFFF5FFFC); + ? const Color(0xFFBBD6B4) + : const Color(0xFFF5FFFC); Color get success => brightness == Brightness.dark - ? const Color(0XFF32A64C) - : const Color(0XFF2BB24A); + ? const Color(0xFF32A64C) + : const Color(0xFF2BB24A); } diff --git a/lib/models/enums.dart b/lib/models/enums.dart index 247826c..1859789 100644 --- a/lib/models/enums.dart +++ b/lib/models/enums.dart @@ -1,6 +1,7 @@ enum AppState { idle, busy, + isolatedBusy, failed, } diff --git a/lib/models/view/alert_data.dart b/lib/models/view/alert_data.dart new file mode 100644 index 0000000..88d5571 --- /dev/null +++ b/lib/models/view/alert_data.dart @@ -0,0 +1,5 @@ +class AlertData { + final String message; + + AlertData({required this.message}); +} diff --git a/lib/pages/authentication/authentication.dart b/lib/pages/authentication/authentication.dart index 0f8209e..6c98ca8 100644 --- a/lib/pages/authentication/authentication.dart +++ b/lib/pages/authentication/authentication.dart @@ -1,7 +1,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/pages/authentication/authentication_state.dart'; import 'package:didvan/pages/authentication/screens/password.dart'; -import 'package:didvan/pages/authentication/screens/phone_number.dart'; +import 'package:didvan/pages/authentication/screens/username.dart'; import 'package:didvan/pages/authentication/screens/reset_password.dart'; import 'package:didvan/pages/authentication/screens/verification.dart'; import 'package:flutter/material.dart'; @@ -16,7 +16,7 @@ class Authentication extends StatefulWidget { class _AuthenticationState extends State { final List _pages = const [ - PhoneNumberInput(), + UsernameInput(), PasswordInput(), Verification(), ResetPassword(), @@ -26,9 +26,18 @@ class _AuthenticationState extends State { Widget build(BuildContext context) { return Scaffold( body: Consumer( - builder: (context, state, child) => AnimatedSwitcher( - duration: DesignConfig.mediumAnimationDuration, - child: _pages[state.currentPageIndex], + builder: (context, state, child) => WillPopScope( + onWillPop: () async { + if (state.currentPageIndex == 0) { + return true; + } + state.currentPageIndex--; + return false; + }, + child: AnimatedSwitcher( + duration: DesignConfig.mediumAnimationDuration, + child: _pages[state.currentPageIndex], + ), ), ), ); diff --git a/lib/pages/authentication/authentication_state.dart b/lib/pages/authentication/authentication_state.dart index 395288c..d26ecc3 100644 --- a/lib/pages/authentication/authentication_state.dart +++ b/lib/pages/authentication/authentication_state.dart @@ -1,8 +1,13 @@ +import 'package:didvan/models/enums.dart'; +import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/network/request.dart'; +import 'package:didvan/services/network/request_helper.dart'; +import 'package:didvan/utils/actions_sheet.dart'; class AuthenticationState extends CoreProvier { int _currentPageIndex = 0; - String phoneNumber = ''; + String username = ''; String password = ''; String verificationCode = ''; @@ -12,4 +17,38 @@ class AuthenticationState extends CoreProvier { } int get currentPageIndex => _currentPageIndex; + + Future confirmUsername() async { + appState = AppState.isolatedBusy; + final RequestService service = RequestService( + RequestHelper.confirmUsername, + useAutherization: false, + body: {'username': username}, + ); + await service.post(); + if (service.isSuccess && service.result['confirmed']) { + appState = AppState.idle; + currentPageIndex++; + } else { + appState = AppState.failed; + ActionSheetUtils.showAlert(AlertData(message: service.errorMessage)); + } + } + + Future login() async { + appState = AppState.isolatedBusy; + final RequestService service = RequestService( + RequestHelper.login, + useAutherization: false, + body: {'username': username, "password": password}, + ); + await service.post(); + if (service.isSuccess && service.result['loggedIn']) { + appState = AppState.idle; + return service.result['token']; + } else { + appState = AppState.failed; + ActionSheetUtils.showAlert(AlertData(message: service.errorMessage)); + } + } } diff --git a/lib/pages/authentication/screens/password.dart b/lib/pages/authentication/screens/password.dart index dd11163..bdd0953 100644 --- a/lib/pages/authentication/screens/password.dart +++ b/lib/pages/authentication/screens/password.dart @@ -1,26 +1,43 @@ import 'package:didvan/pages/authentication/authentication_state.dart'; import 'package:didvan/pages/authentication/widgets/authentication_layout.dart'; +import 'package:didvan/providers/user_provider.dart'; +import 'package:didvan/routes/routes.dart'; +import 'package:didvan/services/network/request.dart'; import 'package:didvan/widgets/didvan/button.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:didvan/widgets/didvan/text_field.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class PasswordInput extends StatelessWidget { +class PasswordInput extends StatefulWidget { const PasswordInput({Key? key}) : super(key: key); + @override + State createState() => _PasswordInputState(); +} + +class _PasswordInputState extends State { + final _formKey = GlobalKey(); + @override Widget build(BuildContext context) { final AuthenticationState state = context.read(); return AuthenticationLayout( - appBarTitle: 'ورود با شماره موبایل ' + state.phoneNumber, + appBarTitle: + 'ورود با ${state.username.contains('09') ? 'شماره موبایل' : 'نام کاربری'} ${state.username}', children: [ - DidvanTextField( - onChanged: (value) => state.password = value, - autoFocus: true, - title: 'کلمه عبور', - hintText: 'کلمه عبور', - obsecureText: true, + Form( + key: _formKey, + child: DidvanTextField( + onChanged: (value) => state.password = value, + autoFocus: true, + title: 'کلمه عبور', + hintText: 'کلمه عبور', + obsecureText: true, + validator: (value) => value!.length < 8 + ? 'کلمه عبور نمی‌تواند از 8 کاراکتر کمتر باشد' + : null, + ), ), const SizedBox( height: 32, @@ -35,7 +52,7 @@ class PasswordInput extends StatelessWidget { ), const Spacer(), DidvanButton( - onPressed: () {}, + onPressed: () => _onPressed(context), title: 'ورود', ), const SizedBox( @@ -44,4 +61,18 @@ class PasswordInput extends StatelessWidget { ], ); } + + Future _onPressed(BuildContext context) async { + if (!_formKey.currentState!.validate()) { + return; + } + final state = context.read(); + final token = await state.login(); + if (token != null) { + final userProvider = context.read(); + await userProvider.setAndGetToken(token: token); + RequestService.token = token; + Navigator.of(context).pushReplacementNamed(Routes.home); + } + } } diff --git a/lib/pages/authentication/screens/phone_number.dart b/lib/pages/authentication/screens/username.dart similarity index 81% rename from lib/pages/authentication/screens/phone_number.dart rename to lib/pages/authentication/screens/username.dart index e108de7..3c7b8e6 100644 --- a/lib/pages/authentication/screens/phone_number.dart +++ b/lib/pages/authentication/screens/username.dart @@ -6,8 +6,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class PhoneNumberInput extends StatelessWidget { - const PhoneNumberInput({ +class UsernameInput extends StatelessWidget { + const UsernameInput({ Key? key, }) : super(key: key); @@ -17,12 +17,15 @@ class PhoneNumberInput extends StatelessWidget { return AuthenticationLayout( children: [ DidvanTextField( - title: 'شماره موبایل', - textInputType: TextInputType.phone, - hintText: 'شماره موبایل', + initialValue: state.username, + title: 'نام کاربری یا شماره موبایل', + hintText: 'نام کاربری یا شماره موبایل', textAlign: TextAlign.center, + validator: (value) => value!.length < 4 + ? 'نام کاربری نمی‌تواند از 4 کاراکتر کوچکتر باشد' + : null, onChanged: (value) { - state.phoneNumber = value; + state.username = value; }, ), const SizedBox( @@ -30,9 +33,7 @@ class PhoneNumberInput extends StatelessWidget { ), DidvanButton( title: 'ورود', - onPressed: () { - state.currentPageIndex = 1; - }, + onPressed: state.confirmUsername, ), const Spacer(), Padding( diff --git a/lib/pages/authentication/screens/verification.dart b/lib/pages/authentication/screens/verification.dart index d0d3bde..3d48f4d 100644 --- a/lib/pages/authentication/screens/verification.dart +++ b/lib/pages/authentication/screens/verification.dart @@ -26,7 +26,7 @@ class Verification extends StatelessWidget { height: 8, ), DidvanText( - state.phoneNumber, + state.username, style: Theme.of(context).textTheme.subtitle1, ), const SizedBox( @@ -65,7 +65,7 @@ class Verification extends StatelessWidget { onPressed: () { state.currentPageIndex++; }, - title: 'تایید', + title: 'ارسال مجدد کد', ), const SizedBox( height: 48, diff --git a/lib/pages/authentication/widgets/authentication_layout.dart b/lib/pages/authentication/widgets/authentication_layout.dart index 2b178e5..f15e7fe 100644 --- a/lib/pages/authentication/widgets/authentication_layout.dart +++ b/lib/pages/authentication/widgets/authentication_layout.dart @@ -14,7 +14,6 @@ class AuthenticationLayout extends StatelessWidget { Widget build(BuildContext context) { return SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20), - physics: const NeverScrollableScrollPhysics(), child: SizedBox( height: MediaQuery.of(context).size.height, child: Column( @@ -34,7 +33,9 @@ class AuthenticationLayout extends StatelessWidget { right: 100, bottom: 40, ), - child: DidvanVerticalLogo(), + child: DidvanVerticalLogo( + height: 200, + ), ), ...children, ], diff --git a/lib/pages/splash/splash.dart b/lib/pages/splash/splash.dart index b2c9677..7ab9d3d 100644 --- a/lib/pages/splash/splash.dart +++ b/lib/pages/splash/splash.dart @@ -1,6 +1,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/main.dart'; import 'package:didvan/providers/theme_provider.dart'; +import 'package:didvan/providers/user_provider.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/utils/actions_sheet.dart'; @@ -36,8 +37,11 @@ class _SplashState extends State { _isGettingThemeData = false; }), ); + final String? token = await context.read().setAndGetToken(); await Future.delayed(const Duration(seconds: 2)); - Navigator.of(context).pushReplacementNamed(Routes.home); + Navigator.of(context).pushReplacementNamed( + token == null ? Routes.authenticaion : Routes.home, + ); } @override diff --git a/lib/providers/core_provider.dart b/lib/providers/core_provider.dart index 45ef78c..5870c8f 100644 --- a/lib/providers/core_provider.dart +++ b/lib/providers/core_provider.dart @@ -1,10 +1,17 @@ import 'package:didvan/models/enums.dart'; +import 'package:didvan/utils/actions_sheet.dart'; import 'package:flutter/cupertino.dart'; class CoreProvier with ChangeNotifier { AppState _appState = AppState.idle; set appState(AppState newState) { + if (newState == AppState.isolatedBusy) { + ActionSheetUtils.showLogoLoadingIndicator(); + } + if (_appState == AppState.isolatedBusy) { + ActionSheetUtils.pop(); + } _appState = newState; notifyListeners(); } diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart index e3efcab..35eeb73 100644 --- a/lib/providers/user_provider.dart +++ b/lib/providers/user_provider.dart @@ -1,3 +1,13 @@ import 'package:didvan/providers/core_provider.dart'; +import 'package:hive/hive.dart'; -class UserProvider extends CoreProvier {} +class UserProvider extends CoreProvier { + Future setAndGetToken({String? token}) async { + final box = await Hive.openBox('autherization'); + if (token != null) { + await box.put('token', token); + } else { + return box.toMap()['token']; + } + } +} diff --git a/lib/services/network/request.dart b/lib/services/network/request.dart index fdc3aab..e358dcd 100644 --- a/lib/services/network/request.dart +++ b/lib/services/network/request.dart @@ -11,7 +11,8 @@ class RequestService { Map get result => _body['result'] ?? const {}; Map get errors => _body['errors'] ?? const {}; - String? errorMessage; + String errorMessage = + 'خطا! لطفا اتصال اینترنت خود را بررسی و مجددا تلاش نمایید.'; dynamic _body; final Map _headers = { @@ -150,7 +151,7 @@ class RequestService { _body = json.decode(response.body); } } - errorMessage = _errorMessageGenerator(); + errorMessage = _errorMessageGenerator(response); } bool _handleError(http.Response? response) { @@ -162,8 +163,8 @@ class RequestService { isSuccess = false; return false; } - if (response.statusCode != 200 && response.statusCode != 204) { - String data; + if (response.statusCode != 200) { + dynamic data; if (response.body.isEmpty) { data = 'No results!'; } else if (response.body.contains('')) { @@ -182,9 +183,15 @@ class RequestService { return true; } - String _errorMessageGenerator() { - return isSuccess - ? result['msg'] - : 'خطا! لطفا اتصال اینترنت خود را بررسی و مجددا تلاش نمایید.'; + String _errorMessageGenerator(http.Response? response) { + String? error; + if (response != null) { + if (!response.body.contains('')) { + if (result.isNotEmpty) { + error = result['msg']; + } + } + } + return error ?? 'خطا! لطفا اتصال اینترنت خود را بررسی و مجددا تلاش نمایید.'; } } diff --git a/lib/utils/actions_sheet.dart b/lib/utils/actions_sheet.dart index 0ba1e37..18fc11c 100644 --- a/lib/utils/actions_sheet.dart +++ b/lib/utils/actions_sheet.dart @@ -1,8 +1,10 @@ +import 'package:another_flushbar/flushbar.dart'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; +import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/widgets/didvan/button.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; @@ -15,13 +17,15 @@ class ActionSheetUtils { static Future showLogoLoadingIndicator() async { await showDialog( context: context, - builder: (context) => _customSystemOverlayStyle( - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width / 3, - ), - child: const RiveAnimation.asset(Assets.logoLoadingAnimation), + builder: (context) => + // _customSystemOverlayStyle( + // child: + Padding( + padding: EdgeInsets.symmetric( + horizontal: MediaQuery.of(context).size.width / 3, ), + child: const RiveAnimation.asset(Assets.logoLoadingAnimation), + // ), ), ); } @@ -29,12 +33,29 @@ class ActionSheetUtils { static AnnotatedRegion _customSystemOverlayStyle({required Widget child}) { return AnnotatedRegion( value: DesignConfig.systemUiOverlayStyle.copyWith( - // systemNavigationBarColor: Colors.black45, - ), + systemNavigationBarColor: DesignConfig + .systemUiOverlayStyle.systemNavigationBarColor! + .withBlue(20), + ), child: child, ); } + static Future showAlert(AlertData alertData) async { + await Flushbar( + margin: const EdgeInsets.symmetric(horizontal: 16), + message: alertData.message, + backgroundColor: Theme.of(context).colorScheme.focused, + borderRadius: DesignConfig.mediumBorderRadius, + messageColor: Theme.of(context).colorScheme.text, + flushbarPosition: FlushbarPosition.TOP, + duration: const Duration(seconds: 2), + boxShadows: [ + BoxShadow(color: Theme.of(context).colorScheme.cardBorder), + ], + ).show(context); + } + static Future showBottomSheet({required ActionSheetData data}) async { await showModalBottomSheet( context: context, @@ -103,4 +124,9 @@ class ActionSheetUtils { ), ); } + + static void pop() { + DesignConfig.updateSystemUiOverlayStyle(); + Navigator.of(context).pop(); + } } diff --git a/lib/widgets/didvan/button.dart b/lib/widgets/didvan/button.dart index dff6d73..7578856 100644 --- a/lib/widgets/didvan/button.dart +++ b/lib/widgets/didvan/button.dart @@ -24,7 +24,10 @@ class DidvanButton extends StatelessWidget { ), height: 48, color: Theme.of(context).colorScheme.primary, - onPressed: onPressed, + onPressed: () { + FocusScope.of(context).unfocus(); + onPressed?.call(); + }, child: _childBuilder(), ), ); diff --git a/lib/widgets/didvan/text_field.dart b/lib/widgets/didvan/text_field.dart index aaed428..7ec263f 100644 --- a/lib/widgets/didvan/text_field.dart +++ b/lib/widgets/didvan/text_field.dart @@ -1,5 +1,7 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/widgets/animated_visibility.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; @@ -36,11 +38,14 @@ class _DidvanTextFieldState extends State { final FocusNode _focusNode = FocusNode(); final TextEditingController _controller = TextEditingController(); - bool _hasError = false; bool _hideContent = false; + String? _error; @override void initState() { + if (widget.initialValue != null) { + _controller.text = widget.initialValue; + } _hideContent = widget.obsecureText; _focusNode.addListener(() { setState(() {}); @@ -57,7 +62,7 @@ class _DidvanTextFieldState extends State { if (widget.title != null) DidvanText( widget.title!, - color: !widget.enabled ? Theme.of(context).colorScheme.hint : null, + color: _titleColor(), ), if (widget.title != null) const SizedBox(height: 8), Container( @@ -85,6 +90,7 @@ class _DidvanTextFieldState extends State { enabled: widget.enabled, border: InputBorder.none, hintText: widget.hintText, + errorStyle: const TextStyle(height: 0), hintStyle: Theme.of(context) .textTheme .bodyText2! @@ -92,6 +98,24 @@ class _DidvanTextFieldState extends State { ), ), ), + const SizedBox(height: 8), + AnimatedVisibility( + isVisible: _error != null, + duration: DesignConfig.lowAnimationDuration, + child: Row( + children: [ + Icon( + DidvanIcons.lightbulb_exclamation_regular, + color: Theme.of(context).colorScheme.error, + size: 14, + ), + DidvanText( + _error ?? '', + style: Theme.of(context).textTheme.caption, + color: Theme.of(context).colorScheme.error, + ), + ], + )) ], ); } @@ -99,12 +123,21 @@ class _DidvanTextFieldState extends State { Color _borderColor() { if (_focusNode.hasFocus) { return Theme.of(context).colorScheme.primary; - } else if (_hasError) { + } else if (_error != null) { return Theme.of(context).colorScheme.error; } return Theme.of(context).colorScheme.border; } + Color? _titleColor() { + if (!widget.enabled) { + return Theme.of(context).colorScheme.hint; + } + if (_error != null) { + return Theme.of(context).colorScheme.error; + } + } + Color _fillColor() { if (!widget.enabled) { return Theme.of(context).colorScheme.secondCTA; @@ -112,7 +145,7 @@ class _DidvanTextFieldState extends State { if (_focusNode.hasFocus) { return Theme.of(context).colorScheme.focused; } - if (_hasError) { + if (_error != null) { return Theme.of(context).colorScheme.errorLight; } return Theme.of(context).colorScheme.surface; @@ -129,7 +162,9 @@ class _DidvanTextFieldState extends State { }); }, child: Icon( - _hideContent ? Icons.remove_red_eye : Icons.remove_red_eye_outlined, + _hideContent + ? DidvanIcons.eye_regular + : DidvanIcons.eye_slash_regular, ), ), ); @@ -143,6 +178,18 @@ class _DidvanTextFieldState extends State { } String? _validator(String? value) { - _hasError = false; + if (widget.validator != null) { + final String? error = widget.validator!(value); + if (error != null) { + setState(() { + _error = error; + }); + return ''; + } else { + setState(() { + _error = null; + }); + } + } } } diff --git a/lib/widgets/logos/didvan_horizontal_logo.dart b/lib/widgets/logos/didvan_horizontal_logo.dart index e9631d6..13437e0 100644 --- a/lib/widgets/logos/didvan_horizontal_logo.dart +++ b/lib/widgets/logos/didvan_horizontal_logo.dart @@ -3,10 +3,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; class DidvanVerticalLogo extends StatelessWidget { - const DidvanVerticalLogo({Key? key}) : super(key: key); + final double? height; + const DidvanVerticalLogo({Key? key, this.height}) : super(key: key); @override Widget build(BuildContext context) { - return SvgPicture.asset(Assets.verticalLogoWithText); + return SvgPicture.asset(Assets.verticalLogoWithText, height: height); } } diff --git a/pubspec.lock b/pubspec.lock index bd7794e..9d580f9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + another_flushbar: + dependency: "direct main" + description: + name: another_flushbar + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.28" async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ac58b10..81a0276 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -57,6 +57,7 @@ dependencies: just_audio: ^0.9.18 record_web: ^0.2.1 just_waveform: ^0.0.1 + another_flushbar: ^1.10.28 dev_dependencies: flutter_test: