From 14b5e4e184f3277a3f44b309d69a9ff775d1be32 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 22 Apr 2025 09:55:10 +0800 Subject: [PATCH] feat: add loading indicator in continue to sign in button (#7799) --- ...inue_with_magic_link_or_passcode_page.dart | 74 +++++++++++++++---- 1 file changed, 59 insertions(+), 15 deletions(-) diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_magic_link_or_passcode_page.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_magic_link_or_passcode_page.dart index ec4fd1bbee..c29a18ea30 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_magic_link_or_passcode_page.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/continue_with/continue_with_magic_link_or_passcode_page.dart @@ -35,6 +35,8 @@ class _ContinueWithMagicLinkOrPasscodePageState final inputPasscodeKey = GlobalKey(); + bool isSubmitting = false; + @override void dispose() { passcodeController.dispose(); @@ -54,6 +56,10 @@ class _ContinueWithMagicLinkOrPasscodePageState ); }); } + + if (state.isSubmitting != isSubmitting) { + setState(() => isSubmitting = state.isSubmitting); + } }, child: Scaffold( body: Center( @@ -81,6 +87,15 @@ class _ContinueWithMagicLinkOrPasscodePageState List _buildEnterCodeManually() { // todo: ask designer to provide the spacing final spacing = VSpace(20); + final textStyle = AFButtonSize.l.buildTextStyle(context); + final textHeight = textStyle.height; + final textFontSize = textStyle.fontSize; + + // the indicator height is the height of the text style. + double indicatorHeight = 20; + if (textHeight != null && textFontSize != null) { + indicatorHeight = textHeight * textFontSize; + } if (!isEnteringPasscode) { return [ @@ -116,26 +131,55 @@ class _ContinueWithMagicLinkOrPasscodePageState VSpace(12), // continue to login - AFFilledTextButton.primary( - text: LocaleKeys.signIn_continueToSignIn.tr(), - onTap: () { - final passcode = passcodeController.text; - if (passcode.isEmpty) { - inputPasscodeKey.currentState?.syncError( - errorText: LocaleKeys.signIn_invalidVerificationCode.tr(), - ); - } else { - widget.onEnterPasscode(passcode); - } - }, - size: AFButtonSize.l, - alignment: Alignment.center, - ), + !isSubmitting + ? _buildContinueButton(textStyle: textStyle) + : _buildIndicator(indicatorHeight: indicatorHeight), spacing, ]; } + Widget _buildContinueButton({ + required TextStyle textStyle, + }) { + return AFFilledTextButton.primary( + text: LocaleKeys.signIn_continueToSignIn.tr(), + onTap: () { + final passcode = passcodeController.text; + if (passcode.isEmpty) { + inputPasscodeKey.currentState?.syncError( + errorText: LocaleKeys.signIn_invalidVerificationCode.tr(), + ); + } else { + widget.onEnterPasscode(passcode); + } + }, + textStyle: textStyle.copyWith( + color: AppFlowyTheme.of(context).textColorScheme.onFill, + ), + size: AFButtonSize.l, + alignment: Alignment.center, + ); + } + + Widget _buildIndicator({ + required double indicatorHeight, + }) { + return AFFilledButton.disabled( + size: AFButtonSize.l, + builder: (context, isHovering, disabled) { + return Align( + child: SizedBox.square( + dimension: indicatorHeight, + child: CircularProgressIndicator( + strokeWidth: 3.0, + ), + ), + ); + }, + ); + } + List _buildBackToLogin() { return [ AFGhostTextButton(