chore: 更新国际化翻译及 UI 组件优化

This commit is contained in:
zl-q
2026-03-30 09:07:30 +08:00
parent 0f3175e303
commit 60318b7aaa
28 changed files with 1360 additions and 66 deletions
@@ -41,8 +41,6 @@ class LoginView extends StatefulWidget {
}
class _LoginViewState extends State<LoginView> {
static const _dialCodes = <String>['+86', '+1', '+44', '+81', '+65'];
final _phoneController = TextEditingController();
final _codeController = TextEditingController();
bool _agreedToTerms = false;
@@ -230,7 +228,6 @@ class _LoginViewState extends State<LoginView> {
keyboardType: TextInputType.phone,
prefix: PhonePrefixSelector(
value: state.dialCode,
items: _dialCodes,
onChanged: (value) {
context
.read<LoginCubit>()
@@ -113,13 +113,15 @@ class FriendRequestResponse {
});
factory FriendRequestResponse.fromJson(Map<String, dynamic> json) {
final rawContent = json['content'] as Map<String, dynamic>?;
final content = rawContent?['message'] as String?;
return FriendRequestResponse(
id: json['id'] as String,
sender: UserBasicInfo.fromJson(json['sender'] as Map<String, dynamic>),
recipient: UserBasicInfo.fromJson(
json['recipient'] as Map<String, dynamic>,
),
content: json['content'] as String?,
content: content,
status: json['status'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
);
@@ -38,11 +38,13 @@ class FriendRequest {
});
factory FriendRequest.fromJson(Map<String, dynamic> json) {
final rawContent = json['content'] as Map<String, dynamic>?;
final content = rawContent?['message'] as String?;
return FriendRequest(
id: json['id'] as String,
sender: FriendUser.fromJson(json['sender'] as Map<String, dynamic>),
recipient: FriendUser.fromJson(json['recipient'] as Map<String, dynamic>),
content: json['content'] as String?,
content: content,
status: _friendRequestStatusFromApi(json['status'] as String),
createdAt: DateTime.parse(json['created_at'] as String),
);
@@ -342,9 +342,7 @@ class _HomeScreenState extends State<HomeScreen>
if (showWaitingIndicator)
Align(
alignment: Alignment.bottomLeft,
child: HomeWaitingIndicator(
label: stageLabel(state.currentStage),
),
child: HomeWaitingIndicator(label: _agentWaitingLabel(state)),
),
Align(
alignment: Alignment.topCenter,
@@ -494,6 +492,13 @@ class _HomeScreenState extends State<HomeScreen>
return state.isWaitingFirstToken || state.isStreaming || state.isCancelling;
}
String _agentWaitingLabel(ChatState state) {
if (state.isWaitingFirstToken && !state.hasSeenStep) {
return context.l10n.agentStageRequesting;
}
return stageLabel(state.currentStage);
}
void _handleScrollChanged() {
if (!_scrollController.hasClients) {
return;
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import '../../../../core/theme/design_tokens.dart';
const homeBackgroundFieldKey = ValueKey('home_background_field');
class HomeBackgroundField extends StatelessWidget {
@@ -13,12 +15,65 @@ class HomeBackgroundField extends StatelessWidget {
key: homeBackgroundFieldKey,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [colorScheme.surface, colorScheme.surfaceContainerLowest],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.surface,
colorScheme.surfaceContainerLow,
colorScheme.surface,
],
stops: const [0, 0.38, 1],
),
),
child: Stack(
fit: StackFit.expand,
children: [
Positioned(
top: -(AppSpacing.xxl * 2),
left: -(AppSpacing.xxl * 2),
child: _AmbientOrb(
color: colorScheme.primaryContainer.withValues(alpha: 0.55),
size: AppSpacing.xxl * 8,
),
),
Positioned(
right: -(AppSpacing.xxl * 2),
top: AppSpacing.xxl,
child: _AmbientOrb(
color: colorScheme.secondaryContainer.withValues(alpha: 0.42),
size: AppSpacing.xxl * 6,
),
),
],
),
);
}
}
class _AmbientOrb extends StatelessWidget {
const _AmbientOrb({required this.color, required this.size});
final Color color;
final double size;
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [
color,
color.withValues(alpha: 0.12),
color.withValues(alpha: 0),
],
stops: const [0, 0.55, 1],
),
),
),
child: const SizedBox.expand(),
);
}
}
@@ -1,10 +1,13 @@
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../../../core/l10n/l10n.dart';
import '../../../../core/theme/design_tokens.dart';
const homeFloatingHeaderKey = ValueKey('home_floating_header');
const homeFloatingHeaderTitleKey = ValueKey('home_floating_header_title');
const _actionSlotWidth =
(AppSpacing.xxl + AppSpacing.lg) * 2 + AppSpacing.sm + AppSpacing.sm;
class HomeFloatingHeader extends StatelessWidget {
const HomeFloatingHeader({
@@ -33,52 +36,66 @@ class HomeFloatingHeader extends StatelessWidget {
AppSpacing.xs,
),
decoration: BoxDecoration(
color: colorScheme.surface.withValues(alpha: 0.95),
border: Border(bottom: BorderSide(color: colorScheme.outlineVariant)),
color: colorScheme.surface.withValues(alpha: 0.92),
border: Border(
bottom: BorderSide(
color: colorScheme.outlineVariant.withValues(alpha: 0.65),
),
),
boxShadow: [
BoxShadow(
color: colorScheme.shadow.withValues(alpha: 0.04),
blurRadius: AppSpacing.xl,
offset: const Offset(AppSpacing.none, AppSpacing.xs),
),
],
),
child: Stack(
alignment: Alignment.center,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_HeaderIconButton(
SizedBox(
width: _actionSlotWidth,
child: Align(
alignment: Alignment.centerLeft,
child: _HeaderIconButton(
icon: LucideIcons.settings,
onPressed: onTapSettings,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_HeaderIconButton(
icon: LucideIcons.calendar,
onPressed: onTapCalendar,
),
const SizedBox(width: AppSpacing.sm),
_MessagesButton(
unreadCount: unreadCount,
onPressed: onTapMessages,
),
],
),
],
),
),
IgnorePointer(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: AppSpacing.xl * 3),
child: Text(
'Linksy',
key: homeFloatingHeaderTitleKey,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: AppSpacing.lg + (AppSpacing.xs / 2),
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
Expanded(
child: Text(
context.l10n.appTitle,
key: homeFloatingHeaderTitleKey,
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: AppSpacing.lg,
fontWeight: FontWeight.w700,
letterSpacing: 0.2,
color: colorScheme.onSurface,
),
),
),
SizedBox(
width: _actionSlotWidth,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_HeaderIconButton(
icon: LucideIcons.calendar,
onPressed: onTapCalendar,
),
const SizedBox(width: AppSpacing.sm),
_MessagesButton(
unreadCount: unreadCount,
onPressed: onTapMessages,
),
],
),
),
],
),
);
@@ -103,7 +120,11 @@ class _HeaderIconButton extends StatelessWidget {
minHeight: AppSpacing.xxl + AppSpacing.lg,
),
onPressed: onPressed,
icon: Icon(icon, size: AppSpacing.xxl, color: colorScheme.onSurface),
icon: Icon(
icon,
size: AppSpacing.xxl,
color: colorScheme.onSurface.withValues(alpha: 0.95),
),
);
}
}
@@ -145,7 +166,11 @@ class _MessagesButton extends StatelessWidget {
),
decoration: BoxDecoration(
color: colorScheme.error,
borderRadius: BorderRadius.circular(AppSpacing.sm),
borderRadius: BorderRadius.circular(AppSpacing.md),
border: Border.all(
color: colorScheme.surface,
width: AppSpacing.xs / 2,
),
),
constraints: const BoxConstraints(
minWidth: AppSpacing.lg,