feat: 添加视觉设计语言系统并重构认证页面UI
- 新增 visual_design_language.md 设计规范文档 - 新增 auth 设计 tokens (authBackground, authCard, authInput, feedback 系列等) - 重构登录/注册/验证码/重置密码页面为新设计系统 - 新增 AuthHeroHeader, AuthSurfaceCard, AuthSection, AuthField, PasswordField 组件 - 重构 AppBanner 和 Toast 支持多类型配置 (info/success/warning/error) - 后端 AgentScope: 重整 schemas/prompts/tools 作用域, 新增协议文档 - 更新 AGENTS.md 集成视觉设计语言约束
This commit is contained in:
@@ -60,7 +60,9 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
|
||||
|
||||
void _onFocusChanged() {
|
||||
if (_isFocused != _focusNode.hasFocus) {
|
||||
_isFocused = _focusNode.hasFocus;
|
||||
setState(() {
|
||||
_isFocused = _focusNode.hasFocus;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,56 +100,80 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final chars = widget.controller.text.split('');
|
||||
final slotHeight = AppSpacing.xl * 2;
|
||||
final slotHeight = AppSpacing.xl * 2 + AppSpacing.sm;
|
||||
final slotSpacing = AppSpacing.sm;
|
||||
final isComplete = chars.length == widget.length;
|
||||
|
||||
return Semantics(
|
||||
label: widget.semanticLabel,
|
||||
child: GestureDetector(
|
||||
onTap: () => _focusNode.requestFocus(),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: SizedBox(
|
||||
height: slotHeight,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Opacity(
|
||||
opacity: 0,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: slotHeight,
|
||||
child: TextField(
|
||||
controller: widget.controller,
|
||||
focusNode: _focusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(widget.length),
|
||||
],
|
||||
onChanged: _handleRawChanged,
|
||||
autofillHints: const [AutofillHints.oneTimeCode],
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
padding: const EdgeInsets.all(AppSpacing.sm),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.authSectionBackground,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(
|
||||
color: _isFocused
|
||||
? AppColors.authInputFocus
|
||||
: AppColors.authSectionBorder,
|
||||
),
|
||||
boxShadow: _isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: AppColors.blue200.withValues(alpha: 0.28),
|
||||
blurRadius: 18,
|
||||
offset: const Offset(0, 8),
|
||||
),
|
||||
]
|
||||
: const [],
|
||||
),
|
||||
child: SizedBox(
|
||||
height: slotHeight,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Opacity(
|
||||
opacity: 0,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: slotHeight,
|
||||
child: TextField(
|
||||
controller: widget.controller,
|
||||
focusNode: _focusNode,
|
||||
keyboardType: widget.keyboardType,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(widget.length),
|
||||
],
|
||||
onChanged: _handleRawChanged,
|
||||
autofillHints: const [AutofillHints.oneTimeCode],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
IgnorePointer(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
for (var index = 0; index < widget.length; index++) ...[
|
||||
Expanded(
|
||||
child: _buildCodeCell(
|
||||
index: index,
|
||||
chars: chars,
|
||||
slotHeight: slotHeight,
|
||||
IgnorePointer(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
for (var index = 0; index < widget.length; index++) ...[
|
||||
Expanded(
|
||||
child: _buildCodeCell(
|
||||
index: index,
|
||||
chars: chars,
|
||||
slotHeight: slotHeight,
|
||||
isComplete: isComplete,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (index != widget.length - 1)
|
||||
SizedBox(width: slotSpacing),
|
||||
if (index != widget.length - 1)
|
||||
SizedBox(width: slotSpacing),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -158,6 +184,7 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
|
||||
required int index,
|
||||
required List<String> chars,
|
||||
required double slotHeight,
|
||||
required bool isComplete,
|
||||
}) {
|
||||
final hasChar = index < chars.length;
|
||||
final isActive =
|
||||
@@ -168,18 +195,31 @@ class _FixedLengthCodeInputState extends State<FixedLengthCodeInput> {
|
||||
height: slotHeight,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
color: hasChar ? AppColors.white : AppColors.authInputBackground,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(
|
||||
color: isActive ? AppColors.primary : AppColors.slate300,
|
||||
color: isActive
|
||||
? AppColors.authPrimaryButton
|
||||
: isComplete
|
||||
? AppColors.authSecondaryButtonBorder
|
||||
: AppColors.authInputBorder,
|
||||
),
|
||||
boxShadow: isActive
|
||||
? [
|
||||
BoxShadow(
|
||||
color: AppColors.blue200.withValues(alpha: 0.32),
|
||||
blurRadius: 14,
|
||||
offset: const Offset(0, 6),
|
||||
),
|
||||
]
|
||||
: const [],
|
||||
),
|
||||
child: Text(
|
||||
hasChar ? chars[index] : '',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: AppSpacing.xl,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate900,
|
||||
color: hasChar ? AppColors.slate900 : AppColors.authLinkMuted,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user