feat(flutter): 重设计登录页视觉
This commit is contained in:
1 parent
6cc5bfc3be
commit
45c3c645ff
4 files changed
+989
-229
No files matched your search
@@ -0,0 +1,183 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
part of 'auth_view.dart';
|
||||
|
||||
class _AuthCardState {
|
||||
const _AuthCardState({
|
||||
required this.l10n,
|
||||
required this.status,
|
||||
required this.fields,
|
||||
});
|
||||
|
||||
final AppLocalizations l10n;
|
||||
final _AuthFormStatus status;
|
||||
final _AuthFieldControllers fields;
|
||||
}
|
||||
|
||||
class _AuthFormStatus {
|
||||
const _AuthFormStatus({
|
||||
required this.signUpMode,
|
||||
required this.busy,
|
||||
required this.errorMessage,
|
||||
required this.obscurePassword,
|
||||
});
|
||||
|
||||
final bool signUpMode;
|
||||
final bool busy;
|
||||
final String? errorMessage;
|
||||
final bool obscurePassword;
|
||||
}
|
||||
|
||||
class _AuthFieldControllers {
|
||||
const _AuthFieldControllers({
|
||||
required this.email,
|
||||
required this.password,
|
||||
required this.emailFocus,
|
||||
required this.passwordFocus,
|
||||
});
|
||||
|
||||
final TextEditingController email;
|
||||
final TextEditingController password;
|
||||
final FocusNode emailFocus;
|
||||
final FocusNode passwordFocus;
|
||||
}
|
||||
|
||||
class _AuthCardCallbacks {
|
||||
const _AuthCardCallbacks({
|
||||
required this.onModeSelected,
|
||||
required this.onEmailChanged,
|
||||
required this.onPasswordChanged,
|
||||
required this.onTogglePasswordVisibility,
|
||||
required this.onSubmit,
|
||||
});
|
||||
|
||||
final ValueChanged<bool> onModeSelected;
|
||||
final ValueChanged<String> onEmailChanged;
|
||||
final ValueChanged<String> onPasswordChanged;
|
||||
final VoidCallback onTogglePasswordVisibility;
|
||||
final VoidCallback onSubmit;
|
||||
}
|
||||
|
||||
class _AuthCard extends StatelessWidget {
|
||||
const _AuthCard({required this.state, required this.callbacks});
|
||||
|
||||
final _AuthCardState state;
|
||||
final _AuthCardCallbacks callbacks;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = state.l10n;
|
||||
final status = state.status;
|
||||
return Container(
|
||||
key: const Key('auth-card'),
|
||||
padding: const EdgeInsets.all(_cardPadding),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.circular(_cardRadius),
|
||||
border: Border.all(
|
||||
color: AppColors.outlineVariantDark,
|
||||
width: _cardBorderWidth,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_AuthModeSwitch(
|
||||
l10n: l10n,
|
||||
signUpMode: status.signUpMode,
|
||||
busy: status.busy,
|
||||
onSelected: callbacks.onModeSelected,
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
Text(
|
||||
status.signUpMode ? l10n.authSignUpTitle : l10n.authSignInTitle,
|
||||
style: const TextStyle(
|
||||
color: AppColors.onPrimary,
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.w800,
|
||||
height: 1.2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
l10n.authSubtitle,
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: 13,
|
||||
height: 1.45,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 22),
|
||||
_AuthFields(state: state, callbacks: callbacks),
|
||||
if (status.errorMessage != null) ...[
|
||||
const SizedBox(height: 14),
|
||||
_AuthError(message: status.errorMessage!),
|
||||
],
|
||||
const SizedBox(height: 22),
|
||||
_AuthSubmitButton(state: state, onSubmit: callbacks.onSubmit),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AuthModeSwitch extends StatelessWidget {
|
||||
const _AuthModeSwitch({
|
||||
required this.l10n,
|
||||
required this.signUpMode,
|
||||
required this.busy,
|
||||
required this.onSelected,
|
||||
});
|
||||
|
||||
final AppLocalizations l10n;
|
||||
final bool signUpMode;
|
||||
final bool busy;
|
||||
final ValueChanged<bool> onSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
key: const Key('auth-switch-mode'),
|
||||
onPressed: busy ? null : () => onSelected(!signUpMode),
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: const Size(0, _minTapTarget),
|
||||
padding: EdgeInsets.zero,
|
||||
overlayColor: Colors.transparent,
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(22)),
|
||||
),
|
||||
child: Container(
|
||||
height: _minTapTarget,
|
||||
padding: const EdgeInsets.all(3),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.bar,
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
border: Border.all(color: AppColors.outlineVariantDark),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _ModeSegment(
|
||||
segmentKey: const Key('auth-mode-sign-in'),
|
||||
label: l10n.signIn,
|
||||
hiddenLabel: l10n.authSwitchToSignIn,
|
||||
selected: !signUpMode,
|
||||
onTap: busy ? null : () => onSelected(false),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: _ModeSegment(
|
||||
segmentKey: const Key('auth-mode-sign-up'),
|
||||
label: l10n.signUp,
|
||||
hiddenLabel: l10n.authSwitchToSignUp,
|
||||
selected: signUpMode,
|
||||
onTap: busy ? null : () => onSelected(true),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user