update theme
This commit is contained in:
@@ -36,8 +36,10 @@ class AccountPage extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
body: SafeArea(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
@@ -48,7 +50,7 @@ class AccountPage extends ConsumerWidget {
|
||||
child: Column(
|
||||
children: [
|
||||
// Simple Header
|
||||
_buildHeader(),
|
||||
_buildHeader(context),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// User Profile Card - only this depends on provider
|
||||
@@ -76,26 +78,28 @@ class AccountPage extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build simple header with title
|
||||
Widget _buildHeader() {
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'Tài khoản',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -103,14 +107,16 @@ class AccountPage extends ConsumerWidget {
|
||||
|
||||
/// Build account menu section
|
||||
Widget _buildAccountMenu(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -158,14 +164,14 @@ class AccountPage extends ConsumerWidget {
|
||||
context.push(RouteNames.changePassword);
|
||||
},
|
||||
),
|
||||
AccountMenuItem(
|
||||
icon: FontAwesomeIcons.language,
|
||||
title: 'Ngôn ngữ',
|
||||
subtitle: 'Tiếng Việt',
|
||||
onTap: () {
|
||||
_showComingSoon(context);
|
||||
},
|
||||
),
|
||||
// AccountMenuItem(
|
||||
// icon: FontAwesomeIcons.language,
|
||||
// title: 'Ngôn ngữ',
|
||||
// subtitle: 'Tiếng Việt',
|
||||
// onTap: () {
|
||||
// _showComingSoon(context);
|
||||
// },
|
||||
// ),
|
||||
AccountMenuItem(
|
||||
icon: FontAwesomeIcons.palette,
|
||||
title: 'Giao diện',
|
||||
@@ -181,14 +187,16 @@ class AccountPage extends ConsumerWidget {
|
||||
|
||||
/// Build support section
|
||||
Widget _buildSupportSection(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -198,8 +206,8 @@ class AccountPage extends ConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Section title
|
||||
const Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpacing.md,
|
||||
AppSpacing.md,
|
||||
AppSpacing.md,
|
||||
@@ -210,7 +218,7 @@ class AccountPage extends ConsumerWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -220,10 +228,10 @@ class AccountPage extends ConsumerWidget {
|
||||
icon: FontAwesomeIcons.headset,
|
||||
title: 'Liên hệ hỗ trợ',
|
||||
subtitle: 'Hotline: 1900 1234',
|
||||
trailing: const FaIcon(
|
||||
trailing: FaIcon(
|
||||
FontAwesomeIcons.phone,
|
||||
size: 18,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onTap: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -283,7 +291,7 @@ class AccountPage extends ConsumerWidget {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Ứng dụng dành cho thầu thợ, kiến trúc sư, đại lý và môi giới trong ngành gạch ốp lát và nội thất.',
|
||||
style: TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -307,27 +315,28 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final userInfoAsync = ref.watch(userInfoProvider);
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
child: userInfoAsync.when(
|
||||
loading: () => _buildLoadingCard(),
|
||||
error: (error, stack) => _buildErrorCard(context, ref, error),
|
||||
data: (userInfo) => _buildProfileCard(context, userInfo),
|
||||
loading: () => _buildLoadingCard(colorScheme),
|
||||
error: (error, stack) => _buildErrorCard(context, ref, error, colorScheme),
|
||||
data: (userInfo) => _buildProfileCard(context, userInfo, colorScheme),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadingCard() {
|
||||
Widget _buildLoadingCard(ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -339,13 +348,13 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
child: const Center(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
@@ -359,7 +368,7 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
height: 20,
|
||||
width: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
@@ -368,7 +377,7 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
height: 14,
|
||||
width: 100,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
),
|
||||
@@ -380,15 +389,15 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorCard(BuildContext context, WidgetRef ref, Object error) {
|
||||
Widget _buildErrorCard(BuildContext context, WidgetRef ref, Object error, ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -399,9 +408,9 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.grey100,
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
),
|
||||
child: const Center(
|
||||
child: FaIcon(
|
||||
@@ -416,22 +425,22 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Không thể tải thông tin',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () => ref.read(userInfoProvider.notifier).refresh(),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'Nhấn để thử lại',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
@@ -444,15 +453,15 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProfileCard(BuildContext context, domain.UserInfo userInfo) {
|
||||
Widget _buildProfileCard(BuildContext context, domain.UserInfo userInfo, ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -471,17 +480,13 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
placeholder: (context, url) => Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFF005B9A), Color(0xFF38B6FF)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
color: colorScheme.primaryContainer,
|
||||
),
|
||||
child: const Center(
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
color: colorScheme.onPrimaryContainer,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
@@ -489,19 +494,15 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
errorWidget: (context, url, error) => Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFF005B9A), Color(0xFF38B6FF)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
color: colorScheme.primaryContainer,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
userInfo.initials,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
style: TextStyle(
|
||||
color: colorScheme.onPrimaryContainer,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
@@ -513,19 +514,15 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
: Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
gradient: LinearGradient(
|
||||
colors: [Color(0xFF005B9A), Color(0xFF38B6FF)],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
color: colorScheme.primaryContainer,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
userInfo.initials,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
style: TextStyle(
|
||||
color: colorScheme.onPrimaryContainer,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
@@ -541,27 +538,27 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
userInfo.fullName,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
'${_getRoleDisplayName(userInfo.role)} · Hạng ${userInfo.tierDisplayName}',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (userInfo.phoneNumber != null) ...[
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
Text(
|
||||
userInfo.phoneNumber!,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -594,12 +591,14 @@ class _ProfileCardSection extends ConsumerWidget {
|
||||
class _LogoutButton extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () {
|
||||
_showLogoutConfirmation(context, ref);
|
||||
_showLogoutConfirmation(context, ref, colorScheme);
|
||||
},
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowRightFromBracket, size: 18),
|
||||
label: const Text('Đăng xuất'),
|
||||
@@ -616,12 +615,19 @@ class _LogoutButton extends ConsumerWidget {
|
||||
}
|
||||
|
||||
/// Show logout confirmation dialog
|
||||
void _showLogoutConfirmation(BuildContext context, WidgetRef ref) {
|
||||
void _showLogoutConfirmation(BuildContext context, WidgetRef ref, ColorScheme colorScheme) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Đăng xuất'),
|
||||
content: const Text('Bạn có chắc chắn muốn đăng xuất?'),
|
||||
backgroundColor: colorScheme.surface,
|
||||
title: Text(
|
||||
'Đăng xuất',
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
),
|
||||
content: Text(
|
||||
'Bạn có chắc chắn muốn đăng xuất?',
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
|
||||
@@ -32,6 +32,8 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// Form key for validation
|
||||
final formKey = useMemoized(() => GlobalKey<FormState>());
|
||||
|
||||
@@ -89,32 +91,32 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: AppColors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: AppBarSpecs.elevation,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(
|
||||
icon: FaIcon(
|
||||
FontAwesomeIcons.arrowLeft,
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: Text(
|
||||
address == null ? 'Thêm địa chỉ mới' : 'Chỉnh sửa địa chỉ',
|
||||
style: const TextStyle(color: Colors.black),
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
),
|
||||
foregroundColor: AppColors.grey900,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const FaIcon(
|
||||
icon: FaIcon(
|
||||
FontAwesomeIcons.circleInfo,
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => _showInfoDialog(context),
|
||||
onPressed: () => _showInfoDialog(context, colorScheme),
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
],
|
||||
@@ -136,10 +138,12 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
children: [
|
||||
// Contact Information Section
|
||||
_buildSection(
|
||||
colorScheme: colorScheme,
|
||||
icon: FontAwesomeIcons.user,
|
||||
title: 'Thông tin liên hệ',
|
||||
children: [
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
controller: nameController,
|
||||
label: 'Họ và tên',
|
||||
icon: FontAwesomeIcons.user,
|
||||
@@ -154,6 +158,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
controller: phoneController,
|
||||
label: 'Số điện thoại',
|
||||
icon: FontAwesomeIcons.phone,
|
||||
@@ -173,6 +178,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
controller: emailController,
|
||||
label: 'Email',
|
||||
icon: FontAwesomeIcons.envelope,
|
||||
@@ -190,6 +196,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
controller: taxIdController,
|
||||
label: 'Mã số thuế',
|
||||
icon: FontAwesomeIcons.fileInvoice,
|
||||
@@ -203,10 +210,12 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
// Address Information Section
|
||||
_buildSection(
|
||||
colorScheme: colorScheme,
|
||||
icon: FontAwesomeIcons.locationDot,
|
||||
title: 'Địa chỉ giao hàng',
|
||||
children: [
|
||||
_buildDropdownWithLoading(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Tỉnh/Thành phố',
|
||||
value: selectedCityCode.value,
|
||||
items: citiesMap,
|
||||
@@ -226,6 +235,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildDropdownWithLoading(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Quận/Huyện',
|
||||
value: selectedWardCode.value,
|
||||
items: wardsMap,
|
||||
@@ -246,6 +256,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
if (citiesAsync.hasError) ...[
|
||||
const SizedBox(height: 8),
|
||||
_buildErrorBanner(
|
||||
colorScheme,
|
||||
'Không thể tải danh sách tỉnh/thành phố. Vui lòng thử lại.',
|
||||
),
|
||||
],
|
||||
@@ -253,11 +264,13 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
if (wardsAsync.hasError && selectedCityCode.value != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
_buildErrorBanner(
|
||||
colorScheme,
|
||||
'Không thể tải danh sách quận/huyện. Vui lòng thử lại.',
|
||||
),
|
||||
],
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
_buildTextArea(
|
||||
colorScheme: colorScheme,
|
||||
controller: addressDetailController,
|
||||
label: 'Địa chỉ cụ thể',
|
||||
placeholder: 'Số nhà, tên đường, khu vực...',
|
||||
@@ -279,11 +292,11 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -303,31 +316,31 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
value: isDefault.value,
|
||||
onChanged: (value) =>
|
||||
isDefault.value = value ?? false,
|
||||
activeColor: AppColors.primaryBlue,
|
||||
activeColor: colorScheme.primary,
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
Text(
|
||||
'Đặt làm địa chỉ mặc định',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 32),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 32),
|
||||
child: Text(
|
||||
'Địa chỉ này sẽ được sử dụng làm mặc định khi đặt hàng',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -401,15 +414,15 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
vertical: AppSpacing.md,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Colors.grey.withValues(alpha: 0.15),
|
||||
color: colorScheme.outlineVariant,
|
||||
),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.08),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.08),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, -4),
|
||||
),
|
||||
@@ -437,12 +450,12 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
isSaving,
|
||||
),
|
||||
icon: isSaving.value
|
||||
? const SizedBox(
|
||||
? SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
color: colorScheme.onPrimary,
|
||||
),
|
||||
)
|
||||
: const FaIcon(FontAwesomeIcons.floppyDisk, size: 18),
|
||||
@@ -454,9 +467,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: AppColors.grey500,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
disabledBackgroundColor: colorScheme.onSurfaceVariant,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -475,6 +488,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
/// Build a section with icon and title
|
||||
Widget _buildSection({
|
||||
required ColorScheme colorScheme,
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required List<Widget> children,
|
||||
@@ -482,11 +496,11 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -500,15 +514,15 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
FaIcon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -522,6 +536,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
/// Build a text field with label and icon
|
||||
Widget _buildTextField({
|
||||
required ColorScheme colorScheme,
|
||||
required TextEditingController controller,
|
||||
required String label,
|
||||
required IconData icon,
|
||||
@@ -538,10 +553,10 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (isRequired)
|
||||
@@ -561,13 +576,13 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
validator: validator,
|
||||
decoration: InputDecoration(
|
||||
hintText: placeholder,
|
||||
hintStyle: const TextStyle(color: AppColors.grey500),
|
||||
hintStyle: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 12),
|
||||
child: FaIcon(
|
||||
icon,
|
||||
size: 16,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
prefixIconConstraints: const BoxConstraints(
|
||||
@@ -575,19 +590,19 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
minHeight: 0,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
fillColor: colorScheme.surface,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -612,9 +627,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
helperText,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -624,6 +639,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
/// Build a dropdown field
|
||||
Widget _buildDropdown({
|
||||
required ColorScheme colorScheme,
|
||||
required String label,
|
||||
required String? value,
|
||||
required Map<String, String> items,
|
||||
@@ -639,10 +655,10 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (isRequired)
|
||||
@@ -662,23 +678,23 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
validator: validator,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: enabled ? Colors.white : const Color(0xFFF3F4F6),
|
||||
fillColor: enabled ? colorScheme.surface : colorScheme.surfaceContainerHighest,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -700,7 +716,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
hint: Text(
|
||||
'-- Chọn $label --',
|
||||
style: const TextStyle(color: AppColors.grey500),
|
||||
style: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
items: enabled
|
||||
? () {
|
||||
@@ -709,9 +725,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
value: entry.key,
|
||||
child: Text(
|
||||
entry.value,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -725,9 +741,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
value: value,
|
||||
child: Text(
|
||||
'$value (đã lưu)',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
@@ -742,7 +758,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
icon: FaIcon(
|
||||
FontAwesomeIcons.chevronDown,
|
||||
size: 14,
|
||||
color: enabled ? AppColors.grey500 : AppColors.grey500.withValues(alpha: 0.5),
|
||||
color: enabled ? colorScheme.onSurfaceVariant : colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -751,6 +767,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
/// Build a dropdown field with loading indicator
|
||||
Widget _buildDropdownWithLoading({
|
||||
required ColorScheme colorScheme,
|
||||
required String label,
|
||||
required String? value,
|
||||
required Map<String, String> items,
|
||||
@@ -767,10 +784,10 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (isRequired)
|
||||
@@ -783,12 +800,12 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
),
|
||||
if (isLoading) ...[
|
||||
const SizedBox(width: 8),
|
||||
const SizedBox(
|
||||
SizedBox(
|
||||
width: 12,
|
||||
height: 12,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -801,23 +818,23 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
validator: validator,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: enabled && !isLoading ? Colors.white : const Color(0xFFF3F4F6),
|
||||
fillColor: enabled && !isLoading ? colorScheme.surface : colorScheme.surfaceContainerHighest,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -837,14 +854,14 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
vertical: 14,
|
||||
),
|
||||
suffixIcon: isLoading
|
||||
? const Padding(
|
||||
padding: EdgeInsets.only(right: 12),
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -857,7 +874,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
? 'Vui lòng chọn Tỉnh/Thành phố trước'
|
||||
: '-- Chọn $label --',
|
||||
style: TextStyle(
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontSize: 14,
|
||||
fontStyle: !enabled || isLoading ? FontStyle.italic : FontStyle.normal,
|
||||
),
|
||||
@@ -869,9 +886,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
value: entry.key,
|
||||
child: Text(
|
||||
entry.value,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -885,9 +902,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
value: value,
|
||||
child: Text(
|
||||
'$value (đã lưu)',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
@@ -903,8 +920,8 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
FontAwesomeIcons.chevronDown,
|
||||
size: 14,
|
||||
color: enabled && !isLoading
|
||||
? AppColors.grey500
|
||||
: AppColors.grey500.withValues(alpha: 0.5),
|
||||
? colorScheme.onSurfaceVariant
|
||||
: colorScheme.onSurfaceVariant.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -913,6 +930,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
|
||||
/// Build a text area field
|
||||
Widget _buildTextArea({
|
||||
required ColorScheme colorScheme,
|
||||
required TextEditingController controller,
|
||||
required String label,
|
||||
required String placeholder,
|
||||
@@ -927,10 +945,10 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
if (isRequired)
|
||||
@@ -950,21 +968,21 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
validator: validator,
|
||||
decoration: InputDecoration(
|
||||
hintText: placeholder,
|
||||
hintStyle: const TextStyle(color: AppColors.grey500),
|
||||
hintStyle: TextStyle(color: colorScheme.onSurfaceVariant),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
fillColor: colorScheme.surface,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -986,9 +1004,9 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
helperText,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -997,7 +1015,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build error banner for API failures
|
||||
Widget _buildErrorBanner(String message) {
|
||||
Widget _buildErrorBanner(ColorScheme colorScheme, String message) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
@@ -1032,7 +1050,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
/// Show info dialog
|
||||
void _showInfoDialog(BuildContext context) {
|
||||
void _showInfoDialog(BuildContext context, ColorScheme colorScheme) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
@@ -1132,7 +1150,7 @@ class AddressFormPage extends HookConsumerWidget {
|
||||
Text(address == null ? 'Đã thêm địa chỉ thành công!' : 'Đã cập nhật địa chỉ thành công!'),
|
||||
],
|
||||
),
|
||||
backgroundColor: const Color(0xFF10B981),
|
||||
backgroundColor: AppColors.success,
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -42,30 +42,32 @@ class AddressesPage extends HookConsumerWidget {
|
||||
// Watch addresses from API
|
||||
final addressesAsync = ref.watch(addressesProvider);
|
||||
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: AppColors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: AppBarSpecs.elevation,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(
|
||||
icon: FaIcon(
|
||||
FontAwesomeIcons.arrowLeft,
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: Text(
|
||||
selectMode ? 'Chọn địa chỉ' : 'Địa chỉ của bạn',
|
||||
style: const TextStyle(color: Colors.black),
|
||||
style: TextStyle(color: colorScheme.onSurface),
|
||||
),
|
||||
foregroundColor: AppColors.grey900,
|
||||
foregroundColor: colorScheme.onSurface,
|
||||
centerTitle: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const FaIcon(
|
||||
icon: FaIcon(
|
||||
FontAwesomeIcons.circleInfo,
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
size: 20,
|
||||
),
|
||||
onPressed: () {
|
||||
@@ -85,7 +87,7 @@ class AddressesPage extends HookConsumerWidget {
|
||||
await ref.read(addressesProvider.notifier).refresh();
|
||||
},
|
||||
child: addresses.isEmpty
|
||||
? _buildEmptyState(context)
|
||||
? _buildEmptyState(context, colorScheme)
|
||||
: ListView.separated(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
itemCount: addresses.length,
|
||||
@@ -168,9 +170,9 @@ class AddressesPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primaryBlue,
|
||||
side: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
foregroundColor: colorScheme.primary,
|
||||
side: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 1.5,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
@@ -205,10 +207,10 @@ class AddressesPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: AppColors.grey100,
|
||||
disabledForegroundColor: AppColors.grey500,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
disabledBackgroundColor: colorScheme.surfaceContainerHighest,
|
||||
disabledForegroundColor: colorScheme.onSurfaceVariant,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -236,8 +238,8 @@ class AddressesPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -262,18 +264,18 @@ class AddressesPage extends HookConsumerWidget {
|
||||
color: AppColors.danger,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Không thể tải danh sách địa chỉ',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
error.toString(),
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -284,8 +286,8 @@ class AddressesPage extends HookConsumerWidget {
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 18),
|
||||
label: const Text('Thử lại'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -296,7 +298,7 @@ class AddressesPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build empty state
|
||||
Widget _buildEmptyState(BuildContext context) {
|
||||
Widget _buildEmptyState(BuildContext context, ColorScheme colorScheme) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -304,15 +306,15 @@ class AddressesPage extends HookConsumerWidget {
|
||||
FaIcon(
|
||||
FontAwesomeIcons.locationDot,
|
||||
size: 64,
|
||||
color: AppColors.grey500.withValues(alpha: 0.4),
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.4),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
Text(
|
||||
'Chưa có địa chỉ nào',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -320,7 +322,7 @@ class AddressesPage extends HookConsumerWidget {
|
||||
'Thêm địa chỉ để nhận hàng nhanh hơn',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500.withValues(alpha: 0.8),
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
@@ -334,8 +336,8 @@ class AddressesPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -357,20 +359,20 @@ class AddressesPage extends HookConsumerWidget {
|
||||
ref.read(addressesProvider.notifier).setDefaultAddress(address.name);
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
const SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
const FaIcon(
|
||||
FaIcon(
|
||||
FontAwesomeIcons.circleCheck,
|
||||
color: Colors.white,
|
||||
size: 18,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
const Text('Đã đặt làm địa chỉ mặc định'),
|
||||
SizedBox(width: 12),
|
||||
Text('Đã đặt làm địa chỉ mặc định'),
|
||||
],
|
||||
),
|
||||
backgroundColor: const Color(0xFF10B981),
|
||||
duration: const Duration(seconds: 2),
|
||||
backgroundColor: AppColors.success,
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -460,7 +462,7 @@ class AddressesPage extends HookConsumerWidget {
|
||||
Text('Đã xóa địa chỉ'),
|
||||
],
|
||||
),
|
||||
backgroundColor: Color(0xFF10B981),
|
||||
backgroundColor: AppColors.success,
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -63,19 +63,21 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
};
|
||||
}, []);
|
||||
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Thay đổi mật khẩu',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -95,11 +97,11 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -109,12 +111,12 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Title
|
||||
const Text(
|
||||
Text(
|
||||
'Cập nhật mật khẩu',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -122,6 +124,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
|
||||
// Current Password
|
||||
_buildPasswordField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Mật khẩu hiện tại',
|
||||
controller: currentPasswordController,
|
||||
isVisible: currentPasswordVisible,
|
||||
@@ -138,6 +141,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
|
||||
// New Password
|
||||
_buildPasswordField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Mật khẩu mới',
|
||||
controller: newPasswordController,
|
||||
isVisible: newPasswordVisible,
|
||||
@@ -158,6 +162,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
|
||||
// Confirm Password
|
||||
_buildPasswordField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Nhập lại mật khẩu mới',
|
||||
controller: confirmPasswordController,
|
||||
isVisible: confirmPasswordVisible,
|
||||
@@ -206,7 +211,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
|
||||
// Security Tips
|
||||
_buildSecurityTips(),
|
||||
_buildSecurityTips(colorScheme),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -216,6 +221,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
// Action Buttons
|
||||
_buildActionButtons(
|
||||
context: context,
|
||||
colorScheme: colorScheme,
|
||||
formKey: formKey,
|
||||
currentPasswordController: currentPasswordController,
|
||||
newPasswordController: newPasswordController,
|
||||
@@ -232,6 +238,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
|
||||
/// Build password field with show/hide toggle
|
||||
Widget _buildPasswordField({
|
||||
required ColorScheme colorScheme,
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
required ValueNotifier<bool> isVisible,
|
||||
@@ -245,10 +252,10 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
children: [
|
||||
if (required)
|
||||
@@ -267,11 +274,11 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Nhập $label',
|
||||
hintStyle: TextStyle(
|
||||
color: AppColors.grey500.withValues(alpha: 0.6),
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.6),
|
||||
fontSize: 14,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFF8FAFC),
|
||||
fillColor: colorScheme.surfaceContainerLowest,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
@@ -280,7 +287,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
icon: FaIcon(
|
||||
isVisible.value ? FontAwesomeIcons.eyeSlash : FontAwesomeIcons.eye,
|
||||
size: 18,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
onPressed: () {
|
||||
isVisible.value = !isVisible.value;
|
||||
@@ -288,16 +295,16 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -315,7 +322,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
helpText,
|
||||
style: const TextStyle(fontSize: 12, color: AppColors.grey500),
|
||||
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
],
|
||||
@@ -323,38 +330,38 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
/// Build security tips section
|
||||
Widget _buildSecurityTips() {
|
||||
Widget _buildSecurityTips(ColorScheme colorScheme) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8FAFC),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(color: const Color(0xFFE2E8F0)),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
Text(
|
||||
'Gợi ý bảo mật:',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildSecurityTip('Sử dụng ít nhất 8 ký tự'),
|
||||
_buildSecurityTip('Kết hợp chữ hoa, chữ thường và số'),
|
||||
_buildSecurityTip('Bao gồm ký tự đặc biệt (!@#\$%^&*)'),
|
||||
_buildSecurityTip('Không sử dụng thông tin cá nhân'),
|
||||
_buildSecurityTip('Thường xuyên thay đổi mật khẩu'),
|
||||
_buildSecurityTip('Sử dụng ít nhất 8 ký tự', colorScheme),
|
||||
_buildSecurityTip('Kết hợp chữ hoa, chữ thường và số', colorScheme),
|
||||
_buildSecurityTip('Bao gồm ký tự đặc biệt (!@#\$%^&*)', colorScheme),
|
||||
_buildSecurityTip('Không sử dụng thông tin cá nhân', colorScheme),
|
||||
_buildSecurityTip('Thường xuyên thay đổi mật khẩu', colorScheme),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Build individual security tip
|
||||
Widget _buildSecurityTip(String text) {
|
||||
Widget _buildSecurityTip(String text, ColorScheme colorScheme) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Row(
|
||||
@@ -369,9 +376,9 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF475569),
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
@@ -384,6 +391,7 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
/// Build action buttons
|
||||
Widget _buildActionButtons({
|
||||
required BuildContext context,
|
||||
required ColorScheme colorScheme,
|
||||
required GlobalKey<FormState> formKey,
|
||||
required TextEditingController currentPasswordController,
|
||||
required TextEditingController newPasswordController,
|
||||
@@ -401,17 +409,17 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
},
|
||||
style: OutlinedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
side: const BorderSide(color: AppColors.grey100),
|
||||
side: BorderSide(color: colorScheme.outlineVariant),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.button),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
child: Text(
|
||||
'Hủy bỏ',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -438,8 +446,8 @@ class ChangePasswordPage extends HookConsumerWidget {
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
|
||||
@@ -31,6 +31,8 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
// Watch user info from API
|
||||
final userInfoAsync = ref.watch(userInfoProvider);
|
||||
|
||||
@@ -45,31 +47,31 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
return userInfoAsync.when(
|
||||
loading: () => Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Thông tin cá nhân',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
centerTitle: false,
|
||||
),
|
||||
body: const Center(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircularProgressIndicator(color: AppColors.primaryBlue),
|
||||
SizedBox(height: AppSpacing.md),
|
||||
CircularProgressIndicator(color: colorScheme.primary),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
Text(
|
||||
'Đang tải thông tin...',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -77,18 +79,18 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
error: (error, stack) => Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Thông tin cá nhân',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -105,11 +107,12 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
color: AppColors.danger,
|
||||
),
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
const Text(
|
||||
Text(
|
||||
'Không thể tải thông tin người dùng',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
@@ -118,8 +121,8 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 16),
|
||||
label: const Text('Thử lại'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: AppColors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -183,12 +186,12 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
}
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: const Color(0xFFF4F6F8),
|
||||
backgroundColor: colorScheme.surfaceContainerLowest,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.surface,
|
||||
elevation: 0,
|
||||
leading: IconButton(
|
||||
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
|
||||
icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20),
|
||||
onPressed: () async {
|
||||
if (hasChanges.value) {
|
||||
final shouldPop = await _showUnsavedChangesDialog(context);
|
||||
@@ -200,10 +203,10 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
title: const Text(
|
||||
title: Text(
|
||||
'Thông tin cá nhân',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
color: colorScheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
@@ -224,6 +227,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
// Profile Avatar Section with Name and Status
|
||||
_buildAvatarAndStatusSection(
|
||||
context,
|
||||
colorScheme,
|
||||
selectedImage,
|
||||
userInfo.initials,
|
||||
userInfo.avatarUrl,
|
||||
@@ -240,11 +244,11 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -253,13 +257,13 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
child: TabBar(
|
||||
controller: tabController,
|
||||
indicator: BoxDecoration(
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
),
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
dividerColor: Colors.transparent,
|
||||
labelColor: Colors.white,
|
||||
unselectedLabelColor: AppColors.grey500,
|
||||
labelColor: colorScheme.onPrimary,
|
||||
unselectedLabelColor: colorScheme.onSurfaceVariant,
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
@@ -278,6 +282,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
// Tab 1: Personal Information (always show if no tabs, or when selected)
|
||||
_buildPersonalInformationTab(
|
||||
ref: ref,
|
||||
colorScheme: colorScheme,
|
||||
nameController: nameController,
|
||||
phoneController: phoneController,
|
||||
emailController: emailController,
|
||||
@@ -298,6 +303,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
_buildVerificationTab(
|
||||
ref: ref,
|
||||
context: context,
|
||||
colorScheme: colorScheme,
|
||||
idCardFrontImage: idCardFrontImage,
|
||||
idCardBackImage: idCardBackImage,
|
||||
certificateImages: certificateImages,
|
||||
@@ -329,6 +335,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
/// Build Personal Information Tab
|
||||
Widget _buildPersonalInformationTab({
|
||||
required WidgetRef ref,
|
||||
required ColorScheme colorScheme,
|
||||
required TextEditingController nameController,
|
||||
required TextEditingController phoneController,
|
||||
required TextEditingController emailController,
|
||||
@@ -351,11 +358,11 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -365,20 +372,20 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Section Header
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.circleUser,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Thông tin cá nhân',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -388,6 +395,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
// Full Name
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Họ và tên',
|
||||
controller: nameController,
|
||||
required: true,
|
||||
@@ -403,6 +411,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
// Phone (Read-only)
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Số điện thoại',
|
||||
controller: phoneController,
|
||||
readOnly: true,
|
||||
@@ -412,6 +421,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
// Email (Read-only)
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Email',
|
||||
controller: emailController,
|
||||
readOnly: true,
|
||||
@@ -422,6 +432,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
// Birth Date
|
||||
_buildDateField(
|
||||
context: context,
|
||||
colorScheme: colorScheme,
|
||||
label: 'Ngày sinh',
|
||||
controller: birthDateController,
|
||||
hasChanges: hasChanges,
|
||||
@@ -431,6 +442,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
// Gender
|
||||
_buildDropdownField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Giới tính',
|
||||
value: selectedGender.value,
|
||||
items: const [
|
||||
@@ -450,6 +462,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
// Company Name
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Tên công ty/Cửa hàng',
|
||||
controller: companyController,
|
||||
),
|
||||
@@ -458,6 +471,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
// Tax ID
|
||||
_buildTextField(
|
||||
colorScheme: colorScheme,
|
||||
label: 'Mã số thuế',
|
||||
controller: taxIdController,
|
||||
),
|
||||
@@ -472,15 +486,15 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEFF6FF),
|
||||
color: colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(color: Colors.blue),
|
||||
border: Border.all(color: colorScheme.primary),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const FaIcon(
|
||||
FaIcon(
|
||||
FontAwesomeIcons.circleInfo,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -489,7 +503,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
'Để thay đổi số điện thoại, email hoặc vai trò, vui lòng liên hệ bộ phận hỗ trợ.',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.primaryBlue.withValues(alpha: 0.9),
|
||||
color: colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -521,8 +535,8 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
certificateImages: certificateImages.value,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -549,6 +563,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
Widget _buildVerificationTab({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required ColorScheme colorScheme,
|
||||
required ValueNotifier<File?> idCardFrontImage,
|
||||
required ValueNotifier<File?> idCardBackImage,
|
||||
required ValueNotifier<List<File>> certificateImages,
|
||||
@@ -574,10 +589,10 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: isVerified
|
||||
? const Color(0xFFF0FDF4) // Green for verified
|
||||
: const Color(0xFFEFF6FF), // Blue for not verified
|
||||
: colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
border: Border.all(
|
||||
color: isVerified ? const Color(0xFFBBF7D0) : Colors.blue,
|
||||
color: isVerified ? const Color(0xFFBBF7D0) : colorScheme.primary,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
@@ -586,7 +601,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
isVerified
|
||||
? FontAwesomeIcons.circleCheck
|
||||
: FontAwesomeIcons.circleInfo,
|
||||
color: isVerified ? AppColors.success : AppColors.primaryBlue,
|
||||
color: isVerified ? AppColors.success : colorScheme.primary,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
@@ -599,7 +614,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
fontSize: 12,
|
||||
color: isVerified
|
||||
? const Color(0xFF166534)
|
||||
: AppColors.primaryBlue.withValues(alpha: 0.9),
|
||||
: colorScheme.onPrimaryContainer,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
@@ -615,11 +630,11 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -629,20 +644,20 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Section Header
|
||||
const Row(
|
||||
Row(
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.fileCircleCheck,
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
size: 20,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Thông tin xác thực',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -651,17 +666,18 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
const Divider(height: 32),
|
||||
|
||||
// ID Card Front Upload
|
||||
const Text(
|
||||
Text(
|
||||
'Ảnh mặt trước CCCD/CMND',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildUploadCard(
|
||||
context: context,
|
||||
colorScheme: colorScheme,
|
||||
icon: FontAwesomeIcons.camera,
|
||||
title: 'Chụp ảnh hoặc chọn file',
|
||||
subtitle: 'JPG, PNG tối đa 5MB',
|
||||
@@ -675,17 +691,18 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// ID Card Back Upload
|
||||
const Text(
|
||||
Text(
|
||||
'Ảnh mặt sau CCCD/CMND',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildUploadCard(
|
||||
context: context,
|
||||
colorScheme: colorScheme,
|
||||
icon: FontAwesomeIcons.camera,
|
||||
title: 'Chụp ảnh hoặc chọn file',
|
||||
subtitle: 'JPG, PNG tối đa 5MB',
|
||||
@@ -699,17 +716,18 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
|
||||
// Certificates Upload (Multiple)
|
||||
const Text(
|
||||
Text(
|
||||
'Chứng chỉ hành nghề',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildMultipleUploadCard(
|
||||
context: context,
|
||||
colorScheme: colorScheme,
|
||||
selectedImages: certificateImages,
|
||||
existingImageUrls: existingCertificateUrls,
|
||||
isVerified: isVerified,
|
||||
@@ -743,8 +761,8 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
certificateImages: certificateImages.value,
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primaryBlue,
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: colorScheme.primary,
|
||||
foregroundColor: colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -770,6 +788,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
/// Build upload card for verification files
|
||||
Widget _buildUploadCard({
|
||||
required BuildContext context,
|
||||
required ColorScheme colorScheme,
|
||||
required IconData icon,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
@@ -790,14 +809,14 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
color: hasAnyImage
|
||||
? const Color(0xFFF0FDF4)
|
||||
: isDisabled
|
||||
? const Color(0xFFF1F5F9) // Gray for disabled
|
||||
: const Color(0xFFF8FAFC),
|
||||
? colorScheme.surfaceContainerHighest
|
||||
: colorScheme.surfaceContainerLowest,
|
||||
border: Border.all(
|
||||
color: hasAnyImage
|
||||
? const Color(0xFFBBF7D0)
|
||||
: isDisabled
|
||||
? const Color(0xFFCBD5E1)
|
||||
: const Color(0xFFE2E8F0),
|
||||
? colorScheme.outlineVariant
|
||||
: colorScheme.outlineVariant,
|
||||
width: 2,
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
@@ -877,7 +896,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
children: [
|
||||
FaIcon(
|
||||
icon,
|
||||
color: isDisabled ? AppColors.grey500 : AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -887,8 +906,8 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isDisabled
|
||||
? AppColors.grey500
|
||||
: const Color(0xFF1E293B),
|
||||
? colorScheme.onSurfaceVariant
|
||||
: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@@ -896,7 +915,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isDisabled ? AppColors.grey500 : AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -908,6 +927,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
/// Build multiple upload card for certificates (supports multiple images)
|
||||
Widget _buildMultipleUploadCard({
|
||||
required BuildContext context,
|
||||
required ColorScheme colorScheme,
|
||||
required ValueNotifier<List<File>> selectedImages,
|
||||
List<String>? existingImageUrls,
|
||||
required bool isVerified,
|
||||
@@ -972,35 +992,35 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF8FAFC),
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
border: Border.all(
|
||||
color: const Color(0xFFE2E8F0),
|
||||
color: colorScheme.outlineVariant,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const FaIcon(
|
||||
FaIcon(
|
||||
FontAwesomeIcons.folderPlus,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
allImages.isEmpty ? 'Chọn ảnh chứng chỉ' : 'Thêm ảnh chứng chỉ',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Text(
|
||||
Text(
|
||||
'Có thể chọn nhiều ảnh - JPG, PNG tối đa 5MB mỗi ảnh',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -1014,27 +1034,27 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F5F9),
|
||||
color: colorScheme.surfaceContainerHighest,
|
||||
border: Border.all(
|
||||
color: const Color(0xFFCBD5E1),
|
||||
color: colorScheme.outlineVariant,
|
||||
width: 2,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
),
|
||||
child: const Column(
|
||||
child: Column(
|
||||
children: [
|
||||
FaIcon(
|
||||
FontAwesomeIcons.certificate,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 32,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Chưa có chứng chỉ',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1124,6 +1144,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
/// Build avatar section with name, position, and status
|
||||
Widget _buildAvatarAndStatusSection(
|
||||
BuildContext context,
|
||||
ColorScheme colorScheme,
|
||||
ValueNotifier<File?> selectedImage,
|
||||
String initials,
|
||||
String? avatarUrl,
|
||||
@@ -1168,11 +1189,11 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.card),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.05),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -1190,11 +1211,11 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
height: 96,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: AppColors.primaryBlue,
|
||||
border: Border.all(color: Colors.white, width: 4),
|
||||
color: colorScheme.primary,
|
||||
border: Border.all(color: colorScheme.surface, width: 4),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.1),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -1237,22 +1258,22 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primaryBlue,
|
||||
color: colorScheme.primary,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.white, width: 3),
|
||||
border: Border.all(color: colorScheme.surface, width: 3),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.15),
|
||||
color: colorScheme.shadow.withValues(alpha: 0.15),
|
||||
blurRadius: 4,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Center(
|
||||
child: Center(
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.camera,
|
||||
size: 14,
|
||||
color: Colors.white,
|
||||
color: colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1266,10 +1287,10 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
// Name
|
||||
Text(
|
||||
fullName,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF212121),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1278,9 +1299,9 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
// Position
|
||||
Text(
|
||||
positionLabels[position] ?? position,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1325,6 +1346,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
/// Build text field
|
||||
Widget _buildTextField({
|
||||
required ColorScheme colorScheme,
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
bool required = false,
|
||||
@@ -1339,10 +1361,10 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
children: [
|
||||
if (required)
|
||||
@@ -1363,27 +1385,27 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Nhập $label',
|
||||
hintStyle: TextStyle(
|
||||
color: AppColors.grey500.withValues(alpha: 0.6),
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.6),
|
||||
fontSize: 14,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: readOnly ? const Color(0xFFF1F5F9) : const Color(0xFFF8FAFC),
|
||||
fillColor: readOnly ? colorScheme.surfaceContainerHighest : colorScheme.surfaceContainerLowest,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -1404,6 +1426,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
/// Build date field
|
||||
Widget _buildDateField({
|
||||
required BuildContext context,
|
||||
required ColorScheme colorScheme,
|
||||
required String label,
|
||||
required TextEditingController controller,
|
||||
ValueNotifier<bool>? hasChanges,
|
||||
@@ -1413,19 +1436,19 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
TextField(
|
||||
controller: controller,
|
||||
readOnly: true,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
onTap: () async {
|
||||
@@ -1447,32 +1470,32 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Chọn ngày sinh',
|
||||
hintStyle: TextStyle(
|
||||
color: AppColors.grey500.withValues(alpha: 0.6),
|
||||
color: colorScheme.onSurfaceVariant.withValues(alpha: 0.6),
|
||||
fontSize: 14,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFF8FAFC),
|
||||
fillColor: colorScheme.surfaceContainerLowest,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 16,
|
||||
),
|
||||
suffixIcon: const Icon(
|
||||
suffixIcon: Icon(
|
||||
Icons.calendar_today,
|
||||
size: 20,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
@@ -1484,6 +1507,7 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
|
||||
/// Build dropdown field
|
||||
Widget _buildDropdownField({
|
||||
required ColorScheme colorScheme,
|
||||
required String label,
|
||||
required String value,
|
||||
required List<Map<String, String>> items,
|
||||
@@ -1494,43 +1518,43 @@ class ProfileEditPage extends HookConsumerWidget {
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1E293B),
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButtonFormField<String>(
|
||||
initialValue: value,
|
||||
onChanged: onChanged,
|
||||
icon: const Padding(
|
||||
padding: EdgeInsets.only(right: 12),
|
||||
icon: Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: FaIcon(
|
||||
FontAwesomeIcons.chevronDown,
|
||||
size: 16,
|
||||
color: AppColors.grey500,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: const Color(0xFFF8FAFC),
|
||||
fillColor: colorScheme.surfaceContainerLowest,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(color: Color(0xFFE2E8F0)),
|
||||
borderSide: BorderSide(color: colorScheme.outlineVariant),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.input),
|
||||
borderSide: const BorderSide(
|
||||
color: AppColors.primaryBlue,
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user