refactor(apps): 主题系统迁移至 ColorScheme + 扩展架构并支持 Dark Mode

This commit is contained in:
qzl
2026-03-27 19:07:39 +08:00
parent ecc1ec6ce4
commit ae29a8209b
146 changed files with 4301 additions and 3200 deletions
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
@@ -9,9 +10,9 @@ import '../../../../shared/widgets/app_button.dart';
import '../../../../shared/widgets/app_loading_indicator.dart';
import '../../../../shared/widgets/toast/toast.dart';
import '../../../../shared/widgets/toast/toast_type.dart';
import '../../data/services/settings_user_cache.dart';
import '../../../contacts/data/users/models/user_response.dart';
import '../../../contacts/data/users/users_api.dart';
import '../../../../data/models/user_profile.dart';
import '../../data/services/user_profile_cache_repository.dart';
import '../../data/services/user_profile_service.dart';
import '../widgets/account_section_card.dart';
import '../widgets/settings_page_scaffold.dart';
@@ -25,11 +26,11 @@ class EditProfileScreen extends StatefulWidget {
class _EditProfileScreenState extends State<EditProfileScreen> {
final _usernameController = TextEditingController();
final _bioController = TextEditingController();
final _usersApi = sl<UsersApi>();
final _userCache = sl<SettingsUserCache>();
final _userProfileService = sl<UserProfileService>();
final _userCache = sl<UserProfileCacheRepository>();
final _imagePicker = ImagePicker();
UserResponse? _user;
UserProfile? _user;
File? _selectedAvatar;
bool _isLoading = true;
bool _isSaving = false;
@@ -55,9 +56,9 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
}
try {
final user = await _usersApi.getMe();
final user = await _userProfileService.getMe();
if (mounted) {
_userCache.set(user);
unawaited(_userCache.setCached(user));
setState(() {
_user = user;
_usernameController.text = user.username;
@@ -115,7 +116,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
});
try {
await _usersApi.uploadAvatar(_selectedAvatar!);
await _userProfileService.uploadAvatar(_selectedAvatar!);
if (mounted) {
Toast.show(
context,
@@ -183,8 +184,8 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
username: usernameChanged ? newUsername : null,
bio: bioChanged ? (newBio.isEmpty ? null : newBio) : null,
);
final updatedUser = await _usersApi.updateMe(request);
_userCache.set(updatedUser);
final updatedUser = await _userProfileService.updateMe(request);
unawaited(_userCache.setCached(updatedUser));
}
if (mounted) {
@@ -254,11 +255,10 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
Widget _buildBasicInfoSection() {
final l10n = context.l10n;
final colorScheme = Theme.of(context).colorScheme;
return AccountSectionCard(
title: l10n.settingsEditProfileBasicInfo,
backgroundColor: AppColors.white,
borderColor: AppColors.borderSecondary,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -269,14 +269,14 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: AppColors.slate700,
color: colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.sm),
TextField(
controller: _usernameController,
onChanged: (_) => _onFieldChanged(),
style: const TextStyle(fontSize: 15, color: AppColors.slate900),
style: TextStyle(fontSize: 15, color: colorScheme.onSurface),
decoration: _buildInputDecoration(
l10n.settingsEditProfileUsernameHint,
),
@@ -287,6 +287,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
}
Widget _buildAvatarSection() {
final colorScheme = Theme.of(context).colorScheme;
final avatarUrl = _user?.avatarUrl;
final hasSelectedAvatar = _selectedAvatar != null;
@@ -300,8 +301,8 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.surfaceSecondary,
border: Border.all(color: AppColors.borderTertiary, width: 2),
color: colorScheme.surfaceContainerLow,
border: Border.all(color: colorScheme.outlineVariant, width: 2),
image: hasSelectedAvatar
? DecorationImage(
image: FileImage(_selectedAvatar!),
@@ -315,10 +316,10 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
: null,
),
child: !hasSelectedAvatar && avatarUrl == null
? const Icon(
? Icon(
Icons.person,
size: 40,
color: AppColors.slate400,
color: colorScheme.onSurfaceVariant,
)
: null,
),
@@ -327,16 +328,16 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withValues(alpha: 0.4),
color: colorScheme.scrim.withValues(alpha: 0.4),
),
child: const Center(
child: Center(
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
AppColors.white,
colorScheme.onPrimary,
),
),
),
@@ -351,13 +352,13 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
height: 28,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColors.blue500,
border: Border.all(color: AppColors.white, width: 2),
color: colorScheme.primary,
border: Border.all(color: colorScheme.surface, width: 2),
),
child: const Icon(
child: Icon(
Icons.camera_alt,
size: 14,
color: AppColors.white,
color: colorScheme.onPrimary,
),
),
),
@@ -369,11 +370,10 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
Widget _buildBioSection() {
final l10n = context.l10n;
final colorScheme = Theme.of(context).colorScheme;
return AccountSectionCard(
title: l10n.settingsEditProfileBio,
backgroundColor: AppColors.white,
borderColor: AppColors.borderSecondary,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -382,7 +382,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: AppColors.slate700,
color: colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.sm),
@@ -391,7 +391,7 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
onChanged: (_) => _onFieldChanged(),
maxLines: 4,
maxLength: 200,
style: const TextStyle(fontSize: 15, color: AppColors.slate900),
style: TextStyle(fontSize: 15, color: colorScheme.onSurface),
decoration: _buildInputDecoration(
l10n.settingsEditProfileBioHint,
).copyWith(contentPadding: const EdgeInsets.all(AppSpacing.lg)),
@@ -402,11 +402,13 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
}
InputDecoration _buildInputDecoration(String hintText) {
final colorScheme = Theme.of(context).colorScheme;
return InputDecoration(
hintText: hintText,
hintStyle: const TextStyle(fontSize: 14, color: AppColors.slate400),
hintStyle: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
filled: true,
fillColor: AppColors.surfaceSecondary,
fillColor: colorScheme.surfaceContainerLow,
contentPadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.lg,
vertical: AppSpacing.lg,
@@ -417,11 +419,11 @@ class _EditProfileScreenState extends State<EditProfileScreen> {
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppRadius.lg),
borderSide: const BorderSide(color: AppColors.borderTertiary),
borderSide: BorderSide(color: colorScheme.outlineVariant),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(AppRadius.lg),
borderSide: const BorderSide(color: AppColors.blue500),
borderSide: BorderSide(color: colorScheme.primary),
),
);
}
@@ -49,7 +49,9 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
body: BlocBuilder<AutomationJobsCubit, AutomationJobsState>(
builder: (context, state) {
if (state.isLoading) {
return const Center(child: AppLoadingIndicator());
return const Center(
child: AppLoadingIndicator(variant: AppLoadingVariant.surface),
);
}
if (state.error != null) {
return Center(child: Text(state.error!));
@@ -90,19 +92,21 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
}
Widget _buildEmptyHint(String text) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
width: double.infinity,
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Text(
text,
style: const TextStyle(
color: AppColors.slate500,
style: TextStyle(
color: colorScheme.onSurfaceVariant,
fontSize: 13,
fontWeight: FontWeight.w500,
),
@@ -111,17 +115,21 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
}
Widget _buildSectionTitle(String title) {
final colorScheme = Theme.of(context).colorScheme;
return Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
);
}
Widget _buildJobCard(AutomationJobModel job) {
final colorScheme = Theme.of(context).colorScheme;
return AppPressable(
onTap: () async {
await context.push(AppRoutes.settingsJobDetail(job.id));
@@ -135,9 +143,9 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Row(
children: [
@@ -145,13 +153,13 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
width: AppSpacing.xxl + AppSpacing.lg,
height: AppSpacing.xxl + AppSpacing.lg,
decoration: BoxDecoration(
color: AppColors.surfaceTertiary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: const Icon(
child: Icon(
Icons.auto_awesome,
size: AppSpacing.lg,
color: AppColors.blue500,
color: colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.md),
@@ -161,17 +169,18 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
children: [
Text(
job.title,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.xs),
Text(
_buildSubtitle(job),
style: const TextStyle(
style: TextStyle(
fontSize: 12,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
],
@@ -47,6 +47,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
int _contextWindowCount = 2;
final Set<String> _selectedTools = <String>{'memory.write', 'memory.forget'};
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
@override
void initState() {
super.initState();
@@ -117,8 +119,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
const SizedBox(height: AppSpacing.sm),
Text(
error,
style: const TextStyle(
color: AppColors.error,
style: TextStyle(
color: _colorScheme.error,
fontSize: 13,
fontWeight: FontWeight.w500,
),
@@ -245,23 +247,23 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.white, AppColors.surfaceInfoLight],
gradient: LinearGradient(
colors: [_colorScheme.surface, _colorScheme.surfaceContainerLow],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
job.title,
style: const TextStyle(
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: _colorScheme.onSurface,
),
),
const SizedBox(height: AppSpacing.sm),
@@ -294,14 +296,14 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
vertical: AppSpacing.xs,
),
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Text(
text,
style: const TextStyle(
color: AppColors.slate600,
style: TextStyle(
color: _colorScheme.onSurfaceVariant,
fontSize: 12,
fontWeight: FontWeight.w600,
),
@@ -453,9 +455,9 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
vertical: AppSpacing.md,
),
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Row(
children: [
@@ -465,8 +467,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
children: [
Text(
label,
style: const TextStyle(
color: AppColors.slate500,
style: TextStyle(
color: _colorScheme.onSurfaceVariant,
fontSize: 12,
fontWeight: FontWeight.w500,
),
@@ -474,8 +476,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
const SizedBox(height: AppSpacing.xs),
Text(
value,
style: const TextStyle(
color: AppColors.slate800,
style: TextStyle(
color: _colorScheme.onSurface,
fontSize: 14,
fontWeight: FontWeight.w600,
),
@@ -483,7 +485,10 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
],
),
),
const Icon(Icons.keyboard_arrow_down, color: AppColors.slate400),
Icon(
Icons.keyboard_arrow_down,
color: _colorScheme.onSurfaceVariant,
),
],
),
),
@@ -502,17 +507,17 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
vertical: AppSpacing.md,
),
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Row(
children: [
Expanded(
child: Text(
context.l10n.settingsJobCounterValue(label, value),
style: const TextStyle(
color: AppColors.slate800,
style: TextStyle(
color: _colorScheme.onSurface,
fontSize: 14,
fontWeight: FontWeight.w600,
),
@@ -537,14 +542,18 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
width: AppSpacing.xxl + AppSpacing.md,
height: AppSpacing.xxl + AppSpacing.md,
decoration: BoxDecoration(
color: onTap == null ? AppColors.slate100 : AppColors.surfaceTertiary,
color: onTap == null
? _colorScheme.surfaceContainerHighest
: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Icon(
icon,
size: AppSpacing.lg,
color: onTap == null ? AppColors.slate300 : AppColors.blue500,
color: onTap == null
? _colorScheme.onSurfaceVariant
: _colorScheme.primary,
),
),
);
@@ -573,16 +582,22 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: selected ? AppColors.blue50 : AppColors.white,
color: selected
? _colorScheme.primaryContainer
: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: selected ? AppColors.blue300 : AppColors.borderSecondary,
color: selected
? _colorScheme.primary
: _colorScheme.outlineVariant,
),
),
child: Text(
localizeToolName(toolName),
style: TextStyle(
color: selected ? AppColors.blue600 : AppColors.slate600,
color: selected
? _colorScheme.primary
: _colorScheme.onSurfaceVariant,
fontSize: 12,
fontWeight: FontWeight.w600,
),
@@ -608,9 +623,9 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
return Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -618,7 +633,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
Text(
l10n.settingsJobRunDays,
style: TextStyle(
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
fontSize: 12,
fontWeight: FontWeight.w500,
),
@@ -648,18 +663,22 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: selected ? AppColors.blue50 : AppColors.white,
color: selected
? _colorScheme.primaryContainer
: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: selected
? AppColors.blue300
: AppColors.borderSecondary,
? _colorScheme.primary
: _colorScheme.outlineVariant,
),
),
child: Text(
entry.value,
style: TextStyle(
color: selected ? AppColors.blue600 : AppColors.slate600,
color: selected
? _colorScheme.primary
: _colorScheme.onSurfaceVariant,
fontSize: 12,
fontWeight: FontWeight.w600,
),
@@ -691,14 +710,14 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Text(
text,
style: const TextStyle(
color: AppColors.slate700,
style: TextStyle(
color: _colorScheme.onSurface,
fontSize: 13,
fontWeight: FontWeight.w500,
height: 1.5,
@@ -710,10 +729,10 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
);
}
@@ -722,9 +741,9 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
return Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -742,8 +761,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
children: [
Text(
label,
style: const TextStyle(
color: AppColors.slate500,
style: TextStyle(
color: _colorScheme.onSurfaceVariant,
fontSize: 13,
fontWeight: FontWeight.w500,
),
@@ -753,8 +772,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
child: Text(
value,
textAlign: TextAlign.right,
style: const TextStyle(
color: AppColors.slate800,
style: TextStyle(
color: _colorScheme.onSurface,
fontSize: 13,
fontWeight: FontWeight.w600,
),
@@ -23,6 +23,8 @@ class _MemoryScreenState extends State<MemoryScreen> {
bool _isLoading = true;
String? _error;
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
@override
void initState() {
super.initState();
@@ -81,16 +83,16 @@ class _MemoryScreenState extends State<MemoryScreen> {
return Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.white, AppColors.surfaceInfoLight],
colors: [_colorScheme.surface, _colorScheme.surfaceContainerLow],
),
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: _colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.blue100.withValues(alpha: 0.35),
color: _colorScheme.shadow.withValues(alpha: 0.2),
blurRadius: 14,
offset: const Offset(0, 4),
),
@@ -103,24 +105,27 @@ class _MemoryScreenState extends State<MemoryScreen> {
width: 44,
height: 44,
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.blue100, AppColors.blue50],
colors: [
_colorScheme.primaryContainer,
_colorScheme.surfaceContainerLow,
],
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.45),
color: _colorScheme.primary.withValues(alpha: 0.2),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: const Icon(
child: Icon(
Icons.auto_awesome,
size: 22,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.md),
@@ -133,7 +138,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
color: _colorScheme.onSurface,
),
),
const SizedBox(height: 2),
@@ -142,7 +147,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
),
],
@@ -167,11 +172,18 @@ class _MemoryScreenState extends State<MemoryScreen> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
Icon(
Icons.error_outline,
size: 48,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
_error ?? context.l10n.memoryLoadFailedRetry,
style: TextStyle(fontSize: 14, color: AppColors.slate500),
style: TextStyle(
fontSize: 14,
color: _colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.lg),
AppPressable(
@@ -183,16 +195,16 @@ class _MemoryScreenState extends State<MemoryScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: _colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Text(
context.l10n.memoryReload,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
),
@@ -222,10 +234,10 @@ class _MemoryScreenState extends State<MemoryScreen> {
padding: const EdgeInsets.symmetric(horizontal: AppSpacing.xs),
child: Text(
label,
style: const TextStyle(
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
),
);
@@ -241,16 +253,16 @@ class _MemoryScreenState extends State<MemoryScreen> {
child: Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.white, AppColors.surfaceInfoLight],
colors: [_colorScheme.surface, _colorScheme.surfaceContainerLow],
),
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: _colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate200.withValues(alpha: 0.45),
color: _colorScheme.shadow.withValues(alpha: 0.15),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -269,16 +281,16 @@ class _MemoryScreenState extends State<MemoryScreen> {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.blue100.withValues(alpha: 0.8),
AppColors.blue50.withValues(alpha: 0.8),
_colorScheme.primaryContainer.withValues(alpha: 0.8),
_colorScheme.surfaceContainerLow.withValues(alpha: 0.8),
],
),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
child: Icon(
Icons.person,
size: 20,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.md),
@@ -291,7 +303,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
color: _colorScheme.onSurface,
),
),
const SizedBox(height: 2),
@@ -302,7 +314,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -310,7 +322,11 @@ class _MemoryScreenState extends State<MemoryScreen> {
],
),
),
Icon(Icons.chevron_right, size: 20, color: AppColors.slate400),
Icon(
Icons.chevron_right,
size: 20,
color: _colorScheme.onSurfaceVariant,
),
],
),
if (hasData) ...[
@@ -352,16 +368,16 @@ class _MemoryScreenState extends State<MemoryScreen> {
child: Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.white, AppColors.surfaceTertiary],
colors: [_colorScheme.surface, _colorScheme.surfaceContainerLow],
),
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate200.withValues(alpha: 0.4),
color: _colorScheme.shadow.withValues(alpha: 0.15),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -380,16 +396,16 @@ class _MemoryScreenState extends State<MemoryScreen> {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
AppColors.violet500.withValues(alpha: 0.15),
AppColors.violet500.withValues(alpha: 0.05),
_colorScheme.tertiary.withValues(alpha: 0.15),
_colorScheme.tertiary.withValues(alpha: 0.05),
],
),
borderRadius: BorderRadius.circular(10),
),
child: const Icon(
child: Icon(
Icons.work_outline,
size: 20,
color: AppColors.violet600,
color: _colorScheme.tertiary,
),
),
const SizedBox(width: AppSpacing.md),
@@ -402,7 +418,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
color: _colorScheme.onSurface,
),
),
const SizedBox(height: 2),
@@ -413,7 +429,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -421,7 +437,11 @@ class _MemoryScreenState extends State<MemoryScreen> {
],
),
),
Icon(Icons.chevron_right, size: 20, color: AppColors.slate400),
Icon(
Icons.chevron_right,
size: 20,
color: _colorScheme.onSurfaceVariant,
),
],
),
if (hasData) ...[
@@ -467,19 +487,23 @@ class _MemoryScreenState extends State<MemoryScreen> {
right: index < icons.length - 1 ? AppSpacing.sm : 0,
),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Column(
children: [
Icon(icons[index], size: 16, color: AppColors.slate400),
Icon(
icons[index],
size: 16,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(height: 4),
Text(
values[index],
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.slate700,
color: _colorScheme.onSurface,
),
),
const SizedBox(height: 2),
@@ -488,7 +512,7 @@ class _MemoryScreenState extends State<MemoryScreen> {
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: AppColors.slate400,
color: _colorScheme.onSurfaceVariant,
),
),
],
@@ -1,11 +1,13 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:social_app/core/constants/app_constants.dart';
import 'package:social_app/core/config/env.dart';
import 'package:social_app/app/di/injection.dart';
import 'package:social_app/app/router/app_routes.dart';
import 'package:social_app/core/l10n/l10n.dart';
import 'package:social_app/core/theme/design_tokens.dart';
import 'package:social_app/core/auth/session_controller.dart';
import 'package:social_app/data/models/user_profile.dart';
import 'package:social_app/data/repositories/friend_repository.dart';
import 'package:social_app/shared/widgets/app_button.dart';
import 'package:social_app/shared/widgets/app_loading_indicator.dart';
import 'package:social_app/shared/widgets/app_pressable.dart';
@@ -13,15 +15,10 @@ import 'package:social_app/shared/widgets/destructive_action_sheet.dart';
import 'package:social_app/shared/widgets/toast/toast.dart';
import 'package:social_app/shared/widgets/toast/toast_type.dart';
import 'package:social_app/core/utils/phone_display_formatter.dart';
import 'package:social_app/features/auth/presentation/bloc/auth_bloc.dart';
import 'package:social_app/features/auth/presentation/bloc/auth_event.dart';
import 'package:social_app/features/auth/presentation/bloc/auth_state.dart';
import 'package:social_app/features/contacts/data/friends_api.dart';
import 'package:social_app/features/settings/data/settings_api.dart';
import 'package:social_app/features/settings/data/services/automation_jobs_api.dart';
import 'package:social_app/features/settings/data/services/settings_user_cache.dart';
import 'package:social_app/features/contacts/data/users/models/user_response.dart';
import 'package:social_app/features/home/presentation/navigation/home_return_policy.dart';
import 'package:social_app/features/settings/data/services/user_profile_cache_repository.dart';
import 'package:social_app/app/router/home_return_policy.dart';
import '../widgets/settings_page_scaffold.dart';
const settingsProfileEditButtonKey = ValueKey('settings_profile_edit_button');
@@ -35,11 +32,13 @@ class SettingsScreen extends StatefulWidget {
}
class _SettingsScreenState extends State<SettingsScreen> {
final FriendsApi _friendsApi = sl<FriendsApi>();
final FriendRepository _friendRepository = sl<FriendRepository>();
final SessionController _sessionController = sl<SessionController>();
final AutomationJobsApi _automationJobsApi = sl<AutomationJobsApi>();
final SettingsUserCache _userCache = sl<SettingsUserCache>();
final UserProfileCacheRepository _userCache =
sl<UserProfileCacheRepository>();
UserResponse? _user;
UserProfile? _user;
bool _isLoading = true;
int _friendsCount = 0;
String? _firstFriendName;
@@ -75,14 +74,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
try {
final friends = await _friendsApi.getFriends();
final friends = await _friendRepository.getFriends();
if (mounted) {
setState(() {
_friendsCount = friends.length;
_firstFriendName = friends.isNotEmpty
? friends.first.friend.username
: null;
_firstFriendName = friends.isNotEmpty ? friends.first.username : null;
});
}
} catch (e) {
@@ -128,13 +125,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
Widget _buildProfileHero() {
final colorScheme = Theme.of(context).colorScheme;
if (_isLoading) {
return Container(
width: double.infinity,
height: 120,
padding: const EdgeInsets.all(AppSpacing.xl),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xxl),
),
child: const Center(child: AppLoadingIndicator(size: 22)),
@@ -151,16 +150,16 @@ class _SettingsScreenState extends State<SettingsScreen> {
width: double.infinity,
padding: const EdgeInsets.all(AppSpacing.xl),
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.white, AppColors.surfaceInfoLight],
colors: [colorScheme.surface, colorScheme.surfaceContainerLow],
),
borderRadius: BorderRadius.circular(AppRadius.xxl),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.blue100.withValues(alpha: 0.35),
color: colorScheme.shadow.withValues(alpha: 0.2),
blurRadius: 14,
offset: const Offset(0, 4),
),
@@ -179,12 +178,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
borderRadius: BorderRadius.circular(32),
boxShadow: [
BoxShadow(
color: Color.fromRGBO(
AppColors.blue400.r.toInt(),
AppColors.blue400.g.toInt(),
AppColors.blue400.b.toInt(),
0.2,
),
color: colorScheme.primary.withValues(alpha: 0.2),
blurRadius: 12,
offset: const Offset(0, 4),
),
@@ -201,10 +195,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [
Text(
username,
style: const TextStyle(
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
overflow: TextOverflow.ellipsis,
),
@@ -214,7 +208,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
],
@@ -235,17 +229,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(
Icon(
Icons.edit,
size: 14,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: 3),
Container(
width: 12,
height: 1.5,
decoration: BoxDecoration(
color: AppColors.slate400,
color: colorScheme.onSurfaceVariant,
borderRadius: BorderRadius.circular(
AppRadius.full,
),
@@ -267,37 +261,47 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
Widget _buildFreeBadge() {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [AppColors.blue50, AppColors.surfaceInfoLight],
colors: [
colorScheme.primaryContainer,
colorScheme.surfaceContainerLow,
],
),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColors.borderQuaternary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Text(
context.l10n.settingsFreeBadge,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: AppColors.blue600,
color: colorScheme.primary,
),
),
);
}
Widget _buildAvatarImage(String? avatarUrl) {
final colorScheme = Theme.of(context).colorScheme;
if (avatarUrl == null) {
return Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.blue100, AppColors.blue50],
colors: [
colorScheme.primaryContainer,
colorScheme.surfaceContainerLow,
],
),
),
child: const Icon(Icons.person, size: 28, color: AppColors.blue600),
child: Icon(Icons.person, size: 28, color: colorScheme.primary),
);
}
return Image.network(
@@ -307,33 +311,39 @@ class _SettingsScreenState extends State<SettingsScreen> {
height: 64,
errorBuilder: (context, error, stackTrace) {
return Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.blue100, AppColors.blue50],
colors: [
colorScheme.primaryContainer,
colorScheme.surfaceContainerLow,
],
),
),
child: const Icon(Icons.person, size: 28, color: AppColors.blue600),
child: Icon(Icons.person, size: 28, color: colorScheme.primary),
);
},
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Container(
decoration: const BoxDecoration(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.blue100, AppColors.blue50],
colors: [
colorScheme.primaryContainer,
colorScheme.surfaceContainerLow,
],
),
),
child: const Center(
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(AppColors.blue600),
valueColor: AlwaysStoppedAnimation<Color>(colorScheme.primary),
),
),
),
@@ -374,7 +384,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded(
child: _buildActionCard(
icon: Icons.people,
iconColor: AppColors.blue500,
title: context.l10n.contactsTitle,
subtitle: _buildFriendsSubtitle(),
onTap: () => context.push(AppRoutes.contactsList),
@@ -384,7 +393,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded(
child: _buildActionCard(
icon: Icons.auto_awesome,
iconColor: AppColors.blue500,
title: context.l10n.settingsFeaturesTitle,
subtitle: _buildAutomationSubtitle(),
onTap: () => context.push(AppRoutes.settingsFeatures),
@@ -396,11 +404,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
Widget _buildActionCard({
required IconData icon,
required Color iconColor,
required String title,
required String subtitle,
required VoidCallback onTap,
}) {
final colorScheme = Theme.of(context).colorScheme;
return AppPressable(
onTap: onTap,
borderRadius: BorderRadius.circular(AppRadius.xl),
@@ -408,12 +417,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
constraints: const BoxConstraints(minHeight: 136),
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate200.withValues(alpha: 0.45),
color: colorScheme.shadow.withValues(alpha: 0.15),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -428,22 +437,26 @@ class _SettingsScreenState extends State<SettingsScreen> {
width: 36,
height: 36,
decoration: BoxDecoration(
color: AppColors.surfaceTertiary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, size: 18, color: iconColor),
child: Icon(icon, size: 18, color: colorScheme.primary),
),
const Spacer(),
Icon(Icons.chevron_right, size: 16, color: AppColors.slate300),
Icon(
Icons.chevron_right,
size: 16,
color: colorScheme.onSurfaceVariant,
),
],
),
const SizedBox(height: AppSpacing.md),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 2),
@@ -452,7 +465,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -464,19 +477,21 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
Widget _buildSubscriptionCard() {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.white, AppColors.surfaceInfoLight],
colors: [colorScheme.surface, colorScheme.surfaceContainerLow],
),
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
boxShadow: [
BoxShadow(
color: AppColors.slate200.withValues(alpha: 0.4),
color: colorScheme.shadow.withValues(alpha: 0.15),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -491,21 +506,24 @@ class _SettingsScreenState extends State<SettingsScreen> {
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [AppColors.blue100, AppColors.blue50],
colors: [
colorScheme.primaryContainer,
colorScheme.surfaceContainerLow,
],
),
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: AppColors.blue200.withValues(alpha: 0.45),
color: colorScheme.primary.withValues(alpha: 0.2),
blurRadius: 6,
offset: const Offset(0, 1),
),
],
),
child: const Icon(
child: Icon(
Icons.workspace_premium,
size: 22,
color: AppColors.blue600,
color: colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.md),
@@ -518,7 +536,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
const SizedBox(height: 2),
@@ -527,7 +545,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
],
@@ -536,13 +554,16 @@ class _SettingsScreenState extends State<SettingsScreen> {
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.blue500, AppColors.blue600],
gradient: LinearGradient(
colors: [
colorScheme.primary,
colorScheme.primary.withValues(alpha: 0.85),
],
),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: const Color(0x4D60A5FA),
color: colorScheme.primary.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -553,7 +574,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: AppColors.white,
color: colorScheme.onPrimary,
),
),
),
@@ -563,11 +584,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
Widget _buildMenuCard(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
children: [
@@ -586,7 +609,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
_buildMenuItem(
icon: Icons.system_update,
title: context.l10n.settingsMenuCheckUpdates,
trailing: 'v${AppConstants.version}',
trailing: 'v${Env.version}',
onTap: _checkForUpdates,
),
],
@@ -600,6 +623,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
String? trailing,
required VoidCallback onTap,
}) {
final colorScheme = Theme.of(context).colorScheme;
return AppPressable(
onTap: onTap,
borderRadius: BorderRadius.circular(AppRadius.md),
@@ -611,14 +636,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [
Row(
children: [
Icon(icon, size: 20, color: AppColors.slate500),
Icon(icon, size: 20, color: colorScheme.onSurfaceVariant),
const SizedBox(width: 10),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
],
@@ -628,17 +653,17 @@ class _SettingsScreenState extends State<SettingsScreen> {
if (trailing != null) ...[
Text(
trailing,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
color: AppColors.slate400,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(width: 6),
],
const Icon(
Icon(
Icons.chevron_right,
size: 18,
color: AppColors.slate400,
color: colorScheme.onSurfaceVariant,
),
],
),
@@ -649,10 +674,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
}
Widget _buildDivider() {
final colorScheme = Theme.of(context).colorScheme;
return Container(
height: 1,
margin: const EdgeInsets.symmetric(horizontal: 14),
color: AppColors.slate100,
color: colorScheme.outlineVariant,
);
}
@@ -679,13 +706,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
return;
}
_userCache.invalidate();
final authBloc = context.read<AuthBloc>();
authBloc.add(AuthLoggedOut());
await _userCache.invalidate();
if (!mounted) {
return;
}
try {
await authBloc.stream
.firstWhere((state) => state is AuthUnauthenticated)
.timeout(const Duration(seconds: 5));
await _sessionController.logoutAndWaitUnauthenticated();
} catch (_) {
if (!mounted) return;
Toast.show(
@@ -703,8 +729,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
try {
final settingsApi = sl<SettingsApi>();
final result = await settingsApi.checkUpdates(
currentVersionCode: AppConstants.build,
currentVersionName: AppConstants.version,
currentVersionCode: Env.build,
currentVersionName: Env.version,
platform: 'android',
);
@@ -26,6 +26,8 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
String? _error;
bool _hasChanges = false;
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
@override
void initState() {
super.initState();
@@ -130,11 +132,15 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
Icon(
Icons.error_outline,
size: 48,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
_error ?? context.l10n.memoryLoadFailedRetry,
style: TextStyle(color: AppColors.slate500),
style: TextStyle(color: _colorScheme.onSurfaceVariant),
),
const SizedBox(height: AppSpacing.lg),
AppPressable(
@@ -146,16 +152,16 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: _colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Text(
context.l10n.memoryReload,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
),
@@ -170,11 +176,15 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.person_off_outlined, size: 48, color: AppColors.slate300),
Icon(
Icons.person_off_outlined,
size: 48,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
context.l10n.settingsUserMemoryEmptyProfile,
style: TextStyle(color: AppColors.slate500),
style: TextStyle(color: _colorScheme.onSurfaceVariant),
),
],
),
@@ -191,13 +201,13 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
child: Container(
height: 52,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.blue500, AppColors.blue600],
gradient: LinearGradient(
colors: [_colorScheme.primary, _colorScheme.primary],
),
borderRadius: BorderRadius.circular(AppRadius.lg),
boxShadow: [
BoxShadow(
color: AppColors.blue500.withValues(alpha: 0.3),
color: _colorScheme.primary.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -211,7 +221,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.white,
color: _colorScheme.onPrimary,
),
),
),
@@ -298,9 +308,9 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -327,7 +337,11 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
borderRadius: BorderRadius.circular(AppRadius.sm),
child: Container(
padding: const EdgeInsets.all(AppSpacing.xs),
child: Icon(Icons.close, size: 18, color: AppColors.slate400),
child: Icon(
Icons.close,
size: 18,
color: _colorScheme.onSurfaceVariant,
),
),
),
],
@@ -416,9 +430,9 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -445,7 +459,11 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
borderRadius: BorderRadius.circular(AppRadius.sm),
child: Container(
padding: const EdgeInsets.all(AppSpacing.xs),
child: Icon(Icons.close, size: 18, color: AppColors.slate400),
child: Icon(
Icons.close,
size: 18,
color: _colorScheme.onSurfaceVariant,
),
),
),
],
@@ -613,9 +631,9 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -649,7 +667,11 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
borderRadius: BorderRadius.circular(AppRadius.sm),
child: Container(
padding: const EdgeInsets.all(AppSpacing.xs),
child: Icon(Icons.close, size: 18, color: AppColors.slate400),
child: Icon(
Icons.close,
size: 18,
color: _colorScheme.onSurfaceVariant,
),
),
),
],
@@ -706,14 +728,14 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
children: [
Row(
children: [
Icon(icon, size: 18, color: AppColors.blue500),
Icon(icon, size: 18, color: _colorScheme.primary),
const SizedBox(width: AppSpacing.sm),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: AppColors.slate800,
color: _colorScheme.onSurface,
),
),
if (count != null) ...[
@@ -724,15 +746,15 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
vertical: 2,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: _colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Text(
'$count',
style: const TextStyle(
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
),
@@ -758,20 +780,20 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.xs),
Container(
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: TextFormField(
initialValue: value,
onChanged: onChanged,
style: const TextStyle(fontSize: 14, color: AppColors.slate800),
style: TextStyle(fontSize: 14, color: _colorScheme.onSurface),
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
@@ -779,7 +801,10 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
),
border: InputBorder.none,
hintText: context.l10n.settingsMemoryInputHint(label),
hintStyle: TextStyle(color: AppColors.slate400, fontSize: 14),
hintStyle: TextStyle(
color: _colorScheme.onSurfaceVariant,
fontSize: 14,
),
),
),
),
@@ -791,14 +816,14 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
return Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Center(
child: Text(
message,
style: TextStyle(fontSize: 14, color: AppColors.slate400),
style: TextStyle(fontSize: 14, color: _colorScheme.onSurfaceVariant),
),
),
);
@@ -823,18 +848,18 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: _colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
entry.value,
style: const TextStyle(
style: TextStyle(
fontSize: 13,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
const SizedBox(width: AppSpacing.xs),
@@ -844,7 +869,7 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
child: Icon(
Icons.close,
size: 14,
color: AppColors.blue400,
color: _colorScheme.primary,
),
),
],
@@ -860,21 +885,28 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: AppColors.borderSecondary,
color: _colorScheme.outlineVariant,
style: BorderStyle.solid,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, size: 14, color: AppColors.slate500),
Icon(
Icons.add,
size: 14,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(width: AppSpacing.xs),
Text(
context.l10n.contactsAdd,
style: TextStyle(fontSize: 13, color: AppColors.slate500),
style: TextStyle(
fontSize: 13,
color: _colorScheme.onSurfaceVariant,
),
),
],
),
@@ -925,24 +957,24 @@ class _UserMemoryDetailScreenState extends State<UserMemoryDetailScreen> {
child: Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: AppColors.borderSecondary,
color: _colorScheme.outlineVariant,
style: BorderStyle.solid,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.add, size: 18, color: AppColors.blue500),
Icon(Icons.add, size: 18, color: _colorScheme.primary),
const SizedBox(width: AppSpacing.xs),
Text(
text,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
],
@@ -94,16 +94,22 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
}
Widget _buildErrorState() {
final colorScheme = Theme.of(context).colorScheme;
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
Icon(
Icons.error_outline,
size: 48,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
_error ?? context.l10n.memoryLoadFailedRetry,
style: TextStyle(color: AppColors.slate500),
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
const SizedBox(height: AppSpacing.lg),
AppPressable(
@@ -115,16 +121,16 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Text(
context.l10n.memoryReload,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: colorScheme.primary,
),
),
),
@@ -269,12 +275,14 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
required IconData icon,
required List<Widget> children,
}) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -286,18 +294,18 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
width: 28,
height: 28,
decoration: BoxDecoration(
color: AppColors.blue50,
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(icon, size: 16, color: AppColors.blue600),
child: Icon(icon, size: 16, color: colorScheme.primary),
),
const SizedBox(width: AppSpacing.sm),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
],
@@ -315,11 +323,13 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
String value, {
bool multiline = false,
}) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Row(
@@ -327,7 +337,7 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [
Icon(icon, size: 16, color: AppColors.slate500),
Icon(icon, size: 16, color: colorScheme.onSurfaceVariant),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Column(
@@ -338,16 +348,16 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 2),
Text(
value,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.slate800,
color: colorScheme.onSurface,
),
),
],
@@ -359,6 +369,8 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
}
Widget _buildPeople(List<Person> people) {
final colorScheme = Theme.of(context).colorScheme;
if (people.isEmpty) {
return _buildEmptyTip(context.l10n.settingsUserMemoryEmptyContacts);
}
@@ -369,9 +381,9 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -410,6 +422,8 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
}
Widget _buildPlaces(List<Place> places) {
final colorScheme = Theme.of(context).colorScheme;
if (places.isEmpty) {
return _buildEmptyTip(context.l10n.settingsUserMemoryEmptyPlaces);
}
@@ -420,9 +434,9 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -455,6 +469,8 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
}
Widget _buildRoutines(List<RecurringRoutine> routines) {
final colorScheme = Theme.of(context).colorScheme;
if (routines.isEmpty) {
return _buildEmptyTip(context.l10n.settingsUserMemoryEmptyRoutines);
}
@@ -465,9 +481,9 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -496,6 +512,8 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
}
Widget _buildTags(List<String> tags) {
final colorScheme = Theme.of(context).colorScheme;
if (tags.isEmpty) {
return _buildEmptyTip(context.l10n.memoryNoInfo);
}
@@ -509,22 +527,22 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.label_outline, size: 14, color: AppColors.blue500),
Icon(Icons.label_outline, size: 14, color: colorScheme.primary),
const SizedBox(width: AppSpacing.xs),
Text(
tag,
style: const TextStyle(
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: colorScheme.onPrimaryContainer,
),
),
],
@@ -535,24 +553,30 @@ class _UserMemoryViewScreenState extends State<UserMemoryViewScreen> {
}
Widget _buildEmptyTip(String text) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.inbox_outlined, size: 16, color: AppColors.slate400),
Icon(
Icons.inbox_outlined,
size: 16,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(width: AppSpacing.sm),
Text(
text,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
],
@@ -26,6 +26,8 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
String? _error;
bool _hasChanges = false;
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
@override
void initState() {
super.initState();
@@ -130,11 +132,15 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
Icon(
Icons.error_outline,
size: 48,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
_error ?? context.l10n.memoryLoadFailedRetry,
style: TextStyle(color: AppColors.slate500),
style: TextStyle(color: _colorScheme.onSurfaceVariant),
),
const SizedBox(height: AppSpacing.lg),
AppPressable(
@@ -146,16 +152,16 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: _colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Text(
context.l10n.memoryReload,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: _colorScheme.primary,
),
),
),
@@ -170,11 +176,15 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.work_off_outlined, size: 48, color: AppColors.slate300),
Icon(
Icons.work_off_outlined,
size: 48,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
context.l10n.settingsWorkMemoryEmptyProfile,
style: TextStyle(color: AppColors.slate500),
style: TextStyle(color: _colorScheme.onSurfaceVariant),
),
],
),
@@ -191,13 +201,13 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
child: Container(
height: 52,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [AppColors.blue500, AppColors.blue600],
gradient: LinearGradient(
colors: [_colorScheme.primary, _colorScheme.primary],
),
borderRadius: BorderRadius.circular(AppRadius.lg),
boxShadow: [
BoxShadow(
color: AppColors.blue500.withValues(alpha: 0.3),
color: _colorScheme.primary.withValues(alpha: 0.3),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -211,7 +221,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.white,
color: _colorScheme.onPrimary,
),
),
),
@@ -338,9 +348,9 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -374,7 +384,11 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
borderRadius: BorderRadius.circular(AppRadius.sm),
child: Container(
padding: const EdgeInsets.all(AppSpacing.xs),
child: Icon(Icons.close, size: 18, color: AppColors.slate400),
child: Icon(
Icons.close,
size: 18,
color: _colorScheme.onSurfaceVariant,
),
),
),
],
@@ -475,9 +489,9 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -506,7 +520,11 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
borderRadius: BorderRadius.circular(AppRadius.sm),
child: Container(
padding: const EdgeInsets.all(AppSpacing.xs),
child: Icon(Icons.close, size: 18, color: AppColors.slate400),
child: Icon(
Icons.close,
size: 18,
color: _colorScheme.onSurfaceVariant,
),
),
),
],
@@ -649,14 +667,14 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
children: [
Row(
children: [
Icon(icon, size: 18, color: AppColors.violet500),
Icon(icon, size: 18, color: _colorScheme.tertiary),
const SizedBox(width: AppSpacing.sm),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: AppColors.slate800,
color: _colorScheme.onSurface,
),
),
if (count != null) ...[
@@ -667,7 +685,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
vertical: 2,
),
decoration: BoxDecoration(
color: AppColors.violet500.withValues(alpha: 0.1),
color: _colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Text(
@@ -675,7 +693,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.violet600,
color: _colorScheme.tertiary,
),
),
),
@@ -701,20 +719,20 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: _colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: AppSpacing.xs),
Container(
decoration: BoxDecoration(
color: AppColors.white,
color: _colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: TextFormField(
initialValue: value,
onChanged: onChanged,
style: const TextStyle(fontSize: 14, color: AppColors.slate800),
style: TextStyle(fontSize: 14, color: _colorScheme.onSurface),
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
@@ -722,7 +740,10 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
),
border: InputBorder.none,
hintText: context.l10n.settingsMemoryInputHint(label),
hintStyle: TextStyle(color: AppColors.slate400, fontSize: 14),
hintStyle: TextStyle(
color: _colorScheme.onSurfaceVariant,
fontSize: 14,
),
),
),
),
@@ -734,14 +755,14 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
return Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: _colorScheme.outlineVariant),
),
child: Center(
child: Text(
message,
style: TextStyle(fontSize: 14, color: AppColors.slate400),
style: TextStyle(fontSize: 14, color: _colorScheme.onSurfaceVariant),
),
),
);
@@ -766,10 +787,10 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.violet500.withValues(alpha: 0.1),
color: _colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: AppColors.violet500.withValues(alpha: 0.3),
color: _colorScheme.tertiary.withValues(alpha: 0.3),
),
),
child: Row(
@@ -779,7 +800,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
entry.value,
style: TextStyle(
fontSize: 13,
color: AppColors.violet600,
color: _colorScheme.tertiary,
),
),
const SizedBox(width: AppSpacing.xs),
@@ -789,7 +810,7 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
child: Icon(
Icons.close,
size: 14,
color: AppColors.violet500,
color: _colorScheme.tertiary,
),
),
],
@@ -805,21 +826,28 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: AppColors.borderSecondary,
color: _colorScheme.outlineVariant,
style: BorderStyle.solid,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.add, size: 14, color: AppColors.slate500),
Icon(
Icons.add,
size: 14,
color: _colorScheme.onSurfaceVariant,
),
const SizedBox(width: AppSpacing.xs),
Text(
context.l10n.contactsAdd,
style: TextStyle(fontSize: 13, color: AppColors.slate500),
style: TextStyle(
fontSize: 13,
color: _colorScheme.onSurfaceVariant,
),
),
],
),
@@ -870,24 +898,24 @@ class _WorkMemoryDetailScreenState extends State<WorkMemoryDetailScreen> {
child: Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: _colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(
color: AppColors.borderSecondary,
color: _colorScheme.outlineVariant,
style: BorderStyle.solid,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.add, size: 18, color: AppColors.violet500),
Icon(Icons.add, size: 18, color: _colorScheme.tertiary),
const SizedBox(width: AppSpacing.xs),
Text(
text,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.violet600,
color: _colorScheme.tertiary,
),
),
],
@@ -94,16 +94,22 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
}
Widget _buildErrorState() {
final colorScheme = Theme.of(context).colorScheme;
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error_outline, size: 48, color: AppColors.slate300),
Icon(
Icons.error_outline,
size: 48,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(height: AppSpacing.md),
Text(
_error ?? context.l10n.memoryLoadFailedRetry,
style: TextStyle(color: AppColors.slate500),
style: TextStyle(color: colorScheme.onSurfaceVariant),
),
const SizedBox(height: AppSpacing.lg),
AppPressable(
@@ -115,16 +121,16 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.blue50,
color: colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.blue100),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Text(
context.l10n.memoryReload,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.blue600,
color: colorScheme.primary,
),
),
),
@@ -249,12 +255,14 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
required IconData icon,
required List<Widget> children,
}) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(AppSpacing.lg),
decoration: BoxDecoration(
color: AppColors.white,
color: colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: AppColors.borderSecondary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -266,18 +274,18 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
width: 28,
height: 28,
decoration: BoxDecoration(
color: AppColors.violet500.withValues(alpha: 0.1),
color: colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Icon(icon, size: 16, color: AppColors.violet600),
child: Icon(icon, size: 16, color: colorScheme.tertiary),
),
const SizedBox(width: AppSpacing.sm),
Text(
title,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
],
@@ -295,11 +303,13 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
String value, {
bool multiline = false,
}) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: Row(
@@ -307,7 +317,7 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [
Icon(icon, size: 16, color: AppColors.slate500),
Icon(icon, size: 16, color: colorScheme.onSurfaceVariant),
const SizedBox(width: AppSpacing.sm),
Expanded(
child: Column(
@@ -318,16 +328,16 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 2),
Text(
value,
style: const TextStyle(
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: AppColors.slate800,
color: colorScheme.onSurface,
),
),
],
@@ -339,6 +349,8 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
}
Widget _buildProjects(List<CurrentProject> projects) {
final colorScheme = Theme.of(context).colorScheme;
if (projects.isEmpty) {
return _buildEmptyTip(context.l10n.settingsWorkMemoryEmptyProjects);
}
@@ -349,9 +361,9 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -411,6 +423,8 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
}
Widget _buildTeamMembers(List<TeamMember> members) {
final colorScheme = Theme.of(context).colorScheme;
if (members.isEmpty) {
return _buildEmptyTip(context.l10n.settingsWorkMemoryEmptyTeamMembers);
}
@@ -421,9 +435,9 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -461,6 +475,8 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
}
Widget _buildTags(List<String> tags) {
final colorScheme = Theme.of(context).colorScheme;
if (tags.isEmpty) {
return _buildEmptyTip(context.l10n.memoryNoInfo);
}
@@ -474,24 +490,24 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: AppColors.violet500.withValues(alpha: 0.1),
color: colorScheme.tertiaryContainer,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: AppColors.violet500.withValues(alpha: 0.25),
color: colorScheme.tertiary.withValues(alpha: 0.25),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.label_outline, size: 14, color: AppColors.violet500),
Icon(Icons.label_outline, size: 14, color: colorScheme.tertiary),
const SizedBox(width: AppSpacing.xs),
Text(
tag,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.violet600,
color: colorScheme.onTertiaryContainer,
),
),
],
@@ -502,24 +518,30 @@ class _WorkMemoryViewScreenState extends State<WorkMemoryViewScreen> {
}
Widget _buildEmptyTip(String text) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.surfaceSecondary,
color: colorScheme.surfaceContainerLow,
borderRadius: BorderRadius.circular(AppRadius.md),
border: Border.all(color: AppColors.borderTertiary),
border: Border.all(color: colorScheme.outlineVariant),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.inbox_outlined, size: 16, color: AppColors.slate400),
Icon(
Icons.inbox_outlined,
size: 16,
color: colorScheme.onSurfaceVariant,
),
const SizedBox(width: AppSpacing.sm),
Text(
text,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
],
@@ -8,27 +8,29 @@ class AccountSectionCard extends StatelessWidget {
this.title,
this.description,
required this.child,
this.backgroundColor = AppColors.white,
this.borderColor = AppColors.borderSecondary,
this.backgroundColor,
this.borderColor,
this.contentPadding = const EdgeInsets.all(AppSpacing.lg),
});
final String? title;
final String? description;
final Widget child;
final Color backgroundColor;
final Color borderColor;
final Color? backgroundColor;
final Color? borderColor;
final EdgeInsetsGeometry contentPadding;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Container(
width: double.infinity,
padding: contentPadding,
decoration: BoxDecoration(
color: backgroundColor,
color: backgroundColor ?? colorScheme.surface,
borderRadius: BorderRadius.circular(AppRadius.xl),
border: Border.all(color: borderColor),
border: Border.all(color: borderColor ?? colorScheme.outlineVariant),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -36,10 +38,10 @@ class AccountSectionCard extends StatelessWidget {
if (title != null) ...[
Text(
title!,
style: const TextStyle(
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: AppColors.slate900,
color: colorScheme.onSurface,
),
),
],
@@ -47,10 +49,10 @@ class AccountSectionCard extends StatelessWidget {
const SizedBox(height: AppSpacing.xs),
Text(
description!,
style: const TextStyle(
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: AppColors.slate500,
color: colorScheme.onSurfaceVariant,
),
),
],
@@ -25,8 +25,10 @@ class SettingsPageScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: AppColors.surfaceSecondary,
backgroundColor: colorScheme.surfaceContainerLow,
resizeToAvoidBottomInset: resizeOnKeyboard,
body: SafeArea(
maintainBottomViewPadding: maintainBottomViewPadding,