forked from Cutlery/immich
		
	improve login ux (#2004)
removed animated switchers to resolve issue with flutter/issues/120874
This commit is contained in:
		
							parent
							
								
									3a1d5de742
								
							
						
					
					
						commit
						08ed71e51e
					
				| @ -242,5 +242,6 @@ | |||||||
|   "permission_onboarding_go_to_settings": "Go to settings", |   "permission_onboarding_go_to_settings": "Go to settings", | ||||||
|   "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", |   "permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.", | ||||||
|   "permission_onboarding_continue_anyway": "Continue anyway", |   "permission_onboarding_continue_anyway": "Continue anyway", | ||||||
|   "permission_onboarding_log_out": "Log out" |   "permission_onboarding_log_out": "Log out", | ||||||
|  |   "login_form_next_button": "Next" | ||||||
| } | } | ||||||
|  | |||||||
| @ -57,13 +57,13 @@ class LoginForm extends HookConsumerWidget { | |||||||
|           msg: "login_form_server_empty".tr(), |           msg: "login_form_server_empty".tr(), | ||||||
|           toastType: ToastType.error, |           toastType: ToastType.error, | ||||||
|         ); |         ); | ||||||
|   | 
 | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       try { |       try { | ||||||
|         isLoadingServer.value = true; |         isLoadingServer.value = true; | ||||||
|         final endpoint =  |         final endpoint = | ||||||
|             await apiService.resolveAndSetEndpoint(serverUrl); |             await apiService.resolveAndSetEndpoint(serverUrl); | ||||||
| 
 | 
 | ||||||
|         final loginConfig = await apiService.oAuthApi.generateConfig( |         final loginConfig = await apiService.oAuthApi.generateConfig( | ||||||
| @ -96,7 +96,7 @@ class LoginForm extends HookConsumerWidget { | |||||||
|         isOauthEnable.value = false; |         isOauthEnable.value = false; | ||||||
|         isLoadingServer.value = false; |         isLoadingServer.value = false; | ||||||
|         return false; |         return false; | ||||||
|       }  |       } | ||||||
| 
 | 
 | ||||||
|       isLoadingServer.value = false; |       isLoadingServer.value = false; | ||||||
|       return true; |       return true; | ||||||
| @ -166,7 +166,6 @@ class LoginForm extends HookConsumerWidget { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     oAuthLogin() async { |     oAuthLogin() async { | ||||||
|       var oAuthService = ref.watch(oAuthServiceProvider); |       var oAuthService = ref.watch(oAuthServiceProvider); | ||||||
|       ref.watch(assetProvider.notifier).clearAllAsset(); |       ref.watch(assetProvider.notifier).clearAllAsset(); | ||||||
| @ -230,151 +229,153 @@ class LoginForm extends HookConsumerWidget { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     buildSelectServer() { |     buildSelectServer() { | ||||||
|       return ConstrainedBox( |       return Column( | ||||||
|         key: const ValueKey('server'), |         crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|         constraints: const BoxConstraints(maxWidth: 300), |         children: [ | ||||||
|  |           ServerEndpointInput( | ||||||
|  |             controller: serverEndpointController, | ||||||
|  |             focusNode: serverEndpointFocusNode, | ||||||
|  |             onSubmit: getServerLoginCredential, | ||||||
|  |           ), | ||||||
|  |           const SizedBox(height: 18), | ||||||
|  |           ElevatedButton.icon( | ||||||
|  |             style: ElevatedButton.styleFrom( | ||||||
|  |               padding: const EdgeInsets.symmetric(vertical: 12), | ||||||
|  |             ), | ||||||
|  |             onPressed: isLoadingServer.value ? null : getServerLoginCredential, | ||||||
|  |             icon: const Icon(Icons.arrow_forward_rounded), | ||||||
|  |             label: const Text( | ||||||
|  |               'login_form_next_button', | ||||||
|  |               style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), | ||||||
|  |             ).tr(), | ||||||
|  |           ), | ||||||
|  |           if (isLoadingServer.value) | ||||||
|  |             const Padding( | ||||||
|  |               padding: EdgeInsets.only(top: 18.0), | ||||||
|  |               child: Center( | ||||||
|  |                 child: CircularProgressIndicator(), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |         ], | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     buildLogin() { | ||||||
|  |       return AutofillGroup( | ||||||
|         child: Column( |         child: Column( | ||||||
|           crossAxisAlignment: CrossAxisAlignment.stretch, |           crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|           children: [ |           children: [ | ||||||
|             ServerEndpointInput( |             Text( | ||||||
|               controller: serverEndpointController, |               serverEndpointController.text, | ||||||
|               focusNode: serverEndpointFocusNode, |               style: Theme.of(context).textTheme.displaySmall, | ||||||
|               onSubmit: getServerLoginCredential, |               textAlign: TextAlign.center, | ||||||
|             ), |             ), | ||||||
|             const SizedBox(height: 18), |             const SizedBox(height: 18), | ||||||
|             ElevatedButton.icon( |             EmailInput( | ||||||
|               style: ElevatedButton.styleFrom( |               controller: usernameController, | ||||||
|                 padding: const EdgeInsets.symmetric(vertical: 12), |               focusNode: emailFocusNode, | ||||||
|               ), |               onSubmit: passwordFocusNode.requestFocus, | ||||||
|               onPressed: isLoadingServer.value ? null : getServerLoginCredential, |  | ||||||
|               icon: const Icon(Icons.arrow_forward_rounded), |  | ||||||
|               label: const Text( |  | ||||||
|                 'Next', |  | ||||||
|                 style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), |  | ||||||
|               ).tr(), |  | ||||||
|             ), |             ), | ||||||
|             if (isLoadingServer.value) |             const SizedBox(height: 8), | ||||||
|               const Padding( |             PasswordInput( | ||||||
|                 padding: EdgeInsets.only(top: 18.0), |               controller: passwordController, | ||||||
|                 child: Center( |               focusNode: passwordFocusNode, | ||||||
|                   child: CircularProgressIndicator(), |               onSubmit: login, | ||||||
|  |             ), | ||||||
|  | 
 | ||||||
|  |           // Note: This used to have an AnimatedSwitcher, but was removed | ||||||
|  |           // because of https://github.com/flutter/flutter/issues/120874 | ||||||
|  |           isLoading.value | ||||||
|  |               ? const Padding( | ||||||
|  |                   padding: EdgeInsets.only(top: 18.0), | ||||||
|  |                   child: SizedBox( | ||||||
|  |                     width: 24, | ||||||
|  |                     height: 24, | ||||||
|  |                     child: FittedBox( | ||||||
|  |                       child: CircularProgressIndicator( | ||||||
|  |                         strokeWidth: 2, | ||||||
|  |                       ), | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |                 ) | ||||||
|  |               : Column( | ||||||
|  |                   crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|  |                   mainAxisAlignment: MainAxisAlignment.center, | ||||||
|  |                   children: [ | ||||||
|  |                     const SizedBox(height: 18), | ||||||
|  |                     LoginButton(onPressed: login), | ||||||
|  |                     if (isOauthEnable.value) ...[ | ||||||
|  |                       Padding( | ||||||
|  |                         padding: const EdgeInsets.symmetric( | ||||||
|  |                           horizontal: 16.0, | ||||||
|  |                         ), | ||||||
|  |                         child: Divider( | ||||||
|  |                           color: | ||||||
|  |                               Brightness.dark == Theme.of(context).brightness | ||||||
|  |                                   ? Colors.white | ||||||
|  |                                   : Colors.black, | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                       OAuthLoginButton( | ||||||
|  |                         serverEndpointController: serverEndpointController, | ||||||
|  |                         buttonLabel: oAuthButtonLabel.value, | ||||||
|  |                         isLoading: isLoading, | ||||||
|  |                         onPressed: oAuthLogin, | ||||||
|  |                       ), | ||||||
|  |                     ], | ||||||
|  |                   ], | ||||||
|                 ), |                 ), | ||||||
|  |               const SizedBox(height: 12), | ||||||
|  |               TextButton.icon( | ||||||
|  |                 icon: const Icon(Icons.arrow_back), | ||||||
|  |                 onPressed: () => serverEndpoint.value = null, | ||||||
|  |                 label: const Text('Back'), | ||||||
|               ), |               ), | ||||||
|           ], |           ], | ||||||
|         ), |         ), | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 |     final serverSelectionOrLogin = serverEndpoint.value == null | ||||||
|     buildLogin() { |  | ||||||
|       return ConstrainedBox( |  | ||||||
|         key: const ValueKey('login'), |  | ||||||
|         constraints: const BoxConstraints(maxWidth: 300), |  | ||||||
|         child: AutofillGroup( |  | ||||||
|           child: Column( |  | ||||||
|             crossAxisAlignment: CrossAxisAlignment.stretch, |  | ||||||
|             children: [ |  | ||||||
|               Text( |  | ||||||
|                 serverEndpointController.text, |  | ||||||
|                 style: Theme.of(context).textTheme.displaySmall, |  | ||||||
|                 textAlign: TextAlign.center, |  | ||||||
|               ), |  | ||||||
|               const SizedBox(height: 18), |  | ||||||
|               EmailInput( |  | ||||||
|                 controller: usernameController, |  | ||||||
|                 focusNode: emailFocusNode, |  | ||||||
|                 onSubmit: passwordFocusNode.requestFocus, |  | ||||||
|               ), |  | ||||||
|               const SizedBox(height: 8), |  | ||||||
|               PasswordInput( |  | ||||||
|                 controller: passwordController, |  | ||||||
|                 focusNode: passwordFocusNode, |  | ||||||
|                 onSubmit: login, |  | ||||||
|               ), |  | ||||||
|               AnimatedSwitcher( |  | ||||||
|                 duration: const Duration(milliseconds: 500), |  | ||||||
|                 child: isLoading.value  |  | ||||||
|                   ? const SizedBox( |  | ||||||
|                       width: 24, |  | ||||||
|                       height: 24, |  | ||||||
|                       child: CircularProgressIndicator( |  | ||||||
|                         strokeWidth: 2, |  | ||||||
|                       ), |  | ||||||
|                     ) |  | ||||||
|                   : Column( |  | ||||||
|                       crossAxisAlignment: CrossAxisAlignment.stretch, |  | ||||||
|                       mainAxisAlignment: MainAxisAlignment.center, |  | ||||||
|                       children: [ |  | ||||||
|                         const SizedBox(height: 18), |  | ||||||
|                         LoginButton(onPressed: login), |  | ||||||
|                         if (isOauthEnable.value) ...[ |  | ||||||
|                           Padding( |  | ||||||
|                             padding: const EdgeInsets.symmetric( |  | ||||||
|                               horizontal: 16.0, |  | ||||||
|                             ), |  | ||||||
|                             child: Divider( |  | ||||||
|                               color: |  | ||||||
|                                   Brightness.dark == Theme.of(context).brightness |  | ||||||
|                                       ? Colors.white |  | ||||||
|                                       : Colors.black, |  | ||||||
|                             ), |  | ||||||
|                           ), |  | ||||||
|                           OAuthLoginButton( |  | ||||||
|                             serverEndpointController: serverEndpointController, |  | ||||||
|                             buttonLabel: oAuthButtonLabel.value, |  | ||||||
|                             isLoading: isLoading, |  | ||||||
|                             onPressed: oAuthLogin, |  | ||||||
|                           ), |  | ||||||
|                         ], |  | ||||||
|                       ], |  | ||||||
|                     ), |  | ||||||
|                   ), |  | ||||||
|                 const SizedBox(height: 12), |  | ||||||
|                 TextButton.icon( |  | ||||||
|                   icon: const Icon(Icons.arrow_back), |  | ||||||
|                   onPressed: () => serverEndpoint.value = null, |  | ||||||
|                   label: const Text('Back'), |  | ||||||
|                 ), |  | ||||||
|             ], |  | ||||||
|           ), |  | ||||||
|         ), |  | ||||||
|       ); |  | ||||||
|     } |  | ||||||
|     final child = serverEndpoint.value == null  |  | ||||||
|       ? buildSelectServer() |       ? buildSelectServer() | ||||||
|       : buildLogin(); |       : buildLogin(); | ||||||
| 
 | 
 | ||||||
|     return LayoutBuilder( |     return LayoutBuilder( | ||||||
|       builder: (context, constraints) { |       builder: (context, constraints) { | ||||||
|         return SingleChildScrollView( |         return SingleChildScrollView( | ||||||
|           child: Column( |           child: Center( | ||||||
|             crossAxisAlignment: CrossAxisAlignment.stretch, |             child: Container( | ||||||
|             mainAxisAlignment: MainAxisAlignment.center, |               constraints: const BoxConstraints(maxWidth: 300), | ||||||
|             children: [ |               child: Column( | ||||||
|               SizedBox( |                 crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|                 height: constraints.maxHeight / 5, |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|               ), |  | ||||||
|               Column( |  | ||||||
|                 crossAxisAlignment: CrossAxisAlignment.center, |  | ||||||
|                 mainAxisAlignment: MainAxisAlignment.end, |  | ||||||
|                 children: [ |                 children: [ | ||||||
|                   GestureDetector( |                   SizedBox( | ||||||
|                     onDoubleTap: () => populateTestLoginInfo(), |                     height: constraints.maxHeight / 5, | ||||||
|                     child: RotationTransition( |  | ||||||
|                       turns: logoAnimationController, |  | ||||||
|                       child: const ImmichLogo( |  | ||||||
|                         heroTag: 'logo', |  | ||||||
|                       ), |  | ||||||
|                     ), |  | ||||||
|                   ), |                   ), | ||||||
|                   const ImmichTitleText(), |                   Column( | ||||||
|  |                     crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |                     mainAxisAlignment: MainAxisAlignment.end, | ||||||
|  |                     children: [ | ||||||
|  |                       GestureDetector( | ||||||
|  |                         onDoubleTap: () => populateTestLoginInfo(), | ||||||
|  |                         child: RotationTransition( | ||||||
|  |                           turns: logoAnimationController, | ||||||
|  |                           child: const ImmichLogo( | ||||||
|  |                             heroTag: 'logo', | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                       const ImmichTitleText(), | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                   const SizedBox(height: 18), | ||||||
|  | 
 | ||||||
|  |                   // Note: This used to have an AnimatedSwitcher, but was removed | ||||||
|  |                   // because of https://github.com/flutter/flutter/issues/120874 | ||||||
|  |                   serverSelectionOrLogin, | ||||||
|                 ], |                 ], | ||||||
|               ), |               ), | ||||||
|               const SizedBox(height: 18), |             ), | ||||||
|               AnimatedSwitcher( |  | ||||||
|                 duration: const Duration(milliseconds: 500), |  | ||||||
|                 child: child,  |  | ||||||
|               ), |  | ||||||
|             ], |  | ||||||
|           ), |           ), | ||||||
|         ); |         ); | ||||||
|       }, |       }, | ||||||
| @ -386,7 +387,7 @@ class ServerEndpointInput extends StatelessWidget { | |||||||
|   final TextEditingController controller; |   final TextEditingController controller; | ||||||
|   final FocusNode focusNode; |   final FocusNode focusNode; | ||||||
|   final Function()? onSubmit; |   final Function()? onSubmit; | ||||||
|    | 
 | ||||||
|   const ServerEndpointInput({ |   const ServerEndpointInput({ | ||||||
|     Key? key, |     Key? key, | ||||||
|     required this.controller, |     required this.controller, | ||||||
| @ -436,7 +437,7 @@ class EmailInput extends StatelessWidget { | |||||||
|   final Function()? onSubmit; |   final Function()? onSubmit; | ||||||
| 
 | 
 | ||||||
|   const EmailInput({ |   const EmailInput({ | ||||||
|     Key? key,  |     Key? key, | ||||||
|     required this.controller, |     required this.controller, | ||||||
|     this.focusNode, |     this.focusNode, | ||||||
|     this.onSubmit, |     this.onSubmit, | ||||||
| @ -480,8 +481,8 @@ class PasswordInput extends StatelessWidget { | |||||||
| 
 | 
 | ||||||
|   const PasswordInput({ |   const PasswordInput({ | ||||||
|     Key? key, |     Key? key, | ||||||
|     required this.controller,  |     required this.controller, | ||||||
|     this.focusNode,  |     this.focusNode, | ||||||
|     this.onSubmit, |     this.onSubmit, | ||||||
|   }) : super(key: key); |   }) : super(key: key); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user