refactor(apps): 主题系统迁移至 ColorScheme + 扩展架构并支持 Dark Mode
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../contacts/data/users/models/user_response.dart';
|
||||
import 'user_profile_cache_repository.dart';
|
||||
|
||||
class SettingsUserCache {
|
||||
final UserProfileCacheRepository _repository;
|
||||
|
||||
SettingsUserCache(this._repository);
|
||||
|
||||
UserResponse? _cachedUser;
|
||||
|
||||
UserResponse? get cachedUser => _cachedUser;
|
||||
|
||||
Future<UserResponse> getProfile({bool forceRefresh = false}) async {
|
||||
final user = await _repository.getProfile(forceRefresh: forceRefresh);
|
||||
_cachedUser = user;
|
||||
return user;
|
||||
}
|
||||
|
||||
void set(UserResponse user) {
|
||||
_cachedUser = user;
|
||||
unawaited(_repository.setCached(user));
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
_cachedUser = null;
|
||||
unawaited(_repository.invalidate());
|
||||
}
|
||||
}
|
||||
@@ -1,83 +1,67 @@
|
||||
import 'dart:async';
|
||||
import '../../../../data/cache/cache_policy.dart';
|
||||
import '../../../../data/cache/cached_repository.dart';
|
||||
import '../../../../data/models/user_profile.dart';
|
||||
|
||||
import '../../../../core/cache/cache_entry.dart';
|
||||
import '../../../../core/cache/cache_policy.dart';
|
||||
import '../../../../core/cache/hybrid_cache_store.dart';
|
||||
import '../../../contacts/data/users/models/user_response.dart';
|
||||
|
||||
class UserProfileCacheRepository {
|
||||
class UserProfileCacheRepository extends CachedRepository<UserProfile> {
|
||||
static const String cacheKey = 'settings:user_profile';
|
||||
|
||||
final HybridCacheStore store;
|
||||
final CachePolicy policy;
|
||||
final DateTime Function() now;
|
||||
final Future<UserResponse> Function() remoteLoader;
|
||||
|
||||
Future<void>? _refreshInFlight;
|
||||
final Future<UserProfile> Function() remoteLoader;
|
||||
UserProfile? _cachedUser;
|
||||
int _generation = 0;
|
||||
|
||||
UserProfileCacheRepository({
|
||||
required this.store,
|
||||
required super.store,
|
||||
required this.remoteLoader,
|
||||
CachePolicy? policy,
|
||||
DateTime Function()? now,
|
||||
}) : policy =
|
||||
policy ??
|
||||
const CachePolicy(
|
||||
softTtl: Duration(minutes: 2),
|
||||
hardTtl: Duration(minutes: 30),
|
||||
minRefreshInterval: Duration(minutes: 1),
|
||||
),
|
||||
now = now ?? DateTime.now;
|
||||
super.now,
|
||||
}) : super(
|
||||
policy:
|
||||
policy ??
|
||||
const CachePolicy(
|
||||
softTtl: Duration(minutes: 2),
|
||||
hardTtl: Duration(minutes: 30),
|
||||
minRefreshInterval: Duration(minutes: 1),
|
||||
),
|
||||
);
|
||||
|
||||
Future<UserResponse> getProfile({bool forceRefresh = false}) async {
|
||||
if (forceRefresh) {
|
||||
return _refreshAndRead();
|
||||
}
|
||||
UserProfile? get cachedUser => _cachedUser;
|
||||
|
||||
final cached = await store.read<CacheEntry<UserResponse>>(cacheKey);
|
||||
if (cached == null) {
|
||||
return _refreshAndRead();
|
||||
}
|
||||
|
||||
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
||||
if (decision.shouldRefreshInBackground) {
|
||||
_refreshInBackground();
|
||||
}
|
||||
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
||||
return _refreshAndRead();
|
||||
}
|
||||
return cached.value;
|
||||
}
|
||||
|
||||
Future<void> setCached(UserResponse user) {
|
||||
return store.write<CacheEntry<UserResponse>>(
|
||||
cacheKey,
|
||||
CacheEntry<UserResponse>(value: user, fetchedAt: now()),
|
||||
Future<UserProfile> getProfile({bool forceRefresh = false}) async {
|
||||
final generation = _generation;
|
||||
final user = await getOrLoad(
|
||||
key: cacheKey,
|
||||
forceRefresh: forceRefresh,
|
||||
loadFromRemote: _loadAndRemember,
|
||||
shouldWriteLoaded: (_) => generation == _generation,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> invalidate() => store.remove(cacheKey);
|
||||
|
||||
void _refreshInBackground() {
|
||||
final running = _refreshInFlight;
|
||||
if (running != null) {
|
||||
return;
|
||||
if (generation == _generation) {
|
||||
_cachedUser = user;
|
||||
}
|
||||
final task = _refreshAndWrite().whenComplete(() {
|
||||
_refreshInFlight = null;
|
||||
});
|
||||
_refreshInFlight = task;
|
||||
unawaited(task);
|
||||
return user;
|
||||
}
|
||||
|
||||
Future<UserResponse> _refreshAndRead() async {
|
||||
await _refreshAndWrite();
|
||||
final cached = await store.read<CacheEntry<UserResponse>>(cacheKey);
|
||||
return cached!.value;
|
||||
Future<void> setCached(UserProfile user) async {
|
||||
final generation = _generation;
|
||||
_cachedUser = user;
|
||||
await writeCacheEntry(cacheKey, user);
|
||||
if (generation != _generation) {
|
||||
_cachedUser = null;
|
||||
await removeCacheKey(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _refreshAndWrite() async {
|
||||
Future<void> invalidate() async {
|
||||
_generation += 1;
|
||||
_cachedUser = null;
|
||||
await removeCacheKey(cacheKey);
|
||||
}
|
||||
|
||||
Future<UserProfile> _loadAndRemember() async {
|
||||
final generation = _generation;
|
||||
final remote = await remoteLoader();
|
||||
await setCached(remote);
|
||||
if (generation == _generation) {
|
||||
_cachedUser = remote;
|
||||
}
|
||||
return remote;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../../../../core/network/i_api_client.dart';
|
||||
import '../../../../data/models/user_profile.dart';
|
||||
|
||||
class UserProfileService {
|
||||
static const _prefix = '/api/v1/users';
|
||||
|
||||
final IApiClient _client;
|
||||
|
||||
UserProfileService(this._client);
|
||||
|
||||
Future<UserProfile> getMe() async {
|
||||
final response = await _client.get<Map<String, dynamic>>('$_prefix/me');
|
||||
final data = response.data;
|
||||
if (data == null) {
|
||||
throw StateError('Invalid getMe response: empty payload');
|
||||
}
|
||||
return UserProfile.fromJson(data);
|
||||
}
|
||||
|
||||
Future<UserProfile> updateMe(UserUpdateRequest request) async {
|
||||
final response = await _client.patch<Map<String, dynamic>>(
|
||||
'$_prefix/me',
|
||||
data: request.toJson(),
|
||||
);
|
||||
final data = response.data;
|
||||
if (data == null) {
|
||||
throw StateError('Invalid updateMe response: empty payload');
|
||||
}
|
||||
return UserProfile.fromJson(data);
|
||||
}
|
||||
|
||||
Future<String> uploadAvatar(File file) async {
|
||||
final formData = FormData.fromMap({
|
||||
'file': await MultipartFile.fromFile(
|
||||
file.path,
|
||||
filename: file.path.split('/').last,
|
||||
),
|
||||
});
|
||||
final response = await _client.post<Map<String, dynamic>>(
|
||||
'$_prefix/me/avatar',
|
||||
data: formData,
|
||||
);
|
||||
final data = response.data;
|
||||
if (data == null || data['url'] is! String) {
|
||||
throw StateError('Invalid uploadAvatar response: missing url');
|
||||
}
|
||||
return data['url'] as String;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user