From 3615b9242bdc9270ad8f78b4578f7a7bb32e184e Mon Sep 17 00:00:00 2001 From: Tamojit Ghosh Date: Sun, 21 Jan 2024 22:00:16 +0530 Subject: [PATCH] Neumorphic textfield in LoginScreen --- lib/Pages/login_screen/login_screen.dart | 328 +++++++++--------- .../widgets/login_screen_textfield.dart | 69 ++-- pubspec.lock | 54 ++- pubspec.yaml | 1 + 4 files changed, 241 insertions(+), 211 deletions(-) diff --git a/lib/Pages/login_screen/login_screen.dart b/lib/Pages/login_screen/login_screen.dart index 8ecc71b..7da794d 100644 --- a/lib/Pages/login_screen/login_screen.dart +++ b/lib/Pages/login_screen/login_screen.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:animate_do/animate_do.dart'; import 'package:clipboard/clipboard.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -94,87 +95,93 @@ class _LoginScreenState extends State { SizedBox( height: hp * 0.06, ), - LoginScreenTextField( - key: Key('Url TextField'), - controller: urlController, - labelText: l10n.login_screen_url, - prefixIcon: Icons.link, - themeIndex: themeIndex, - trailingIconButton1: Align( - alignment: Alignment.centerRight, - child: IconButton( - onPressed: () { - FlutterClipboard.paste().then((value) { - setState(() { - urlController.text = value; + FadeInLeft( + child: LoginScreenTextField( + key: Key('Url TextField'), + controller: urlController, + labelText: l10n.login_screen_url, + prefixIcon: Icons.link, + themeIndex: themeIndex, + trailingIconButton1: Align( + alignment: Alignment.centerRight, + child: IconButton( + onPressed: () { + FlutterClipboard.paste().then((value) { + setState(() { + urlController.text = value; + }); }); - }); - }, - icon: Icon( - Icons.paste, - color: ThemeBloc.theme(themeIndex) - .textTheme - .bodyLarge! - .color!, - size: 20, + }, + icon: Icon( + Icons.paste, + color: ThemeBloc.theme(themeIndex) + .textTheme + .bodyLarge! + .color!, + size: 20, + ), ), ), - ), - trailingIconButton2: Align( - alignment: Alignment.centerRight, - child: Tooltip( - triggerMode: TooltipTriggerMode.tap, - message: - "URL for your Flood instance (local, seedbox...).", - showDuration: Duration(seconds: 3), - child: Icon( - Icons.info_outline, - color: ThemeBloc.theme(themeIndex) - .textTheme - .bodyLarge! - .color!, - size: 20, + trailingIconButton2: Align( + alignment: Alignment.centerRight, + child: Tooltip( + triggerMode: TooltipTriggerMode.tap, + message: + "URL for your Flood instance (local, seedbox...).", + showDuration: Duration(seconds: 3), + child: Icon( + Icons.info_outline, + color: ThemeBloc.theme(themeIndex) + .textTheme + .bodyLarge! + .color!, + size: 20, + ), ), ), ), ), SizedBox( - height: hp * 0.01, + height: hp * 0.02, ), - LoginScreenTextField( - key: Key('Username TextField'), - controller: usernameController, - labelText: l10n.login_screen_username, - prefixIcon: Icons.person, - themeIndex: themeIndex, + FadeInRight( + child: LoginScreenTextField( + key: Key('Username TextField'), + controller: usernameController, + labelText: l10n.login_screen_username, + prefixIcon: Icons.person, + themeIndex: themeIndex, + ), ), SizedBox( - height: hp * 0.01, + height: hp * 0.02, ), - LoginScreenTextField( - key: Key('Password TextField'), - controller: passwordController, - labelText: l10n.login_screen_password, - prefixIcon: Icons.lock_outline, - themeIndex: themeIndex, - obscureText: showPass, - trailingIconButton1: Align( - alignment: Alignment.centerRight, - child: IconButton( - onPressed: () { - setState(() { - showPass = !showPass; - }); - }, - icon: Icon( - (showPass) - ? Icons.visibility - : Icons.visibility_off, - color: ThemeBloc.theme(themeIndex) - .textTheme - .bodyLarge! - .color!, - size: 20, + FadeInLeft( + child: LoginScreenTextField( + key: Key('Password TextField'), + controller: passwordController, + labelText: l10n.login_screen_password, + prefixIcon: Icons.lock_outline, + themeIndex: themeIndex, + obscureText: showPass, + trailingIconButton1: Align( + alignment: Alignment.centerRight, + child: IconButton( + onPressed: () { + setState(() { + showPass = !showPass; + }); + }, + icon: Icon( + (showPass) + ? Icons.visibility + : Icons.visibility_off, + color: ThemeBloc.theme(themeIndex) + .textTheme + .bodyLarge! + .color!, + size: 20, + ), ), ), ), @@ -182,101 +189,104 @@ class _LoginScreenState extends State { SizedBox( height: hp * 0.06, ), - Container( - height: hp * 0.07, - width: MediaQuery.of(context).size.width * 0.8, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20)), - child: ElevatedButton( - onPressed: () async { - if (_formKey.currentState != null && - _formKey.currentState!.validate()) { - BlocProvider.of(context, listen: false) - .add( - SetBaseUrlEvent(url: urlController.text), - ); - await Future.delayed(Duration.zero); - setState(() { - showSpinner = true; - }); - bool isLoginSuccessful = await AuthApi.loginUser( - username: usernameController.text, - password: passwordController.text, - context: context); - if (isLoginSuccessful) { - Toasts.showSuccessToast( - msg: l10n.login_success); - Navigator.of(context).pushNamedAndRemoveUntil( - Routes.homeScreenRoute, - (Route route) => false, - arguments: themeIndex, + FadeInUp( + child: Container( + height: hp * 0.07, + width: MediaQuery.of(context).size.width * 0.8, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20)), + child: ElevatedButton( + onPressed: () async { + if (_formKey.currentState != null && + _formKey.currentState!.validate()) { + BlocProvider.of(context, listen: false) + .add( + SetBaseUrlEvent(url: urlController.text), ); - SharedPreferences prefs = - await SharedPreferences.getInstance(); - bool batteryOptimizationInfoSeen = - prefs.getBool( - 'batteryOptimizationInfoSeen') ?? - false; - if (batteryOptimizationInfoSeen == false) { - BlocProvider.of(context, - listen: false) - .add( - SetBatteryOptimizationInfoStatusEvent( - seen: true)); - - Future.delayed( - Duration.zero, - () => showGeneralDialog( - barrierDismissible: false, - context: context, - barrierColor: Colors.black54, - // space around dialog - transitionDuration: - Duration(milliseconds: 1000), - transitionBuilder: - (context, a1, a2, child) { - return ScaleTransition( - scale: CurvedAnimation( - parent: a1, - curve: Curves.elasticOut, - reverseCurve: - Curves.easeOutCubic), - child: CustomDialogAnimation( - themeIndex: 2, - ), - ); - }, - pageBuilder: (BuildContext context, - Animation animation, - Animation - secondaryAnimation) { - return Container(); - }, - ), + await Future.delayed(Duration.zero); + setState(() { + showSpinner = true; + }); + bool isLoginSuccessful = + await AuthApi.loginUser( + username: usernameController.text, + password: passwordController.text, + context: context); + if (isLoginSuccessful) { + Toasts.showSuccessToast( + msg: l10n.login_success); + Navigator.of(context).pushNamedAndRemoveUntil( + Routes.homeScreenRoute, + (Route route) => false, + arguments: themeIndex, ); + SharedPreferences prefs = + await SharedPreferences.getInstance(); + bool batteryOptimizationInfoSeen = + prefs.getBool( + 'batteryOptimizationInfoSeen') ?? + false; + if (batteryOptimizationInfoSeen == false) { + BlocProvider.of(context, + listen: false) + .add( + SetBatteryOptimizationInfoStatusEvent( + seen: true)); + + Future.delayed( + Duration.zero, + () => showGeneralDialog( + barrierDismissible: false, + context: context, + barrierColor: Colors.black54, + // space around dialog + transitionDuration: + Duration(milliseconds: 1000), + transitionBuilder: + (context, a1, a2, child) { + return ScaleTransition( + scale: CurvedAnimation( + parent: a1, + curve: Curves.elasticOut, + reverseCurve: + Curves.easeOutCubic), + child: CustomDialogAnimation( + themeIndex: 2, + ), + ); + }, + pageBuilder: (BuildContext context, + Animation animation, + Animation + secondaryAnimation) { + return Container(); + }, + ), + ); + } + } else { + Toasts.showFailToast(msg: l10n.login_fail); } - } else { - Toasts.showFailToast(msg: l10n.login_fail); + setState(() { + showSpinner = false; + }); } - setState(() { - showSpinner = false; - }); - } - }, - style: ElevatedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(14.0), + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(14.0), + ), + backgroundColor: + ThemeBloc.theme(themeIndex).primaryColorDark, ), - backgroundColor: - ThemeBloc.theme(themeIndex).primaryColorDark, - ), - child: Center( - child: Text( - l10n.login_button, - style: TextStyle( - color: Colors.white, - fontSize: 20, - fontWeight: FontWeight.w600), + child: Center( + child: Text( + l10n.login_button, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w600), + ), ), ), ), diff --git a/lib/Pages/login_screen/widgets/login_screen_textfield.dart b/lib/Pages/login_screen/widgets/login_screen_textfield.dart index d6b8e96..dc11941 100644 --- a/lib/Pages/login_screen/widgets/login_screen_textfield.dart +++ b/lib/Pages/login_screen/widgets/login_screen_textfield.dart @@ -25,11 +25,25 @@ class LoginScreenTextField extends StatelessWidget { @override Widget build(BuildContext context) { return Container( + decoration: BoxDecoration( + color: ThemeBloc.theme(themeIndex).primaryColor, + borderRadius: BorderRadius.circular(10.0), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), child: Stack( children: [ TextFormField( controller: controller, obscureText: obscureText, + cursorColor: + ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!, validator: (String? value) { if (value != null && value.isEmpty) { return context.l10n.login_screen_textfield_validator; @@ -46,42 +60,31 @@ class LoginScreenTextField extends StatelessWidget { ), labelText: labelText, labelStyle: TextStyle( - fontFamily: 'Montserrat', - fontWeight: FontWeight.bold, - color: - ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: ThemeBloc.theme(themeIndex).primaryColorDark, - ), - ), - border: UnderlineInputBorder( - borderSide: BorderSide( - color: - ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!, - ), - ), - disabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: - ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!, - ), - ), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: - ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!, - ), + fontFamily: 'Montserrat', + fontWeight: FontWeight.bold, + color: ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!, ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + ThemeBloc.theme(themeIndex).textTheme.bodyLarge!.color!, + ), + borderRadius: BorderRadius.circular(10)), + enabledBorder: InputBorder.none, + errorBorder: InputBorder.none, + disabledBorder: InputBorder.none, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + trailingIconButton1 ?? Container(), + trailingIconButton2 ?? Container(), + ], ), ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - trailingIconButton1 ?? Container(), - trailingIconButton2 ?? Container(), - ], - ) ], ), ); diff --git a/pubspec.lock b/pubspec.lock index 9e2e895..970dbaf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.4.1" + animate_do: + dependency: "direct main" + description: + name: animate_do + sha256: "91b3e0306ba2096c7a7e1ee1ba96f491e25e92034c215f4bf8de6a7251c4bef1" + url: "https://pub.dev" + source: hosted + version: "3.1.2" args: dependency: transitive description: @@ -253,10 +261,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -761,18 +769,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" meta: dependency: "direct overridden" description: @@ -1158,26 +1166,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1222,26 +1230,26 @@ packages: dependency: transitive description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.9" timing: dependency: transitive description: @@ -1458,6 +1466,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1515,5 +1531,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9289ad2..2936b28 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -74,6 +74,7 @@ dependencies: wakelock: ^0.6.2 battery_plus: ^2.0.2 wifi_iot: ^0.3.18 + animate_do: ^3.1.2 dependency_overrides: meta: ^1.7.0