feat(auth): 登录页改为 ymb 品牌并支持后台配置背景图

This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-30 17:14:30 +08:00
1 parent 22cc669bbe
commit e7399fa3fd
24 files changed
+378 -188

No files matched your search

+9 -27
View File
@@ -300,7 +300,7 @@ class _AuthBrand extends StatelessWidget {
@override
Widget build(BuildContext context) {
final copy = _brandCopy(context, centered: !compact);
final copy = _brandCopy(centered: !compact);
if (compact) {
return Row(
key: const Key('auth-brand'),
@@ -324,32 +324,14 @@ class _AuthBrand extends StatelessWidget {
radius: compact ? _compactBrandMarkRadius : _brandMarkRadius,
);
Widget _brandCopy(BuildContext context, {required bool centered}) => Column(
crossAxisAlignment: centered
? CrossAxisAlignment.center
: CrossAxisAlignment.start,
children: [
Text(
AppLocalizations.of(context)!.appName,
textAlign: centered ? TextAlign.center : TextAlign.start,
style: const TextStyle(
color: AppColors.onSurface,
fontSize: 24,
fontWeight: FontWeight.w700,
height: 1.1,
),
),
const SizedBox(height: 6),
Text(
tagline,
textAlign: centered ? TextAlign.center : TextAlign.start,
style: const TextStyle(
color: AppColors.muted,
fontSize: 13,
height: 1.35,
),
),
],
Widget _brandCopy({required bool centered}) => Text(
tagline,
textAlign: centered ? TextAlign.center : TextAlign.start,
style: const TextStyle(
color: AppColors.muted,
fontSize: 13,
height: 1.35,
),
);
}
+71 -74
View File
@@ -7,6 +7,7 @@ import 'package:provider/provider.dart';
import '../../core/data/remote/api_client.dart';
import '../../core/formatting/error_text.dart';
import '../../core/data/services/app_config_controller.dart';
import '../../core/data/services/auth_controller.dart';
import '../../core/platform/haptics.dart';
import '../../core/platform/keyboard_inset.dart';
@@ -14,6 +15,7 @@ import '../../core/theme/app_colors.dart';
import '../../core/theme/app_dimens.dart';
import '../../core/theme/app_theme.dart';
import '../../core/widgets/centered_toast.dart';
import '../../core/widgets/remote_image.dart';
import '../../core/widgets/subpage_header.dart';
import '../../l10n/app_localizations.dart';
import 'auth_legal_links.dart';
@@ -28,7 +30,7 @@ const double _brandMarkSize = 88;
const double _compactBrandMarkSize = 48;
const double _brandMarkRadius = 26;
const double _compactBrandMarkRadius = 14;
const String _appLogoAsset = 'assets/icon/fruitshorts.png';
const String _appLogoAsset = 'assets/icon/ymb.png';
const double _contentMaxWidth = 440;
const double _fieldHeight = 52;
const double _fieldRadius = 12;
@@ -276,9 +278,7 @@ class _AuthPageState extends State<AuthPage> {
),
),
// 全屏海报背景:压暗保证文字可读,顶部再叠品牌红光。
Positioned.fill(
child: IgnorePointer(child: _AuthPosterBackdrop()),
),
Positioned.fill(child: IgnorePointer(child: _AuthPosterBackdrop())),
const Positioned.fill(
child: IgnorePointer(
child: DecoratedBox(
@@ -396,80 +396,67 @@ class _AuthPageState extends State<AuthPage> {
AppLocalizations l10n, {
required bool compact,
required double minHeight,
}) =>
TweenAnimationBuilder<double>(
key: const Key('auth-enter-animation'),
tween: Tween(begin: 0, end: 1),
duration: _enterDuration,
curve: Curves.easeOutCubic,
builder: (context, value, child) => Opacity(
opacity: value,
child: Transform.translate(
offset: Offset(0, _enterSlideDistance * (1 - value)),
child: child,
),
),
child: _emailEntryOpen
? Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_AuthBrand(tagline: l10n.authBrandTagline, compact: compact),
SizedBox(height: compact ? _compactBrandGap : 28),
_authCard(l10n),
],
)
: _landing(l10n, minHeight: minHeight),
);
}) => TweenAnimationBuilder<double>(
key: const Key('auth-enter-animation'),
tween: Tween(begin: 0, end: 1),
duration: _enterDuration,
curve: Curves.easeOutCubic,
builder: (context, value, child) => Opacity(
opacity: value,
child: Transform.translate(
offset: Offset(0, _enterSlideDistance * (1 - value)),
child: child,
),
),
child: _emailEntryOpen
? Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_AuthBrand(tagline: l10n.authBrandTagline, compact: compact),
SizedBox(height: compact ? _compactBrandGap : 28),
_authCard(l10n),
],
)
: _landing(l10n, minHeight: minHeight),
);
/// 落地页内容需要有限高度才能用 Spacer 三段分割,这里显式钉住可用高度。
Widget _landing(AppLocalizations l10n, {required double minHeight}) =>
SizedBox(
height: minHeight,
child: Column(
key: const Key('auth-landing'),
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Spacer(flex: 5),
Center(
child: _brandMarkWidget(
key: const Key('auth-brand-logo'),
size: _brandMarkSize,
radius: _brandMarkRadius,
key: const Key('auth-landing'),
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Spacer(flex: 5),
Center(
child: _brandMarkWidget(
key: const Key('auth-brand-logo'),
size: _brandMarkSize,
radius: _brandMarkRadius,
),
),
const Spacer(flex: 2),
_AuthEntryButton(
key: const Key('auth-entry-phone'),
label: l10n.authPhoneSignIn,
icon: Icons.phone_android_rounded,
primary: true,
onTap: _openPhoneSheet,
),
const SizedBox(height: 12),
_AuthEntryButton(
key: const Key('auth-entry-email'),
label: l10n.authEmailSignIn,
icon: Icons.mail_outline_rounded,
primary: false,
onTap: () => setState(() => _emailEntryOpen = true),
),
const SizedBox(height: 14),
const Opacity(opacity: 0.75, child: AuthLegalLinks()),
const Spacer(flex: 6),
],
),
),
const SizedBox(height: 10),
Text(
l10n.fruitShortsBrand,
textAlign: TextAlign.center,
style: const TextStyle(
fontFamily: 'Outfit',
color: AppColors.onSurface,
fontSize: 26,
fontWeight: FontWeight.w700,
letterSpacing: 2,
),
),
const Spacer(flex: 3),
_AuthEntryButton(
key: const Key('auth-entry-phone'),
label: l10n.authPhoneSignIn,
icon: Icons.phone_android_rounded,
primary: true,
onTap: _openPhoneSheet,
),
const SizedBox(height: 12),
_AuthEntryButton(
key: const Key('auth-entry-email'),
label: l10n.authEmailSignIn,
icon: Icons.mail_outline_rounded,
primary: false,
onTap: () => setState(() => _emailEntryOpen = true),
),
const SizedBox(height: 14),
const Opacity(opacity: 0.75, child: AuthLegalLinks()),
const Spacer(flex: 6),
],
),
);
Future<void> _openPhoneSheet() async {
@@ -578,16 +565,26 @@ class _AuthEntryButton extends StatelessWidget {
}
/// 登录页全屏海报背景(单张,无拼缝)。
///
/// 优先用后台下发的 `loginBackgroundUrl`(后台未配置或加载失败时回落到本地默认图)。
class _AuthPosterBackdrop extends StatelessWidget {
const _AuthPosterBackdrop();
static const String _defaultAsset = 'assets/auth/bg_3.jpg';
@override
Widget build(BuildContext context) {
return Image.asset(
final remoteUrl = context.watch<AppConfigController>().loginBackgroundUrl;
return RemoteImage(
key: const Key('auth-poster-backdrop'),
'assets/auth/bg_3.jpg',
remoteUrl,
fit: BoxFit.cover,
alignment: Alignment.center,
fallbackWidget: Image.asset(
_defaultAsset,
key: const Key('auth-poster-default'),
fit: BoxFit.cover,
alignment: Alignment.center,
),
);
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ class MineAboutPage extends StatelessWidget {
),
children: [
Text(
l10n.appName,
l10n.aboutBrandName,
textAlign: TextAlign.center,
style: const TextStyle(
fontFamily: 'Outfit',