chore: 更新国际化翻译及 UI 组件优化
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user