diff --git a/lib/features/account/presentation/pages/account_page.dart b/lib/features/account/presentation/pages/account_page.dart index 0191583..a7eed8b 100644 --- a/lib/features/account/presentation/pages/account_page.dart +++ b/lib/features/account/presentation/pages/account_page.dart @@ -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( 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(), diff --git a/lib/features/account/presentation/pages/address_form_page.dart b/lib/features/account/presentation/pages/address_form_page.dart index 7c9f572..a315dbf 100644 --- a/lib/features/account/presentation/pages/address_form_page.dart +++ b/lib/features/account/presentation/pages/address_form_page.dart @@ -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()); @@ -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 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 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 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( 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), ), ); diff --git a/lib/features/account/presentation/pages/addresses_page.dart b/lib/features/account/presentation/pages/addresses_page.dart index f4b6242..9a39a0c 100644 --- a/lib/features/account/presentation/pages/addresses_page.dart +++ b/lib/features/account/presentation/pages/addresses_page.dart @@ -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), ), ); diff --git a/lib/features/account/presentation/pages/change_password_page.dart b/lib/features/account/presentation/pages/change_password_page.dart index 1081699..1d744dc 100644 --- a/lib/features/account/presentation/pages/change_password_page.dart +++ b/lib/features/account/presentation/pages/change_password_page.dart @@ -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 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 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( diff --git a/lib/features/account/presentation/pages/profile_edit_page.dart b/lib/features/account/presentation/pages/profile_edit_page.dart index f72fa9b..f712f96 100644 --- a/lib/features/account/presentation/pages/profile_edit_page.dart +++ b/lib/features/account/presentation/pages/profile_edit_page.dart @@ -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 idCardFrontImage, required ValueNotifier idCardBackImage, required ValueNotifier> 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> selectedImages, List? 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 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? 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> 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( 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, ), ), diff --git a/lib/features/account/presentation/widgets/account_menu_item.dart b/lib/features/account/presentation/widgets/account_menu_item.dart index 87caef0..fa28922 100644 --- a/lib/features/account/presentation/widgets/account_menu_item.dart +++ b/lib/features/account/presentation/widgets/account_menu_item.dart @@ -7,7 +7,6 @@ library; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; /// Account Menu Item Widget /// @@ -51,6 +50,8 @@ class AccountMenuItem extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return InkWell( onTap: onTap, child: Container( @@ -58,9 +59,9 @@ class AccountMenuItem extends StatelessWidget { horizontal: AppSpacing.md, vertical: AppSpacing.md, ), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - bottom: BorderSide(color: AppColors.grey100, width: 1.0), + bottom: BorderSide(color: colorScheme.outlineVariant, width: 1.0), ), ), child: Row( @@ -70,16 +71,14 @@ class AccountMenuItem extends StatelessWidget { width: 40, height: 40, decoration: BoxDecoration( - color: - iconBackgroundColor ?? - AppColors.lightBlue.withValues(alpha: 0.1), + color: iconBackgroundColor ?? colorScheme.primaryContainer, shape: BoxShape.circle, ), child: Center( child: FaIcon( icon, size: 18, - color: iconColor ?? AppColors.primaryBlue, + color: iconColor ?? colorScheme.primary, ), ), ), @@ -92,19 +91,19 @@ class AccountMenuItem extends StatelessWidget { children: [ Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 15, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (subtitle != null) ...[ const SizedBox(height: 4), Text( subtitle!, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -114,10 +113,10 @@ class AccountMenuItem extends StatelessWidget { // Trailing widget (default: chevron) trailing ?? - const FaIcon( + FaIcon( FontAwesomeIcons.chevronRight, size: 18, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ], ), diff --git a/lib/features/account/presentation/widgets/address_card.dart b/lib/features/account/presentation/widgets/address_card.dart index 68502ac..7930a29 100644 --- a/lib/features/account/presentation/widgets/address_card.dart +++ b/lib/features/account/presentation/widgets/address_card.dart @@ -41,25 +41,27 @@ class AddressCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), border: isDefault - ? Border.all(color: AppColors.primaryBlue, width: 2) + ? Border.all(color: colorScheme.primary, width: 2) : null, boxShadow: isDefault ? [ BoxShadow( - color: AppColors.primaryBlue.withValues(alpha: 0.15), + color: colorScheme.primary.withValues(alpha: 0.15), blurRadius: 12, offset: const Offset(0, 4), ), ] : [ BoxShadow( - color: Colors.black.withValues(alpha: 0.05), + color: colorScheme.shadow.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -78,7 +80,7 @@ class AddressCard extends StatelessWidget { value: true, groupValue: isSelected, onChanged: (_) => onRadioTap?.call(), - activeColor: AppColors.primaryBlue, + activeColor: colorScheme.primary, ), ), ), @@ -94,10 +96,10 @@ class AddressCard extends StatelessWidget { Flexible( child: Text( name, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), overflow: TextOverflow.ellipsis, ), @@ -110,15 +112,15 @@ class AddressCard extends StatelessWidget { vertical: 2, ), decoration: BoxDecoration( - color: AppColors.primaryBlue, + color: colorScheme.primary, borderRadius: BorderRadius.circular(4), ), - child: const Text( + child: Text( 'Mặc định', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w500, - color: Colors.white, + color: colorScheme.onPrimary, ), ), ) @@ -133,18 +135,18 @@ class AddressCard extends StatelessWidget { ), decoration: BoxDecoration( border: Border.all( - color: AppColors.primaryBlue.withValues( + color: colorScheme.primary.withValues( alpha: 0.3, ), ), borderRadius: BorderRadius.circular(4), ), - child: const Text( + child: Text( 'Đặt mặc định', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w500, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ), @@ -157,9 +159,9 @@ class AddressCard extends StatelessWidget { // Phone Text( phone, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), @@ -168,9 +170,9 @@ class AddressCard extends StatelessWidget { // Address Text Text( address, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Color(0xFF212121), + color: colorScheme.onSurface, height: 1.4, ), ), @@ -194,14 +196,14 @@ class AddressCard extends StatelessWidget { width: 36, height: 36, decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), - child: const Center( + child: Center( child: FaIcon( FontAwesomeIcons.penToSquare, size: 16, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ), @@ -221,7 +223,7 @@ class AddressCard extends StatelessWidget { width: 36, height: 36, decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: const Center( diff --git a/lib/features/auth/presentation/pages/business_unit_selection_page.dart b/lib/features/auth/presentation/pages/business_unit_selection_page.dart index 91364a4..a9c3e08 100644 --- a/lib/features/auth/presentation/pages/business_unit_selection_page.dart +++ b/lib/features/auth/presentation/pages/business_unit_selection_page.dart @@ -121,19 +121,21 @@ class _BusinessUnitSelectionPageState extends State { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, appBar: AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, 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( 'Đơn vị kinh doanh', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.w600, ), @@ -141,7 +143,7 @@ class _BusinessUnitSelectionPageState extends State { centerTitle: false, actions: [ IconButton( - icon: const FaIcon(FontAwesomeIcons.circleInfo, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.circleInfo, color: colorScheme.onSurface, size: 20), onPressed: _showInfoDialog, ), const SizedBox(width: AppSpacing.sm), @@ -164,20 +166,20 @@ class _BusinessUnitSelectionPageState extends State { ), borderRadius: BorderRadius.circular(20), ), - child: const Column( + child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'DBIZ', style: TextStyle( - color: Colors.white, + color: colorScheme.onPrimary, fontSize: 32, fontWeight: FontWeight.w700, ), ), Text( 'Worker App', - style: TextStyle(color: Colors.white, fontSize: 12), + style: TextStyle(color: colorScheme.onPrimary, fontSize: 12), ), ], ), @@ -187,22 +189,22 @@ class _BusinessUnitSelectionPageState extends State { const SizedBox(height: AppSpacing.xl), // Welcome Message - const Text( + Text( 'Chọn đơn vị kinh doanh để tiếp tục', textAlign: TextAlign.center, - style: TextStyle(color: AppColors.grey500, fontSize: 14), + style: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 14), ), const SizedBox(height: 40), - const Padding( - padding: EdgeInsets.symmetric(horizontal: AppSpacing.sm), + Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.sm), child: Text( 'Đơn vị kinh doanh', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ), @@ -227,11 +229,11 @@ class _BusinessUnitSelectionPageState extends State { bottom: isLast ? 0 : AppSpacing.xs, ), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, border: Border.all( color: isSelected - ? AppColors.primaryBlue - : AppColors.grey100, + ? colorScheme.primary + : colorScheme.surfaceContainerHighest, width: isSelected ? 2 : 1, ), borderRadius: BorderRadius.vertical( @@ -249,7 +251,7 @@ class _BusinessUnitSelectionPageState extends State { boxShadow: isSelected ? [ BoxShadow( - color: AppColors.primaryBlue.withValues( + color: colorScheme.primary.withValues( alpha: 0.1, ), blurRadius: 8, @@ -289,17 +291,17 @@ class _BusinessUnitSelectionPageState extends State { height: 40, decoration: BoxDecoration( color: isSelected - ? AppColors.primaryBlue.withValues( + ? colorScheme.primary.withValues( alpha: 0.1, ) - : AppColors.grey50, + : colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(8), ), child: Icon( FontAwesomeIcons.building, color: isSelected - ? AppColors.primaryBlue - : AppColors.grey500, + ? colorScheme.primary + : colorScheme.onSurfaceVariant, size: 20, ), ), @@ -317,17 +319,17 @@ class _BusinessUnitSelectionPageState extends State { ? FontWeight.w600 : FontWeight.w500, color: isSelected - ? AppColors.primaryBlue - : AppColors.grey900, + ? colorScheme.primary + : colorScheme.onSurface, ), ), if (unit.description != null) ...[ const SizedBox(height: 2), Text( unit.description!, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -342,19 +344,19 @@ class _BusinessUnitSelectionPageState extends State { shape: BoxShape.circle, border: Border.all( color: isSelected - ? AppColors.primaryBlue - : AppColors.grey500, + ? colorScheme.primary + : colorScheme.onSurfaceVariant, width: 2, ), color: isSelected - ? AppColors.primaryBlue + ? colorScheme.primary : Colors.transparent, ), child: isSelected - ? const Icon( + ? Icon( FontAwesomeIcons.solidCircle, size: 10, - color: AppColors.white, + color: colorScheme.onPrimary, ) : null, ), @@ -376,8 +378,8 @@ class _BusinessUnitSelectionPageState extends State { child: ElevatedButton( onPressed: _handleContinue, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( diff --git a/lib/features/auth/presentation/pages/forgot_password_page.dart b/lib/features/auth/presentation/pages/forgot_password_page.dart index af198dc..b8b594e 100644 --- a/lib/features/auth/presentation/pages/forgot_password_page.dart +++ b/lib/features/auth/presentation/pages/forgot_password_page.dart @@ -137,10 +137,12 @@ class _ForgotPasswordPageState extends ConsumerState { @override Widget build(BuildContext context) { + 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, title: const Text( 'Quên mật khẩu', @@ -166,27 +168,27 @@ class _ForgotPasswordPageState extends ConsumerState { const SizedBox(height: AppSpacing.xl), // Icon - _buildIcon(), + _buildIcon(colorScheme), const SizedBox(height: AppSpacing.xl), // Title & Instructions - _buildInstructions(), + _buildInstructions(colorScheme), const SizedBox(height: AppSpacing.xl), // Form Card - _buildFormCard(), + _buildFormCard(colorScheme), const SizedBox(height: AppSpacing.lg), // Back to Login Link - _buildBackToLoginLink(), + _buildBackToLoginLink(colorScheme), const SizedBox(height: AppSpacing.xl), // Support Link - _buildSupportLink(), + _buildSupportLink(colorScheme), ], ), ), @@ -196,45 +198,45 @@ class _ForgotPasswordPageState extends ConsumerState { } /// Build icon - Widget _buildIcon() { + Widget _buildIcon(ColorScheme colorScheme) { return Center( child: Container( width: 100, height: 100, decoration: BoxDecoration( - color: AppColors.primaryBlue.withValues(alpha: 0.1), + color: colorScheme.primary.withValues(alpha: 0.1), shape: BoxShape.circle, ), - child: const Icon( + child: Icon( FontAwesomeIcons.key, size: 50, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ); } /// Build instructions - Widget _buildInstructions() { - return const Column( + Widget _buildInstructions(ColorScheme colorScheme) { + return Column( children: [ Text( 'Đặt lại mật khẩu', style: TextStyle( fontSize: 28.0, fontWeight: FontWeight.bold, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - SizedBox(height: AppSpacing.sm), + const SizedBox(height: AppSpacing.sm), Padding( - padding: EdgeInsets.symmetric(horizontal: AppSpacing.md), + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), child: Text( 'Nhập số điện thoại đã đăng ký. Chúng tôi sẽ gửi mã OTP để xác nhận và đặt lại mật khẩu của bạn.', textAlign: TextAlign.center, style: TextStyle( fontSize: 15.0, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, height: 1.5, ), ), @@ -244,11 +246,11 @@ class _ForgotPasswordPageState extends ConsumerState { } /// Build form card - Widget _buildFormCard() { + Widget _buildFormCard(ColorScheme colorScheme) { return Container( padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( @@ -282,23 +284,23 @@ class _ForgotPasswordPageState extends ConsumerState { child: ElevatedButton( onPressed: _isLoading ? null : _handleSubmit, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, - disabledBackgroundColor: AppColors.grey100, - disabledForegroundColor: AppColors.grey500, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, + disabledBackgroundColor: colorScheme.surfaceContainerHighest, + disabledForegroundColor: colorScheme.onSurfaceVariant, elevation: ButtonSpecs.elevation, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(ButtonSpecs.borderRadius), ), ), child: _isLoading - ? const SizedBox( + ? SizedBox( height: 20.0, width: 20.0, child: CircularProgressIndicator( strokeWidth: 2.0, valueColor: AlwaysStoppedAnimation( - AppColors.white, + colorScheme.onPrimary, ), ), ) @@ -317,21 +319,21 @@ class _ForgotPasswordPageState extends ConsumerState { } /// Build back to login link - Widget _buildBackToLoginLink() { + Widget _buildBackToLoginLink(ColorScheme colorScheme) { return Center( child: RichText( text: TextSpan( text: 'Nhớ mật khẩu? ', - style: const TextStyle(fontSize: 14.0, color: AppColors.grey500), + style: TextStyle(fontSize: 14.0, color: colorScheme.onSurfaceVariant), children: [ WidgetSpan( child: GestureDetector( onTap: () => context.pop(), - child: const Text( + child: Text( 'Đăng nhập', style: TextStyle( fontSize: 14.0, - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, decoration: TextDecoration.none, ), @@ -345,20 +347,20 @@ class _ForgotPasswordPageState extends ConsumerState { } /// Build support link - Widget _buildSupportLink() { + Widget _buildSupportLink(ColorScheme colorScheme) { return Center( child: TextButton.icon( onPressed: _showSupport, - icon: const Icon( + icon: Icon( FontAwesomeIcons.headset, size: AppIconSize.sm, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), - label: const Text( + label: Text( 'Hỗ trợ khách hàng', style: TextStyle( fontSize: 14.0, - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, ), ), diff --git a/lib/features/auth/presentation/pages/login_page.dart b/lib/features/auth/presentation/pages/login_page.dart index edeb69d..f73e34b 100644 --- a/lib/features/auth/presentation/pages/login_page.dart +++ b/lib/features/auth/presentation/pages/login_page.dart @@ -166,9 +166,10 @@ class _LoginPageState extends ConsumerState { // Watch auth state for loading indicator final authState = ref.watch(authProvider); final isPasswordVisible = ref.watch(passwordVisibilityProvider); + final colorScheme = Theme.of(context).colorScheme; return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(AppSpacing.lg), @@ -185,22 +186,22 @@ class _LoginPageState extends ConsumerState { const SizedBox(height: AppSpacing.xl), // Welcome Message - _buildWelcomeMessage(), + _buildWelcomeMessage(colorScheme), const SizedBox(height: AppSpacing.xl), // Login Form Card - _buildLoginForm(authState, isPasswordVisible), + _buildLoginForm(authState, isPasswordVisible, colorScheme), const SizedBox(height: AppSpacing.lg), // Register Link - _buildRegisterLink(), + _buildRegisterLink(colorScheme), const SizedBox(height: AppSpacing.xl), // Support Link - _buildSupportLink(), + _buildSupportLink(colorScheme), ], ), @@ -228,7 +229,7 @@ class _LoginPageState extends ConsumerState { Text( 'EUROTILE', style: TextStyle( - color: AppColors.white, + color: Colors.white, fontSize: 32.0, fontWeight: FontWeight.w700, letterSpacing: 1.5, @@ -238,7 +239,7 @@ class _LoginPageState extends ConsumerState { Text( 'Worker App', style: TextStyle( - color: AppColors.white, + color: Colors.white, fontSize: 12.0, letterSpacing: 0.5, ), @@ -250,21 +251,21 @@ class _LoginPageState extends ConsumerState { } /// Build welcome message - Widget _buildWelcomeMessage() { - return const Column( + Widget _buildWelcomeMessage(ColorScheme colorScheme) { + return Column( children: [ Text( 'Xin chào!', style: TextStyle( fontSize: 32.0, fontWeight: FontWeight.bold, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - SizedBox(height: AppSpacing.xs), + const SizedBox(height: AppSpacing.xs), Text( 'Đăng nhập để tiếp tục', - style: TextStyle(fontSize: 16.0, color: AppColors.grey500), + style: TextStyle(fontSize: 16.0, color: colorScheme.onSurfaceVariant), ), ], ); @@ -274,13 +275,14 @@ class _LoginPageState extends ConsumerState { Widget _buildLoginForm( AsyncValue authState, bool isPasswordVisible, + ColorScheme colorScheme, ) { final isLoading = authState.isLoading; return Container( padding: const EdgeInsets.all(AppSpacing.lg), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( @@ -314,30 +316,30 @@ class _LoginPageState extends ConsumerState { enabled: !isLoading, obscureText: !isPasswordVisible, textInputAction: TextInputAction.done, - style: const TextStyle( + style: TextStyle( fontSize: InputFieldSpecs.fontSize, - color: AppColors.grey900, + color: colorScheme.onSurface, ), decoration: InputDecoration( labelText: 'Mật khẩu', - labelStyle: const TextStyle( + labelStyle: TextStyle( fontSize: InputFieldSpecs.labelFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), hintText: 'Nhập mật khẩu', - hintStyle: const TextStyle( + hintStyle: TextStyle( fontSize: InputFieldSpecs.hintFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - prefixIcon: const Icon( + prefixIcon: Icon( FontAwesomeIcons.lock, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: AppIconSize.md, ), suffixIcon: IconButton( icon: Icon( isPasswordVisible ? FontAwesomeIcons.eye : FontAwesomeIcons.eyeSlash, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: AppIconSize.md, ), onPressed: () { @@ -345,14 +347,14 @@ class _LoginPageState extends ConsumerState { }, ), filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, contentPadding: InputFieldSpecs.contentPadding, border: OutlineInputBorder( borderRadius: BorderRadius.circular( InputFieldSpecs.borderRadius, ), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 1.0, ), ), @@ -360,8 +362,8 @@ class _LoginPageState extends ConsumerState { borderRadius: BorderRadius.circular( InputFieldSpecs.borderRadius, ), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 1.0, ), ), @@ -369,8 +371,8 @@ class _LoginPageState extends ConsumerState { borderRadius: BorderRadius.circular( InputFieldSpecs.borderRadius, ), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2.0, ), ), @@ -424,7 +426,7 @@ class _LoginPageState extends ConsumerState { _rememberMe = value ?? false; }); }, - activeColor: AppColors.primaryBlue, + activeColor: colorScheme.primary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4.0), ), @@ -437,11 +439,11 @@ class _LoginPageState extends ConsumerState { _rememberMe = !_rememberMe; }); }, - child: const Text( + child: Text( 'Ghi nhớ đăng nhập', style: TextStyle( fontSize: 14.0, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -455,7 +457,7 @@ class _LoginPageState extends ConsumerState { 'Quên mật khẩu?', style: TextStyle( fontSize: 14.0, - color: isLoading ? AppColors.grey500 : AppColors.primaryBlue, + color: isLoading ? colorScheme.onSurfaceVariant : colorScheme.primary, fontWeight: FontWeight.w500, ), ), @@ -471,23 +473,23 @@ class _LoginPageState extends ConsumerState { child: ElevatedButton( onPressed: isLoading ? null : _handleLogin, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, - disabledBackgroundColor: AppColors.grey100, - disabledForegroundColor: AppColors.grey500, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, + disabledBackgroundColor: colorScheme.surfaceContainerHighest, + disabledForegroundColor: colorScheme.onSurfaceVariant, elevation: ButtonSpecs.elevation, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(ButtonSpecs.borderRadius), ), ), child: isLoading - ? const SizedBox( + ? SizedBox( height: 20.0, width: 20.0, child: CircularProgressIndicator( strokeWidth: 2.0, valueColor: AlwaysStoppedAnimation( - AppColors.white, + colorScheme.onPrimary, ), ), ) @@ -506,21 +508,21 @@ class _LoginPageState extends ConsumerState { } /// Build register link - Widget _buildRegisterLink() { + Widget _buildRegisterLink(ColorScheme colorScheme) { return Center( child: RichText( text: TextSpan( text: 'Chưa có tài khoản? ', - style: const TextStyle(fontSize: 14.0, color: AppColors.grey500), + style: TextStyle(fontSize: 14.0, color: colorScheme.onSurfaceVariant), children: [ WidgetSpan( child: GestureDetector( onTap: _navigateToRegister, - child: const Text( + child: Text( 'Đăng ký ngay', style: TextStyle( fontSize: 14.0, - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, decoration: TextDecoration.none, ), @@ -534,20 +536,20 @@ class _LoginPageState extends ConsumerState { } /// Build support link - Widget _buildSupportLink() { + Widget _buildSupportLink(ColorScheme colorScheme) { return Center( child: TextButton.icon( onPressed: _showSupport, - icon: const Icon( + icon: Icon( FontAwesomeIcons.headset, size: AppIconSize.sm, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), - label: const Text( + label: Text( 'Hỗ trợ khách hàng', style: TextStyle( fontSize: 14.0, - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, ), ), diff --git a/lib/features/auth/presentation/pages/otp_verification_page.dart b/lib/features/auth/presentation/pages/otp_verification_page.dart index 6a28544..4a5370d 100644 --- a/lib/features/auth/presentation/pages/otp_verification_page.dart +++ b/lib/features/auth/presentation/pages/otp_verification_page.dart @@ -14,7 +14,7 @@ import 'package:go_router/go_router.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/router/app_router.dart'; -import 'package:worker/core/theme/colors.dart'; +import 'package:worker/core/theme/colors.dart'; // Keep for status colors and brand gradients /// OTP Verification Page /// @@ -237,19 +237,21 @@ class _OtpVerificationPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( - backgroundColor: AppColors.grey50, + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, 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( 'Xác thực OTP', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.w600, ), @@ -276,14 +278,14 @@ class _OtpVerificationPageState extends ConsumerState { gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: [AppColors.primaryBlue, AppColors.lightBlue], + colors: [AppColors.primaryBlue, AppColors.lightBlue], // Keep brand colors ), shape: BoxShape.circle, ), - child: const Icon( + child: Icon( FontAwesomeIcons.shieldHalved, size: 36, - color: AppColors.white, + color: colorScheme.onPrimary, ), ), ), @@ -291,24 +293,24 @@ class _OtpVerificationPageState extends ConsumerState { const SizedBox(height: AppSpacing.lg), // Instructions - const Text( + Text( 'Nhập mã xác thực', textAlign: TextAlign.center, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 12), - const Text( + Text( 'Mã OTP đã được gửi đến số điện thoại', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), @@ -317,10 +319,10 @@ class _OtpVerificationPageState extends ConsumerState { Text( _formatPhoneNumber(widget.phoneNumber), textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), @@ -329,7 +331,7 @@ class _OtpVerificationPageState extends ConsumerState { // OTP Input Card Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( @@ -351,7 +353,7 @@ class _OtpVerificationPageState extends ConsumerState { padding: EdgeInsets.only( left: index > 0 ? 8 : 0, ), - child: _buildOtpInput(index), + child: _buildOtpInput(index, colorScheme), ), ), ), @@ -365,8 +367,8 @@ class _OtpVerificationPageState extends ConsumerState { child: ElevatedButton( onPressed: _isLoading ? null : _handleVerifyOtp, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( @@ -375,13 +377,13 @@ class _OtpVerificationPageState extends ConsumerState { ), ), child: _isLoading - ? const SizedBox( + ? SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( - AppColors.white, + colorScheme.onPrimary, ), ), ) @@ -405,9 +407,9 @@ class _OtpVerificationPageState extends ConsumerState { child: Text.rich( TextSpan( text: 'Không nhận được mã? ', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), children: [ WidgetSpan( @@ -421,8 +423,8 @@ class _OtpVerificationPageState extends ConsumerState { fontSize: 12, fontWeight: FontWeight.w500, color: _countdown > 0 - ? AppColors.grey500 - : AppColors.primaryBlue, + ? colorScheme.onSurfaceVariant + : colorScheme.primary, decoration: _countdown == 0 ? TextDecoration.none : TextDecoration.none, @@ -445,7 +447,7 @@ class _OtpVerificationPageState extends ConsumerState { } /// Build single OTP input box - Widget _buildOtpInput(int index) { + Widget _buildOtpInput(int index, ColorScheme colorScheme) { return SizedBox( width: 48, height: 48, @@ -455,10 +457,10 @@ class _OtpVerificationPageState extends ConsumerState { textAlign: TextAlign.center, keyboardType: TextInputType.number, maxLength: 1, - style: const TextStyle( + style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), inputFormatters: [ FilteringTextInputFormatter.digitsOnly, @@ -468,20 +470,20 @@ class _OtpVerificationPageState extends ConsumerState { contentPadding: EdgeInsets.zero, enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), filled: false, - fillColor: AppColors.white, + fillColor: colorScheme.surface, ), onChanged: (value) => _onOtpChanged(index, value), onTap: () { diff --git a/lib/features/auth/presentation/pages/register_page.dart b/lib/features/auth/presentation/pages/register_page.dart index 6875f7f..03c10a8 100644 --- a/lib/features/auth/presentation/pages/register_page.dart +++ b/lib/features/auth/presentation/pages/register_page.dart @@ -379,6 +379,9 @@ class _RegisterPageState extends ConsumerState { @override Widget build(BuildContext context) { + // Get color scheme at the start of build method + final colorScheme = Theme.of(context).colorScheme; + // Initialize data on first build if (!_hasInitialized) { // Use addPostFrameCallback to avoid calling setState during build @@ -388,18 +391,18 @@ class _RegisterPageState extends ConsumerState { } 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(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20), onPressed: () => context.pop(), ), - title: const Text( + title: Text( 'Đăng ký tài khoản', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.w600, ), @@ -407,15 +410,15 @@ class _RegisterPageState extends ConsumerState { centerTitle: false, ), body: _isLoadingData - ? const Center( + ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - CircularProgressIndicator(), - SizedBox(height: AppSpacing.md), + const CircularProgressIndicator(), + const SizedBox(height: AppSpacing.md), Text( 'Đang tải dữ liệu...', - style: TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), @@ -429,19 +432,19 @@ class _RegisterPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Welcome section - const Text( + Text( 'Tạo tài khoản mới', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, - color: AppColors.grey900, + color: colorScheme.onSurface, ), textAlign: TextAlign.center, ), const SizedBox(height: AppSpacing.xs), - const Text( + Text( 'Điền thông tin để bắt đầu', - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: AppSpacing.lg), @@ -449,7 +452,7 @@ class _RegisterPageState extends ConsumerState { // Form card Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), boxShadow: [ BoxShadow( @@ -464,7 +467,7 @@ class _RegisterPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Full Name - _buildLabel('Họ và tên *'), + _buildLabel('Họ và tên *', colorScheme), TextFormField( controller: _fullNameController, focusNode: _fullNameFocus, @@ -472,6 +475,7 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Nhập họ và tên', prefixIcon: FontAwesomeIcons.user, + colorScheme: colorScheme, ), validator: (value) => Validators.minLength( value, @@ -482,7 +486,7 @@ class _RegisterPageState extends ConsumerState { const SizedBox(height: AppSpacing.md), // Phone Number - _buildLabel('Số điện thoại *'), + _buildLabel('Số điện thoại *', colorScheme), PhoneInputField( controller: _phoneController, focusNode: _phoneFocus, @@ -491,7 +495,7 @@ class _RegisterPageState extends ConsumerState { const SizedBox(height: AppSpacing.md), // Email - _buildLabel('Email *'), + _buildLabel('Email *', colorScheme), TextFormField( controller: _emailController, focusNode: _emailFocus, @@ -500,13 +504,14 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Nhập email', prefixIcon: FontAwesomeIcons.envelope, + colorScheme: colorScheme, ), validator: Validators.email, ), const SizedBox(height: AppSpacing.md), // Password - _buildLabel('Mật khẩu *'), + _buildLabel('Mật khẩu *', colorScheme), TextFormField( controller: _passwordController, focusNode: _passwordFocus, @@ -515,12 +520,13 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Tạo mật khẩu mới', prefixIcon: FontAwesomeIcons.lock, + colorScheme: colorScheme, suffixIcon: IconButton( icon: Icon( _passwordVisible ? FontAwesomeIcons.eye : FontAwesomeIcons.eyeSlash, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), onPressed: () { setState(() { @@ -533,28 +539,28 @@ class _RegisterPageState extends ConsumerState { Validators.passwordSimple(value, minLength: 6), ), const SizedBox(height: AppSpacing.xs), - const Text( + Text( 'Mật khẩu tối thiểu 6 ký tự', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: AppSpacing.md), // Role Selection (Customer Groups) - _buildLabel('Vai trò *'), - _buildCustomerGroupDropdown(), + _buildLabel('Vai trò *', colorScheme), + _buildCustomerGroupDropdown(colorScheme), const SizedBox(height: AppSpacing.md), // Verification Section (conditional) if (_shouldShowVerification) ...[ - _buildVerificationSection(), + _buildVerificationSection(colorScheme), const SizedBox(height: AppSpacing.md), ], // Company Name (optional) - _buildLabel('Tên công ty/Cửa hàng'), + _buildLabel('Tên công ty/Cửa hàng', colorScheme), TextFormField( controller: _companyController, focusNode: _companyFocus, @@ -562,13 +568,14 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Nhập tên công ty (không bắt buộc)', prefixIcon: FontAwesomeIcons.building, + colorScheme: colorScheme, ), ), const SizedBox(height: AppSpacing.md), // City/Province - _buildLabel('Tỉnh/Thành phố *'), - _buildCityDropdown(), + _buildLabel('Tỉnh/Thành phố *', colorScheme), + _buildCityDropdown(colorScheme), const SizedBox(height: AppSpacing.md), // Terms and Conditions @@ -582,7 +589,7 @@ class _RegisterPageState extends ConsumerState { _termsAccepted = value ?? false; }); }, - activeColor: AppColors.primaryBlue, + activeColor: colorScheme.primary, ), Expanded( child: Padding( @@ -593,23 +600,23 @@ class _RegisterPageState extends ConsumerState { _termsAccepted = !_termsAccepted; }); }, - child: const Text.rich( + child: Text.rich( TextSpan( text: 'Tôi đồng ý với ', - style: TextStyle(fontSize: 13), + style: const TextStyle(fontSize: 13), children: [ TextSpan( text: 'Điều khoản sử dụng', style: TextStyle( - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, ), ), - TextSpan(text: ' và '), + const TextSpan(text: ' và '), TextSpan( text: 'Chính sách bảo mật', style: TextStyle( - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, ), ), @@ -629,8 +636,8 @@ class _RegisterPageState extends ConsumerState { child: ElevatedButton( onPressed: _isLoading ? null : _handleRegister, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( @@ -639,13 +646,13 @@ class _RegisterPageState extends ConsumerState { ), ), child: _isLoading - ? const SizedBox( + ? SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( - AppColors.white, + colorScheme.onPrimary, ), ), ) @@ -667,17 +674,17 @@ class _RegisterPageState extends ConsumerState { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( + Text( 'Đã có tài khoản? ', - style: TextStyle(fontSize: 13, color: AppColors.grey500), + style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant), ), GestureDetector( onTap: () => context.pop(), - child: const Text( + child: Text( 'Đăng nhập', style: TextStyle( fontSize: 13, - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w500, ), ), @@ -694,15 +701,15 @@ class _RegisterPageState extends ConsumerState { } /// Build label widget - Widget _buildLabel(String text) { + Widget _buildLabel(String text, ColorScheme colorScheme) { return Padding( padding: const EdgeInsets.only(bottom: AppSpacing.xs), child: Text( text, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ); @@ -712,34 +719,35 @@ class _RegisterPageState extends ConsumerState { InputDecoration _buildInputDecoration({ required String hintText, required IconData prefixIcon, + required ColorScheme colorScheme, Widget? suffixIcon, }) { return InputDecoration( hintText: hintText, - hintStyle: const TextStyle( + hintStyle: TextStyle( fontSize: InputFieldSpecs.hintFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), prefixIcon: Icon( prefixIcon, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: AppIconSize.md, ), suffixIcon: suffixIcon, filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, contentPadding: InputFieldSpecs.contentPadding, border: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.grey100, width: 1.0), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest, width: 1.0), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.grey100, width: 1.0), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest, width: 1.0), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2.0), + borderSide: BorderSide(color: colorScheme.primary, width: 2.0), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), @@ -753,7 +761,7 @@ class _RegisterPageState extends ConsumerState { } /// Build customer group dropdown - Widget _buildCustomerGroupDropdown() { + Widget _buildCustomerGroupDropdown(ColorScheme colorScheme) { final customerGroupsAsync = ref.watch(customerGroupsProvider); return customerGroupsAsync.when( @@ -763,6 +771,7 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Chọn vai trò', prefixIcon: FontAwesomeIcons.briefcase, + colorScheme: colorScheme, ), items: groups .map( @@ -825,7 +834,7 @@ class _RegisterPageState extends ConsumerState { } /// Build city dropdown - Widget _buildCityDropdown() { + Widget _buildCityDropdown(ColorScheme colorScheme) { final citiesAsync = ref.watch(citiesProvider); return citiesAsync.when( @@ -835,6 +844,7 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Chọn tỉnh/thành phố', prefixIcon: Icons.location_city, + colorScheme: colorScheme, ), items: cities .map( @@ -890,11 +900,11 @@ class _RegisterPageState extends ConsumerState { } /// Build verification section - Widget _buildVerificationSection() { + Widget _buildVerificationSection(ColorScheme colorScheme) { return Container( decoration: BoxDecoration( - color: const Color(0xFFF8FAFC), - border: Border.all(color: const Color(0xFFE2E8F0), width: 2), + color: colorScheme.surfaceContainerLowest, + border: Border.all(color: colorScheme.outlineVariant, width: 2), borderRadius: BorderRadius.circular(AppRadius.lg), ), padding: const EdgeInsets.all(AppSpacing.md), @@ -905,28 +915,28 @@ class _RegisterPageState extends ConsumerState { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon(Icons.shield, color: AppColors.primaryBlue, size: 20), + Icon(Icons.shield, color: colorScheme.primary, size: 20), const SizedBox(width: AppSpacing.xs), - const Text( + Text( 'Thông tin xác thực', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], ), const SizedBox(height: AppSpacing.xs), - const Text( + Text( 'Thông tin này sẽ được dùng để xác minh tư cách chuyên môn của bạn', - style: TextStyle(fontSize: 12, color: AppColors.grey500), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: AppSpacing.md), // ID Number - _buildLabel('Số CCCD/CMND'), + _buildLabel('Số CCCD/CMND', colorScheme), TextFormField( controller: _idNumberController, focusNode: _idNumberFocus, @@ -935,12 +945,13 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Nhập số CCCD/CMND', prefixIcon: Icons.badge, + colorScheme: colorScheme, ), ), const SizedBox(height: AppSpacing.md), // Tax Code - _buildLabel('Mã số thuế cá nhân/Công ty'), + _buildLabel('Mã số thuế cá nhân/Công ty', colorScheme), TextFormField( controller: _taxCodeController, focusNode: _taxCodeFocus, @@ -949,13 +960,14 @@ class _RegisterPageState extends ConsumerState { decoration: _buildInputDecoration( hintText: 'Nhập mã số thuế (không bắt buộc)', prefixIcon: Icons.receipt_long, + colorScheme: colorScheme, ), validator: Validators.taxIdOptional, ), const SizedBox(height: AppSpacing.md), // ID Card Upload - _buildLabel('Ảnh mặt trước CCCD/CMND'), + _buildLabel('Ảnh mặt trước CCCD/CMND', colorScheme), FileUploadCard( file: _idCardFile, onTap: () => _pickImage(true), @@ -967,7 +979,7 @@ class _RegisterPageState extends ConsumerState { const SizedBox(height: AppSpacing.md), // Certificate Upload - _buildLabel('Ảnh chứng chỉ hành nghề hoặc GPKD'), + _buildLabel('Ảnh chứng chỉ hành nghề hoặc GPKD', colorScheme), FileUploadCard( file: _certificateFile, onTap: () => _pickImage(false), diff --git a/lib/features/auth/presentation/pages/splash_page.dart b/lib/features/auth/presentation/pages/splash_page.dart index a37875c..aa93f7d 100644 --- a/lib/features/auth/presentation/pages/splash_page.dart +++ b/lib/features/auth/presentation/pages/splash_page.dart @@ -15,8 +15,10 @@ class SplashPage extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -37,7 +39,7 @@ class SplashPage extends StatelessWidget { Text( 'EUROTILE', style: TextStyle( - color: AppColors.white, + color: Colors.white, fontSize: 32.0, fontWeight: FontWeight.w700, letterSpacing: 1.5, @@ -47,7 +49,7 @@ class SplashPage extends StatelessWidget { Text( 'Worker App', style: TextStyle( - color: AppColors.white, + color: Colors.white, fontSize: 12.0, letterSpacing: 0.5, ), @@ -59,19 +61,19 @@ class SplashPage extends StatelessWidget { const SizedBox(height: 48.0), // Loading Indicator - const CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(AppColors.primaryBlue), + CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation(colorScheme.primary), strokeWidth: 3.0, ), const SizedBox(height: 16.0), // Loading Text - const Text( + Text( 'Đang tải...', style: TextStyle( fontSize: 14.0, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], diff --git a/lib/features/auth/presentation/widgets/file_upload_card.dart b/lib/features/auth/presentation/widgets/file_upload_card.dart index 3100b55..ddcc2d0 100644 --- a/lib/features/auth/presentation/widgets/file_upload_card.dart +++ b/lib/features/auth/presentation/widgets/file_upload_card.dart @@ -77,25 +77,27 @@ class FileUploadCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + if (file != null) { // Show preview with remove option - return _buildPreview(context); + return _buildPreview(context, colorScheme); } else { // Show upload area - return _buildUploadArea(context); + return _buildUploadArea(context, colorScheme); } } /// Build upload area - Widget _buildUploadArea(BuildContext context) { + Widget _buildUploadArea(BuildContext context, ColorScheme colorScheme) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(AppRadius.lg), child: Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, border: Border.all( - color: const Color(0xFFCBD5E1), + color: colorScheme.outlineVariant, width: 2, strokeAlign: BorderSide.strokeAlignInside, ), @@ -105,16 +107,16 @@ class FileUploadCard extends StatelessWidget { child: Column( children: [ // Icon - Icon(icon, size: 32, color: AppColors.grey500), + Icon(icon, size: 32, color: colorScheme.onSurfaceVariant), const SizedBox(height: AppSpacing.sm), // Title Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.xs), @@ -122,7 +124,7 @@ class FileUploadCard extends StatelessWidget { // Subtitle Text( subtitle, - style: const TextStyle(fontSize: 12, color: AppColors.grey500), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), ), ], ), @@ -131,11 +133,11 @@ class FileUploadCard extends StatelessWidget { } /// Build preview with remove button - Widget _buildPreview(BuildContext context) { + Widget _buildPreview(BuildContext context, ColorScheme colorScheme) { return Container( decoration: BoxDecoration( - color: AppColors.white, - border: Border.all(color: AppColors.grey100, width: 1), + color: colorScheme.surface, + border: Border.all(color: colorScheme.surfaceContainerHighest, width: 1), borderRadius: BorderRadius.circular(AppRadius.md), ), padding: const EdgeInsets.all(AppSpacing.sm), @@ -153,10 +155,10 @@ class FileUploadCard extends StatelessWidget { return Container( width: 50, height: 50, - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerHighest, + child: Icon( FontAwesomeIcons.image, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 24, ), ); @@ -173,10 +175,10 @@ class FileUploadCard extends StatelessWidget { children: [ Text( _getFileName(file!.path), - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -188,9 +190,9 @@ class FileUploadCard extends StatelessWidget { if (snapshot.hasData) { return Text( _formatFileSize(snapshot.data!), - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ); } diff --git a/lib/features/auth/presentation/widgets/phone_input_field.dart b/lib/features/auth/presentation/widgets/phone_input_field.dart index cdea2e8..5977efc 100644 --- a/lib/features/auth/presentation/widgets/phone_input_field.dart +++ b/lib/features/auth/presentation/widgets/phone_input_field.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; +import 'package:worker/core/theme/colors.dart'; // For AppColors.danger /// Phone Input Field /// @@ -65,6 +65,8 @@ class PhoneInputField extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return TextFormField( controller: controller, focusNode: focusNode, @@ -78,41 +80,41 @@ class PhoneInputField extends StatelessWidget { // Limit to reasonable phone length LengthLimitingTextInputFormatter(15), ], - style: const TextStyle( + style: TextStyle( fontSize: InputFieldSpecs.fontSize, - color: AppColors.grey900, + color: colorScheme.onSurface, ), decoration: InputDecoration( labelText: 'Số điện thoại', - labelStyle: const TextStyle( + labelStyle: TextStyle( fontSize: InputFieldSpecs.labelFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), hintText: 'Nhập số điện thoại', - hintStyle: const TextStyle( + hintStyle: TextStyle( fontSize: InputFieldSpecs.hintFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - prefixIcon: const Icon( + prefixIcon: Icon( FontAwesomeIcons.phone, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: AppIconSize.md, ), filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, contentPadding: InputFieldSpecs.contentPadding, border: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.grey100, width: 1.0), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest, width: 1.0), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.grey100, width: 1.0), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest, width: 1.0), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2.0, ), ), diff --git a/lib/features/auth/presentation/widgets/role_dropdown.dart b/lib/features/auth/presentation/widgets/role_dropdown.dart index 58695c2..98b5dc0 100644 --- a/lib/features/auth/presentation/widgets/role_dropdown.dart +++ b/lib/features/auth/presentation/widgets/role_dropdown.dart @@ -54,34 +54,36 @@ class RoleDropdown extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return DropdownButtonFormField( initialValue: value, decoration: InputDecoration( hintText: 'Chọn vai trò của bạn', - hintStyle: const TextStyle( + hintStyle: TextStyle( fontSize: InputFieldSpecs.hintFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - prefixIcon: const Icon( + prefixIcon: Icon( FontAwesomeIcons.briefcase, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: AppIconSize.md, ), filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, contentPadding: InputFieldSpecs.contentPadding, border: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.grey100, width: 1.0), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest, width: 1.0), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide(color: AppColors.grey100, width: 1.0), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest, width: 1.0), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2.0, ), ), @@ -105,11 +107,11 @@ class RoleDropdown extends StatelessWidget { ], onChanged: onChanged, validator: validator, - icon: const FaIcon(FontAwesomeIcons.chevronDown, color: AppColors.grey500, size: 16), - dropdownColor: AppColors.white, - style: const TextStyle( + icon: FaIcon(FontAwesomeIcons.chevronDown, color: colorScheme.onSurfaceVariant, size: 16), + dropdownColor: colorScheme.surface, + style: TextStyle( fontSize: InputFieldSpecs.fontSize, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ); } diff --git a/lib/features/cart/presentation/pages/cart_page.dart b/lib/features/cart/presentation/pages/cart_page.dart index a4683a0..d465480 100644 --- a/lib/features/cart/presentation/pages/cart_page.dart +++ b/lib/features/cart/presentation/pages/cart_page.dart @@ -49,6 +49,7 @@ class _CartPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final cartState = ref.watch(cartProvider); final currencyFormatter = NumberFormat.currency( @@ -69,26 +70,26 @@ class _CartPageState extends ConsumerState { } }, child: Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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: Text( 'Giỏ hàng ($itemCount)', - style: const TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ if (cartState.isNotEmpty) IconButton( icon: Icon( FontAwesomeIcons.trashCan, - color: hasSelection ? AppColors.danger : AppColors.grey500, + color: hasSelection ? AppColors.danger : colorScheme.onSurfaceVariant, ), onPressed: hasSelection ? () { @@ -130,7 +131,7 @@ class _CartPageState extends ConsumerState { // Loading overlay if (cartState.isLoading) Container( - color: Colors.black.withValues(alpha: 0.1), + color: colorScheme.onSurface.withValues(alpha: 0.1), child: const Center( child: CircularProgressIndicator(), ), @@ -155,15 +156,17 @@ class _CartPageState extends ConsumerState { /// Build select all section Widget _buildSelectAllSection(CartState cartState, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + return Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.05), + color: colorScheme.onSurface.withValues(alpha: 0.05), blurRadius: 4, offset: const Offset(0, 2), ), @@ -200,7 +203,7 @@ class _CartPageState extends ConsumerState { Text( 'Đã chọn: ${cartState.selectedCount}/${cartState.itemCount}', style: AppTypography.bodyMedium.copyWith( - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.w600, fontSize: 14, ), @@ -218,15 +221,17 @@ class _CartPageState extends ConsumerState { NumberFormat currencyFormatter, bool hasSelection, ) { + final colorScheme = Theme.of(context).colorScheme; + return Container( decoration: BoxDecoration( - color: AppColors.white, - border: const Border( - top: BorderSide(color: Color(0xFFF0F0F0), width: 2), + color: colorScheme.surface, + border: Border( + top: BorderSide(color: colorScheme.outlineVariant, width: 2), ), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.08), + color: colorScheme.onSurface.withValues(alpha: 0.08), blurRadius: 10, offset: const Offset(0, -2), ), @@ -245,14 +250,14 @@ class _CartPageState extends ConsumerState { Text( 'Tổng tạm tính (${cartState.selectedCount} sản phẩm)', style: AppTypography.bodyMedium.copyWith( - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, fontSize: 14, ), ), Text( currencyFormatter.format(cartState.selectedTotal), style: AppTypography.headlineSmall.copyWith( - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.bold, fontSize: 20, ), @@ -302,27 +307,27 @@ class _CartPageState extends ConsumerState { } : null, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - disabledBackgroundColor: AppColors.grey100, + backgroundColor: colorScheme.primary, + disabledBackgroundColor: colorScheme.inverseSurface.withValues(alpha: 0.6), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), elevation: 0, ), child: _isSyncing - ? const SizedBox( + ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: - AlwaysStoppedAnimation(AppColors.white), + AlwaysStoppedAnimation(colorScheme.surface), ), ) : Text( 'Tiến hành đặt hàng', style: AppTypography.labelLarge.copyWith( - color: AppColors.white, + color: colorScheme.surface, fontWeight: FontWeight.w600, fontSize: 16, ), @@ -359,6 +364,8 @@ class _CartPageState extends ConsumerState { /// Build error state (shown when cart fails to load and is empty) Widget _buildErrorState(BuildContext context, String errorMessage) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -374,7 +381,7 @@ class _CartPageState extends ConsumerState { padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( errorMessage, - style: AppTypography.bodyMedium.copyWith(color: AppColors.grey500), + style: AppTypography.bodyMedium.copyWith(color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ), @@ -393,6 +400,8 @@ class _CartPageState extends ConsumerState { /// Build empty cart state Widget _buildEmptyCart(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -400,19 +409,19 @@ class _CartPageState extends ConsumerState { Icon( FontAwesomeIcons.cartShopping, size: 80, - color: AppColors.grey500.withValues(alpha: 0.5), + color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), const SizedBox(height: 16), Text( 'Giỏ hàng trống', style: AppTypography.headlineMedium.copyWith( - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), Text( 'Hãy thêm sản phẩm vào giỏ hàng', - style: AppTypography.bodyMedium.copyWith(color: AppColors.grey500), + style: AppTypography.bodyMedium.copyWith(color: colorScheme.onSurfaceVariant), ), const SizedBox(height: 24), ElevatedButton.icon( @@ -475,24 +484,26 @@ class _CustomCheckbox extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return GestureDetector( onTap: () => onChanged?.call(!value), child: Container( width: 22, height: 22, decoration: BoxDecoration( - color: value ? AppColors.primaryBlue : AppColors.white, + color: value ? colorScheme.primary : colorScheme.surface, border: Border.all( - color: value ? AppColors.primaryBlue : const Color(0xFFCBD5E1), + color: value ? colorScheme.primary : colorScheme.outlineVariant, width: 2, ), borderRadius: BorderRadius.circular(6), ), child: value - ? const Icon( + ? Icon( FontAwesomeIcons.check, size: 16, - color: AppColors.white, + color: colorScheme.surface, ) : null, ), diff --git a/lib/features/cart/presentation/pages/checkout_page.dart b/lib/features/cart/presentation/pages/checkout_page.dart index 6b84f1a..f9045f9 100644 --- a/lib/features/cart/presentation/pages/checkout_page.dart +++ b/lib/features/cart/presentation/pages/checkout_page.dart @@ -42,6 +42,8 @@ class CheckoutPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + // Form key for validation final formKey = useMemoized(() => GlobalKey()); @@ -102,22 +104,22 @@ class CheckoutPage extends HookConsumerWidget { final total = subtotal - memberDiscount + shipping; return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( - backgroundColor: Colors.white, + backgroundColor: colorScheme.surface, elevation: 0, leading: IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.arrowLeft, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () => context.pop(), ), - title: const Text( + title: Text( 'Thanh toán', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.bold, ), @@ -165,11 +167,11 @@ class CheckoutPage 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.onSurface.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -183,11 +185,11 @@ class CheckoutPage 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.onSurface.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -203,9 +205,9 @@ class CheckoutPage extends HookConsumerWidget { const SizedBox(height: 12), Text( 'Không thể tải phương thức thanh toán', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -225,7 +227,7 @@ class CheckoutPage extends HookConsumerWidget { const SizedBox(height: AppSpacing.md), // Discount Code Section - _buildDiscountCodeSection(), + _buildDiscountCodeSection(context), const SizedBox(height: AppSpacing.md), @@ -263,13 +265,13 @@ class CheckoutPage extends HookConsumerWidget { }, activeColor: AppColors.warning, ), - const Expanded( + Expanded( child: Text( 'Yêu cầu hợp đồng', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ), @@ -281,20 +283,20 @@ class CheckoutPage extends HookConsumerWidget { const SizedBox(height: AppSpacing.md), // Terms and Conditions - const Padding( - padding: EdgeInsets.symmetric(horizontal: AppSpacing.md), + Padding( + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), child: Text.rich( TextSpan( text: 'Bằng cách đặt hàng, bạn đồng ý với ', style: TextStyle( fontSize: 14, - color: Color(0xFF6B7280), + color: colorScheme.onSurfaceVariant, ), children: [ TextSpan( text: 'Điều khoản & Điều kiện', style: TextStyle( - color: AppColors.primaryBlue, + color: colorScheme.primary, decoration: TextDecoration.underline, ), ), @@ -328,16 +330,18 @@ class CheckoutPage extends HookConsumerWidget { } /// Build Discount Code Section (Card 4 from HTML) - Widget _buildDiscountCodeSection() { + Widget _buildDiscountCodeSection(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( 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.onSurface.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -351,16 +355,16 @@ class CheckoutPage extends HookConsumerWidget { children: [ Icon( FontAwesomeIcons.ticket, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 20, ), const SizedBox(width: 8), - const Text( + Text( 'Mã giảm giá', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ], @@ -377,16 +381,16 @@ class CheckoutPage extends HookConsumerWidget { hintText: 'Nhập mã giảm giá', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Color(0xFFD1D5DB)), + borderSide: BorderSide(color: colorScheme.outlineVariant), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: Color(0xFFD1D5DB)), + borderSide: BorderSide(color: colorScheme.outlineVariant), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -403,7 +407,7 @@ class CheckoutPage extends HookConsumerWidget { // TODO: Apply discount code }, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, + backgroundColor: colorScheme.primary, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, @@ -413,10 +417,10 @@ class CheckoutPage extends HookConsumerWidget { ), elevation: 0, ), - child: const Text( + child: Text( 'Áp dụng', style: TextStyle( - color: Colors.white, + color: colorScheme.onPrimary, fontWeight: FontWeight.w600, ), ), @@ -436,18 +440,18 @@ class CheckoutPage extends HookConsumerWidget { ), child: Row( children: [ - Icon( + const Icon( FontAwesomeIcons.circleCheck, color: AppColors.success, size: 18, ), const SizedBox(width: 8), - const Expanded( + Expanded( child: Text( 'Bạn được giảm 15% (hạng Diamond)', style: TextStyle( fontSize: 14, - color: Color(0xFF166534), + color: const Color(0xFF166534), fontWeight: FontWeight.w500, ), ), diff --git a/lib/features/cart/presentation/widgets/cart_item_widget.dart b/lib/features/cart/presentation/widgets/cart_item_widget.dart index 5ff1a1a..4a3a7e1 100644 --- a/lib/features/cart/presentation/widgets/cart_item_widget.dart +++ b/lib/features/cart/presentation/widgets/cart_item_widget.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/core/theme/typography.dart'; import 'package:worker/features/cart/presentation/providers/cart_provider.dart'; import 'package:worker/features/cart/presentation/providers/cart_state.dart'; @@ -74,6 +73,7 @@ class _CartItemWidgetState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final cartState = ref.watch(cartProvider); final isSelected = cartState.selectedItems[widget.item.product.productId] ?? false; @@ -88,7 +88,7 @@ class _CartItemWidgetState extends ConsumerState { margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( @@ -127,7 +127,7 @@ class _CartItemWidgetState extends ConsumerState { placeholder: (context, url) => Container( width: 100, height: 100, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), @@ -135,10 +135,10 @@ class _CartItemWidgetState extends ConsumerState { errorWidget: (context, url, error) => Container( width: 100, height: 100, - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 32, ), ), @@ -169,7 +169,7 @@ class _CartItemWidgetState extends ConsumerState { Text( '${currencyFormatter.format(widget.item.product.basePrice)}/m²', style: AppTypography.titleMedium.copyWith( - color: AppColors.primaryBlue, + color: colorScheme.primary, fontWeight: FontWeight.bold, fontSize: 16, ), @@ -209,22 +209,22 @@ class _CartItemWidgetState extends ConsumerState { contentPadding: EdgeInsets.zero, border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide( - color: Color(0xFFE0E0E0), + borderSide: BorderSide( + color: colorScheme.outlineVariant, width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide( - color: Color(0xFFE0E0E0), + borderSide: BorderSide( + color: colorScheme.outlineVariant, width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -254,7 +254,7 @@ class _CartItemWidgetState extends ConsumerState { Text( 'm²', style: AppTypography.bodySmall.copyWith( - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -266,7 +266,7 @@ class _CartItemWidgetState extends ConsumerState { RichText( text: TextSpan( style: AppTypography.bodySmall.copyWith( - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, fontSize: 13, ), children: [ @@ -305,24 +305,25 @@ class _CustomCheckbox extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; return GestureDetector( onTap: () => onChanged?.call(!value), child: Container( width: 20, height: 20, decoration: BoxDecoration( - color: value ? AppColors.primaryBlue : AppColors.white, + color: value ? colorScheme.primary : colorScheme.surface, border: Border.all( - color: value ? AppColors.primaryBlue : const Color(0xFFCBD5E1), + color: value ? colorScheme.primary : colorScheme.outlineVariant, width: 2, ), borderRadius: BorderRadius.circular(6), ), child: value - ? const Icon( + ? Icon( FontAwesomeIcons.check, size: 14, - color: AppColors.white, + color: colorScheme.surface, ) : null, ), @@ -341,6 +342,7 @@ class _QuantityButton extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; return InkWell( onTap: onPressed, borderRadius: BorderRadius.circular(6), @@ -348,11 +350,11 @@ class _QuantityButton extends StatelessWidget { width: 32, height: 32, decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFE0E0E0), width: 2), + border: Border.all(color: colorScheme.outlineVariant, width: 2), borderRadius: BorderRadius.circular(6), - color: AppColors.white, + color: colorScheme.surface, ), - child: Icon(icon, size: 16, color: AppColors.grey900), + child: Icon(icon, size: 16, color: colorScheme.onSurface), ), ); } diff --git a/lib/features/cart/presentation/widgets/checkout_date_picker_field.dart b/lib/features/cart/presentation/widgets/checkout_date_picker_field.dart index 985fbeb..15b240d 100644 --- a/lib/features/cart/presentation/widgets/checkout_date_picker_field.dart +++ b/lib/features/cart/presentation/widgets/checkout_date_picker_field.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; /// Checkout Date Picker Field /// @@ -24,15 +23,17 @@ class CheckoutDatePickerField extends HookWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Ngày nhận hàng mong muốn', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), @@ -51,9 +52,9 @@ class CheckoutDatePickerField extends HookWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: const Color(0xFFF8FAFC), + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(AppRadius.input), - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -65,14 +66,14 @@ class CheckoutDatePickerField extends HookWidget { style: TextStyle( fontSize: 14, color: selectedDate.value != null - ? const Color(0xFF212121) - : AppColors.grey500.withValues(alpha: 0.6), + ? colorScheme.onSurface + : colorScheme.onSurfaceVariant.withValues(alpha: 0.6), ), ), - const Icon( + Icon( FontAwesomeIcons.calendar, size: 20, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ], ), diff --git a/lib/features/cart/presentation/widgets/checkout_dropdown_field.dart b/lib/features/cart/presentation/widgets/checkout_dropdown_field.dart index 6a3b932..4476631 100644 --- a/lib/features/cart/presentation/widgets/checkout_dropdown_field.dart +++ b/lib/features/cart/presentation/widgets/checkout_dropdown_field.dart @@ -28,16 +28,18 @@ class CheckoutDropdownField extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( text: TextSpan( text: label, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), children: [ if (required) @@ -53,23 +55,23 @@ class CheckoutDropdownField extends StatelessWidget { initialValue: value, 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, ), ), diff --git a/lib/features/cart/presentation/widgets/checkout_submit_button.dart b/lib/features/cart/presentation/widgets/checkout_submit_button.dart index fe913c3..4f5a389 100644 --- a/lib/features/cart/presentation/widgets/checkout_submit_button.dart +++ b/lib/features/cart/presentation/widgets/checkout_submit_button.dart @@ -42,6 +42,8 @@ class CheckoutSubmitButton extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + return Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), @@ -66,8 +68,8 @@ class CheckoutSubmitButton extends HookConsumerWidget { style: ElevatedButton.styleFrom( backgroundColor: ignorePricingRule ? AppColors.warning - : AppColors.primaryBlue, - foregroundColor: Colors.white, + : colorScheme.primary, + foregroundColor: colorScheme.surface, padding: const EdgeInsets.symmetric(vertical: 16), elevation: 0, shape: RoundedRectangleBorder( diff --git a/lib/features/cart/presentation/widgets/checkout_text_field.dart b/lib/features/cart/presentation/widgets/checkout_text_field.dart index 3f23302..17086c8 100644 --- a/lib/features/cart/presentation/widgets/checkout_text_field.dart +++ b/lib/features/cart/presentation/widgets/checkout_text_field.dart @@ -32,16 +32,18 @@ class CheckoutTextField extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( text: TextSpan( text: label, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), children: [ if (required) @@ -61,27 +63,27 @@ class CheckoutTextField extends StatelessWidget { decoration: InputDecoration( hintText: 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, ), 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, ), ), diff --git a/lib/features/cart/presentation/widgets/delivery_information_section.dart b/lib/features/cart/presentation/widgets/delivery_information_section.dart index 8c69e21..dc6c517 100644 --- a/lib/features/cart/presentation/widgets/delivery_information_section.dart +++ b/lib/features/cart/presentation/widgets/delivery_information_section.dart @@ -9,7 +9,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/account/domain/entities/address.dart'; import 'package:worker/features/account/presentation/providers/address_provider.dart'; import 'package:worker/features/cart/presentation/widgets/checkout_date_picker_field.dart'; @@ -33,6 +32,8 @@ class DeliveryInformationSection extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + // Watch the default address final defaultAddr = ref.watch(defaultAddressProvider); @@ -54,7 +55,7 @@ class DeliveryInformationSection 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( @@ -70,18 +71,18 @@ class DeliveryInformationSection extends HookConsumerWidget { // Section Title Row( children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.truck, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 16, ), const SizedBox(width: AppSpacing.sm), - const Text( + Text( 'Thông tin giao hàng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ], @@ -93,12 +94,12 @@ class DeliveryInformationSection extends HookConsumerWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Địa chỉ nhận hàng', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, - color: Color(0xFF424242), + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.sm), @@ -125,7 +126,7 @@ class DeliveryInformationSection extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFE0E0E0)), + border: Border.all(color: colorScheme.outline), borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Row( @@ -137,10 +138,10 @@ class DeliveryInformationSection extends HookConsumerWidget { // Name Text( selectedAddress.value!.addressTitle, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), @@ -148,9 +149,9 @@ class DeliveryInformationSection extends HookConsumerWidget { // Phone Text( selectedAddress.value!.phone, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF757575), + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 2), @@ -158,19 +159,19 @@ class DeliveryInformationSection extends HookConsumerWidget { // Address Text( selectedAddress.value!.fullAddress, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF757575), + color: colorScheme.onSurfaceVariant, ), ), ], ), ), const SizedBox(width: AppSpacing.sm), - const FaIcon( + FaIcon( FontAwesomeIcons.chevronRight, size: 14, - color: Color(0xFF9E9E9E), + color: colorScheme.onSurfaceVariant, ), ], ), @@ -194,26 +195,26 @@ class DeliveryInformationSection extends HookConsumerWidget { padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( border: Border.all( - color: AppColors.primaryBlue, + color: colorScheme.primary, style: BorderStyle.solid, ), borderRadius: BorderRadius.circular(AppRadius.sm), ), - child: const Row( + child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FaIcon( FontAwesomeIcons.plus, size: 14, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), - SizedBox(width: AppSpacing.sm), + const SizedBox(width: AppSpacing.sm), Text( 'Thêm địa chỉ giao hàng', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], diff --git a/lib/features/cart/presentation/widgets/invoice_section.dart b/lib/features/cart/presentation/widgets/invoice_section.dart index 29284ab..d673615 100644 --- a/lib/features/cart/presentation/widgets/invoice_section.dart +++ b/lib/features/cart/presentation/widgets/invoice_section.dart @@ -8,7 +8,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/account/presentation/providers/address_provider.dart'; /// Invoice Section @@ -22,6 +21,8 @@ class InvoiceSection extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + // Watch the default address final defaultAddr = ref.watch(defaultAddressProvider); @@ -29,7 +30,7 @@ class InvoiceSection 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( @@ -45,19 +46,19 @@ class InvoiceSection extends HookConsumerWidget { // Header with Toggle Row( children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.fileInvoice, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 16, ), const SizedBox(width: AppSpacing.sm), - const Expanded( + Expanded( child: Text( 'Phát hành hóa đơn', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ), @@ -67,7 +68,7 @@ class InvoiceSection extends HookConsumerWidget { onChanged: (value) { needsInvoice.value = value; }, - activeTrackColor: AppColors.primaryBlue, + activeTrackColor: colorScheme.primary, ), ], ), @@ -75,7 +76,7 @@ class InvoiceSection extends HookConsumerWidget { // Invoice Information (visible when toggle is ON) if (needsInvoice.value) ...[ const SizedBox(height: AppSpacing.md), - const Divider(color: Color(0xFFE0E0E0)), + Divider(color: colorScheme.outlineVariant), const SizedBox(height: AppSpacing.md), // Address Card @@ -89,7 +90,7 @@ class InvoiceSection extends HookConsumerWidget { child: Container( padding: const EdgeInsets.all(AppSpacing.sm), decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFE0E0E0)), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(AppRadius.sm), ), child: Row( @@ -101,10 +102,10 @@ class InvoiceSection extends HookConsumerWidget { // Company/Address Title Text( defaultAddr.addressTitle, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), @@ -114,9 +115,9 @@ class InvoiceSection extends HookConsumerWidget { defaultAddr.taxCode!.isNotEmpty) ...[ Text( 'Mã số thuế: ${defaultAddr.taxCode}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF757575), + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 2), @@ -125,9 +126,9 @@ class InvoiceSection extends HookConsumerWidget { // Phone Text( 'Số điện thoại: ${defaultAddr.phone}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF757575), + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 2), @@ -137,9 +138,9 @@ class InvoiceSection extends HookConsumerWidget { defaultAddr.email!.isNotEmpty) ...[ Text( 'Email: ${defaultAddr.email}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF757575), + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 2), @@ -148,19 +149,19 @@ class InvoiceSection extends HookConsumerWidget { // Address Text( 'Địa chỉ: ${defaultAddr.fullAddress}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF757575), + color: colorScheme.onSurfaceVariant, ), ), ], ), ), const SizedBox(width: AppSpacing.sm), - const FaIcon( + FaIcon( FontAwesomeIcons.chevronRight, size: 14, - color: Color(0xFF9E9E9E), + color: colorScheme.onSurfaceVariant, ), ], ), @@ -177,26 +178,26 @@ class InvoiceSection extends HookConsumerWidget { padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( border: Border.all( - color: AppColors.primaryBlue, + color: colorScheme.primary, style: BorderStyle.solid, ), borderRadius: BorderRadius.circular(AppRadius.sm), ), - child: const Row( + child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ FaIcon( FontAwesomeIcons.plus, size: 14, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), - SizedBox(width: AppSpacing.sm), + const SizedBox(width: AppSpacing.sm), Text( 'Thêm địa chỉ xuất hóa đơn', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], diff --git a/lib/features/cart/presentation/widgets/order_summary_section.dart b/lib/features/cart/presentation/widgets/order_summary_section.dart index c9d8e32..9592adc 100644 --- a/lib/features/cart/presentation/widgets/order_summary_section.dart +++ b/lib/features/cart/presentation/widgets/order_summary_section.dart @@ -29,11 +29,13 @@ class OrderSummarySection extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( 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( @@ -47,32 +49,32 @@ class OrderSummarySection extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section Title - const Text( + Text( 'Tóm tắt đơn hàng', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.md), // Cart Items with conversion details - ...cartItems.map((item) => _buildCartItemWithConversion(item)), + ...cartItems.map((item) => _buildCartItemWithConversion(context, item)), const Divider(height: 32), // Subtotal - _buildSummaryRow('Tạm tính', subtotal), + _buildSummaryRow(context, 'Tạm tính', subtotal), const SizedBox(height: 8), // Member Tier Discount (Diamond 15%) - _buildSummaryRow('Giảm giá Diamond', -discount, isDiscount: true), + _buildSummaryRow(context, 'Giảm giá Diamond', -discount, isDiscount: true), const SizedBox(height: 8), // Shipping - _buildSummaryRow('Phí vận chuyển', shipping, isFree: shipping == 0), + _buildSummaryRow(context, 'Phí vận chuyển', shipping, isFree: shipping == 0), const Divider(height: 24), @@ -80,20 +82,20 @@ class OrderSummarySection extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( 'Tổng thanh toán', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), Text( _formatCurrency(total), - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], @@ -104,7 +106,9 @@ class OrderSummarySection extends StatelessWidget { } /// Build cart item with conversion details on two lines - Widget _buildCartItemWithConversion(Map item) { + Widget _buildCartItemWithConversion(BuildContext context, Map item) { + final colorScheme = Theme.of(context).colorScheme; + // Get real conversion data from CartItemData final quantity = item['quantity'] as double; final quantityConverted = item['quantityConverted'] as double; @@ -125,10 +129,10 @@ class OrderSummarySection extends StatelessWidget { // Line 1: Product name Text( item['name'] as String, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -137,9 +141,9 @@ class OrderSummarySection extends StatelessWidget { // Line 2: Conversion details (muted text) Text( '${quantity.toStringAsFixed(2)} m² ($boxes viên / ${quantityConverted.toStringAsFixed(2)} m²)', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -151,10 +155,10 @@ class OrderSummarySection extends StatelessWidget { // Price (right side) - using converted quantity for accurate billing Text( _formatCurrency(price * quantityConverted), - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ], @@ -164,24 +168,27 @@ class OrderSummarySection extends StatelessWidget { /// Build summary row Widget _buildSummaryRow( + BuildContext context, String label, double amount, { bool isDiscount = false, bool isFree = false, }) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, - style: const TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), Text( isFree ? 'Miễn phí' : _formatCurrency(amount), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: isDiscount ? AppColors.success : const Color(0xFF212121), + color: isDiscount ? AppColors.success : colorScheme.onSurface, ), ), ], diff --git a/lib/features/cart/presentation/widgets/payment_method_section.dart b/lib/features/cart/presentation/widgets/payment_method_section.dart index 3d82942..3013007 100644 --- a/lib/features/cart/presentation/widgets/payment_method_section.dart +++ b/lib/features/cart/presentation/widgets/payment_method_section.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/orders/domain/entities/payment_term.dart'; /// Payment Method Section @@ -25,13 +24,15 @@ class PaymentMethodSection extends HookWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + // Show empty state if no payment terms available if (paymentTerms.isEmpty) { return Container( 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( @@ -41,12 +42,12 @@ class PaymentMethodSection extends HookWidget { ), ], ), - child: const Center( + child: Center( child: Text( 'Không có phương thức thanh toán khả dụng', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -57,7 +58,7 @@ class PaymentMethodSection extends HookWidget { 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( @@ -71,12 +72,12 @@ class PaymentMethodSection extends HookWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Section Title - const Text( + Text( 'Phương thức thanh toán', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), @@ -109,12 +110,12 @@ class PaymentMethodSection extends HookWidget { onChanged: (value) { paymentMethod.value = value!; }, - activeColor: AppColors.primaryBlue, + activeColor: colorScheme.primary, ), const SizedBox(width: 12), Icon( icon, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 24, ), const SizedBox(width: 12), @@ -132,9 +133,9 @@ class PaymentMethodSection extends HookWidget { const SizedBox(height: 4), Text( term.customDescription, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], diff --git a/lib/features/cart/presentation/widgets/price_negotiation_section.dart b/lib/features/cart/presentation/widgets/price_negotiation_section.dart index 8f743ea..3f16bb2 100644 --- a/lib/features/cart/presentation/widgets/price_negotiation_section.dart +++ b/lib/features/cart/presentation/widgets/price_negotiation_section.dart @@ -18,6 +18,8 @@ class PriceNegotiationSection extends HookWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), padding: const EdgeInsets.all(AppSpacing.md), @@ -35,7 +37,7 @@ class PriceNegotiationSection extends HookWidget { }, activeColor: AppColors.warning, ), - const Expanded( + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -44,13 +46,16 @@ class PriceNegotiationSection extends HookWidget { style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), - SizedBox(height: 4), + const SizedBox(height: 4), Text( 'Gửi yêu cầu đàm phán giá cho đơn hàng này', - style: TextStyle(fontSize: 13, color: AppColors.grey500), + style: TextStyle( + fontSize: 13, + color: colorScheme.onSurfaceVariant, + ), ), ], ), diff --git a/lib/features/favorites/presentation/pages/favorites_page.dart b/lib/features/favorites/presentation/pages/favorites_page.dart index 485e4c1..fcf0231 100644 --- a/lib/features/favorites/presentation/pages/favorites_page.dart +++ b/lib/features/favorites/presentation/pages/favorites_page.dart @@ -78,6 +78,8 @@ class FavoritesPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + // Search controller final searchController = useTextEditingController(); final searchQuery = useState(''); @@ -104,20 +106,20 @@ class FavoritesPage extends HookConsumerWidget { } return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( leading: IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.arrowLeft, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () => context.pop(), ), - title: const Text('Yêu thích', style: TextStyle(color: Colors.black)), + title: Text('Yêu thích', style: TextStyle(color: colorScheme.onSurface)), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ // Count badge @@ -127,10 +129,10 @@ class FavoritesPage extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text( '($favoriteCount)', - style: const TextStyle( + style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ), @@ -139,9 +141,9 @@ class FavoritesPage extends HookConsumerWidget { // Clear all button if (favoriteCount > 0) IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.trashCan, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), tooltip: 'Xóa tất cả', @@ -177,16 +179,16 @@ class FavoritesPage extends HookConsumerWidget { onChanged: (value) => searchQuery.value = value, decoration: InputDecoration( hintText: 'Tìm kiếm sản phẩm...', - hintStyle: const TextStyle(color: AppColors.grey500), - prefixIcon: const Icon( + hintStyle: TextStyle(color: colorScheme.onSurfaceVariant), + prefixIcon: Icon( Icons.search, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), suffixIcon: searchQuery.value.isNotEmpty ? IconButton( - icon: const Icon( + icon: Icon( Icons.clear, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), onPressed: () { searchController.clear(); @@ -195,19 +197,19 @@ class FavoritesPage extends HookConsumerWidget { ) : null, filled: true, - fillColor: Colors.white, + fillColor: colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppRadius.button), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppRadius.button), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppRadius.button), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -229,9 +231,9 @@ class FavoritesPage extends HookConsumerWidget { alignment: Alignment.centerLeft, child: Text( 'Tìm thấy ${filteredProducts.length} sản phẩm', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -245,26 +247,26 @@ class FavoritesPage extends HookConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.magnifyingGlass, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), const SizedBox(height: AppSpacing.md), Text( 'Không tìm thấy "${searchQuery.value}"', - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.sm), - const Text( + Text( 'Thử tìm kiếm với từ khóa khác', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -431,6 +433,8 @@ class _EmptyState extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Padding( padding: const EdgeInsets.all(AppSpacing.xl), @@ -441,18 +445,18 @@ class _EmptyState extends StatelessWidget { FaIcon( FontAwesomeIcons.heart, size: 80.0, - color: AppColors.grey500.withValues(alpha: 0.5), + color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), const SizedBox(height: AppSpacing.lg), // Heading - const Text( + Text( 'Chưa có sản phẩm yêu thích', style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), textAlign: TextAlign.center, ), @@ -460,9 +464,9 @@ class _EmptyState extends StatelessWidget { const SizedBox(height: AppSpacing.sm), // Subtext - const Text( + Text( 'Thêm sản phẩm vào danh sách yêu thích để xem lại sau', - style: TextStyle(fontSize: 14.0, color: AppColors.grey500), + style: TextStyle(fontSize: 14.0, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), @@ -475,8 +479,8 @@ class _EmptyState extends StatelessWidget { context.pushReplacement('/products'); }, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.surface, padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xl, vertical: AppSpacing.md, @@ -527,23 +531,25 @@ class _LoadingState extends StatelessWidget { class _ShimmerCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: ProductCardSpecs.elevation, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(ProductCardSpecs.borderRadius), ), child: Shimmer.fromColors( - baseColor: AppColors.grey100, - highlightColor: AppColors.grey50, + baseColor: colorScheme.surfaceContainerHighest, + highlightColor: colorScheme.surfaceContainerLowest, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Image placeholder Expanded( child: Container( - decoration: const BoxDecoration( - color: AppColors.grey100, - borderRadius: BorderRadius.vertical( + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest, + borderRadius: const BorderRadius.vertical( top: Radius.circular(ProductCardSpecs.borderRadius), ), ), @@ -561,7 +567,7 @@ class _ShimmerCard extends StatelessWidget { height: 14.0, width: double.infinity, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(4.0), ), ), @@ -573,7 +579,7 @@ class _ShimmerCard extends StatelessWidget { height: 12.0, width: 80.0, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(4.0), ), ), @@ -585,7 +591,7 @@ class _ShimmerCard extends StatelessWidget { height: 16.0, width: 100.0, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(4.0), ), ), @@ -597,7 +603,7 @@ class _ShimmerCard extends StatelessWidget { height: 36.0, width: double.infinity, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(AppRadius.button), ), ), @@ -626,6 +632,8 @@ class _ErrorState extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Padding( padding: const EdgeInsets.all(AppSpacing.xl), @@ -642,12 +650,12 @@ class _ErrorState extends StatelessWidget { const SizedBox(height: AppSpacing.lg), // Title - const Text( + Text( 'Có lỗi xảy ra', style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), textAlign: TextAlign.center, ), @@ -657,7 +665,7 @@ class _ErrorState extends StatelessWidget { // Error message Text( error.toString(), - style: const TextStyle(fontSize: 14.0, color: AppColors.grey500), + style: TextStyle(fontSize: 14.0, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, maxLines: 3, overflow: TextOverflow.ellipsis, @@ -669,8 +677,8 @@ class _ErrorState extends StatelessWidget { ElevatedButton.icon( onPressed: onRetry, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.surface, padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xl, vertical: AppSpacing.md, diff --git a/lib/features/favorites/presentation/widgets/favorite_product_card.dart b/lib/features/favorites/presentation/widgets/favorite_product_card.dart index 5d07d76..79522f3 100644 --- a/lib/features/favorites/presentation/widgets/favorite_product_card.dart +++ b/lib/features/favorites/presentation/widgets/favorite_product_card.dart @@ -12,7 +12,7 @@ import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import 'package:shimmer/shimmer.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; +import 'package:worker/core/theme/colors.dart'; // Keep for AppColors.danger and AppColors.white import 'package:worker/features/favorites/presentation/providers/favorites_provider.dart'; import 'package:worker/features/products/domain/entities/product.dart'; @@ -76,6 +76,8 @@ class FavoriteProductCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: ProductCardSpecs.elevation, shape: RoundedRectangleBorder( @@ -101,16 +103,16 @@ class FavoriteProductCard extends ConsumerWidget { memCacheWidth: ImageSpecs.productImageCacheWidth, memCacheHeight: ImageSpecs.productImageCacheHeight, placeholder: (context, url) => Shimmer.fromColors( - baseColor: AppColors.grey100, - highlightColor: AppColors.grey50, - child: Container(color: AppColors.grey100), + baseColor: colorScheme.surfaceContainerHighest, + highlightColor: colorScheme.surfaceContainerLowest, + child: Container(color: colorScheme.surfaceContainerHighest), ), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, size: 48.0, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -122,7 +124,7 @@ class FavoriteProductCard extends ConsumerWidget { right: AppSpacing.sm, child: Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, shape: BoxShape.circle, boxShadow: [ BoxShadow( @@ -176,9 +178,9 @@ class FavoriteProductCard extends ConsumerWidget { if (product.erpnextItemCode != null) Text( 'Mã: ${product.erpnextItemCode}', - style: const TextStyle( + style: TextStyle( fontSize: 12.0, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -189,10 +191,10 @@ class FavoriteProductCard extends ConsumerWidget { // Price Text( _formatPrice(product.effectivePrice), - style: const TextStyle( + style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), @@ -208,9 +210,9 @@ class FavoriteProductCard extends ConsumerWidget { context.push('/products/${product.productId}'); }, style: OutlinedButton.styleFrom( - foregroundColor: AppColors.primaryBlue, - side: const BorderSide( - color: AppColors.primaryBlue, + foregroundColor: colorScheme.primary, + side: BorderSide( + color: colorScheme.primary, width: 1.5, ), elevation: 0, diff --git a/lib/features/loyalty/presentation/pages/loyalty_page.dart b/lib/features/loyalty/presentation/pages/loyalty_page.dart index df58182..494c7d4 100644 --- a/lib/features/loyalty/presentation/pages/loyalty_page.dart +++ b/lib/features/loyalty/presentation/pages/loyalty_page.dart @@ -9,7 +9,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/loyalty/presentation/providers/loyalty_points_provider.dart'; /// Loyalty Page @@ -24,18 +23,19 @@ class LoyaltyPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final loyaltyPoints = ref.watch(loyaltyPointsProvider); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( - title: const Text( + title: Text( 'Hội viên thân thiết', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, automaticallyImplyLeading: false, ), @@ -50,17 +50,17 @@ class LoyaltyPage extends ConsumerWidget { const SizedBox(height: 16), // Progress Card - _buildProgressCard(), + _buildProgressCard(colorScheme), const SizedBox(height: 16), // Loyalty Features Menu - ..._buildLoyaltyMenu(context), + ..._buildLoyaltyMenu(context, colorScheme), const SizedBox(height: 16), // Current Benefits Card - _buildBenefitsCard(), + _buildBenefitsCard(colorScheme), ], ), ), @@ -201,7 +201,7 @@ class LoyaltyPage extends ConsumerWidget { } /// Build Progress Card - Widget _buildProgressCard() { + Widget _buildProgressCard(ColorScheme colorScheme) { return Card( elevation: 5, margin: EdgeInsets.zero, @@ -211,12 +211,12 @@ class LoyaltyPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Tiến trình lên hạng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), @@ -227,11 +227,11 @@ class LoyaltyPage extends ConsumerWidget { children: [ Text( 'Hạng hiện tại: DIAMOND', - style: TextStyle(fontSize: 13, color: AppColors.grey500), + style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant), ), Text( 'Hạng kế tiếp: PLATINUM', - style: TextStyle(fontSize: 13, color: AppColors.grey500), + style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant), ), ], ), @@ -244,7 +244,7 @@ class LoyaltyPage extends ConsumerWidget { child: LinearProgressIndicator( value: 0.65, minHeight: 8, - backgroundColor: AppColors.grey100, + backgroundColor: colorScheme.surfaceContainerHighest, valueColor: const AlwaysStoppedAnimation( Color(0xFF4A00E0), ), @@ -257,18 +257,18 @@ class LoyaltyPage extends ConsumerWidget { Center( child: RichText( textAlign: TextAlign.center, - text: const TextSpan( - style: TextStyle(fontSize: 13, color: AppColors.grey500), + text: TextSpan( + style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant), children: [ - TextSpan(text: 'Còn '), + const TextSpan(text: 'Còn '), TextSpan( text: '2,250 điểm', style: TextStyle( fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - TextSpan(text: ' nữa để lên hạng Platinum'), + const TextSpan(text: ' nữa để lên hạng Platinum'), ], ), ), @@ -280,7 +280,7 @@ class LoyaltyPage extends ConsumerWidget { } /// Build Loyalty Menu Items - List _buildLoyaltyMenu(BuildContext context) { + List _buildLoyaltyMenu(BuildContext context, ColorScheme colorScheme) { final menuItems = [ { 'icon': FontAwesomeIcons.gift, @@ -326,7 +326,7 @@ class LoyaltyPage extends ConsumerWidget { elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), - side: const BorderSide(color: AppColors.grey100), + side: BorderSide(color: colorScheme.surfaceContainerHighest), ), child: InkWell( onTap: () { @@ -348,12 +348,12 @@ class LoyaltyPage extends ConsumerWidget { width: 48, height: 48, decoration: BoxDecoration( - color: AppColors.primaryBlue.withValues(alpha: 0.1), + color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( item['icon'] as IconData, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 24, ), ), @@ -367,18 +367,18 @@ class LoyaltyPage extends ConsumerWidget { children: [ Text( item['title'] as String, - style: const TextStyle( + style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( item['subtitle'] as String, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -386,9 +386,9 @@ class LoyaltyPage extends ConsumerWidget { ), // Arrow - const Icon( + Icon( FontAwesomeIcons.chevronRight, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 20, ), ], @@ -400,7 +400,7 @@ class LoyaltyPage extends ConsumerWidget { } /// Build Benefits Card - Widget _buildBenefitsCard() { + Widget _buildBenefitsCard(ColorScheme colorScheme) { return Card( elevation: 1, margin: EdgeInsets.zero, @@ -410,22 +410,23 @@ class LoyaltyPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Quyền lợi hạng Diamond', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), - _buildBenefitItem('Chiết khấu 15% cho tất cả sản phẩm'), - _buildBenefitItem('Giao hàng miễn phí cho đơn từ 5 triệu'), - _buildBenefitItem('Ưu tiên xử lý đơn hàng'), - _buildBenefitItem('Tặng 500 điểm vào ngày sinh nhật'), - _buildBenefitItem('Tư vấn thiết kế miễn phí'), + _buildBenefitItem('Chiết khấu 15% cho tất cả sản phẩm', colorScheme), + _buildBenefitItem('Giao hàng miễn phí cho đơn từ 5 triệu', colorScheme), + _buildBenefitItem('Ưu tiên xử lý đơn hàng', colorScheme), + _buildBenefitItem('Tặng 500 điểm vào ngày sinh nhật', colorScheme), + _buildBenefitItem('Tư vấn thiết kế miễn phí', colorScheme), _buildBenefitItem( 'Mời tham gia sự kiện VIP độc quyền', + colorScheme, isLast: true, ), ], @@ -435,7 +436,7 @@ class LoyaltyPage extends ConsumerWidget { } /// Build Benefit Item - Widget _buildBenefitItem(String text, {bool isLast = false}) { + Widget _buildBenefitItem(String text, ColorScheme colorScheme, {bool isLast = false}) { return Padding( padding: EdgeInsets.only(bottom: isLast ? 0 : 12), child: Row( @@ -446,9 +447,9 @@ class LoyaltyPage extends ConsumerWidget { Expanded( child: Text( text, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.4, ), ), diff --git a/lib/features/loyalty/presentation/pages/points_history_page.dart b/lib/features/loyalty/presentation/pages/points_history_page.dart index 26cb0bf..ea71dc2 100644 --- a/lib/features/loyalty/presentation/pages/points_history_page.dart +++ b/lib/features/loyalty/presentation/pages/points_history_page.dart @@ -27,22 +27,23 @@ class PointsHistoryPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final historyAsync = ref.watch(pointsHistoryProvider); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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( 'Lịch sử điểm', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, ), body: RefreshIndicator( @@ -52,7 +53,7 @@ class PointsHistoryPage extends ConsumerWidget { child: historyAsync.when( data: (entries) { if (entries.isEmpty) { - return _buildEmptyState(); + return _buildEmptyState(colorScheme); } return SingleChildScrollView( @@ -61,27 +62,27 @@ class PointsHistoryPage extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Filter Section - _buildFilterSection(), + _buildFilterSection(colorScheme), const SizedBox(height: 16), // Transaction List ...entries.map( - (entry) => _buildTransactionCard(context, ref, entry), + (entry) => _buildTransactionCard(context, ref, entry, colorScheme), ), ], ), ); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => _buildErrorState(error), + error: (error, stack) => _buildErrorState(error, colorScheme), ), ), ); } /// Build filter section - Widget _buildFilterSection() { + Widget _buildFilterSection(ColorScheme colorScheme) { return Card( elevation: 1, margin: EdgeInsets.zero, @@ -94,21 +95,21 @@ class PointsHistoryPage extends ConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( 'Bộ lọc', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - FaIcon(FontAwesomeIcons.sliders, color: AppColors.primaryBlue, size: 18), + FaIcon(FontAwesomeIcons.sliders, color: colorScheme.primary, size: 18), ], ), const SizedBox(height: 8), - const Text( + Text( 'Thời gian hiệu lực: 01/01/2023 - 31/12/2023', - style: TextStyle(fontSize: 12, color: AppColors.grey500), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), ), ], ), @@ -121,6 +122,7 @@ class PointsHistoryPage extends ConsumerWidget { BuildContext context, WidgetRef ref, LoyaltyPointEntryModel entry, + ColorScheme colorScheme, ) { final dateFormatter = DateFormat('dd/MM/yyyy HH:mm:ss'); final currencyFormatter = NumberFormat.currency( @@ -155,10 +157,10 @@ class PointsHistoryPage extends ConsumerWidget { // Description Text( entry.description, - style: const TextStyle( + style: TextStyle( fontSize: 15, fontWeight: FontWeight.w500, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), const SizedBox(height: 4), @@ -166,9 +168,9 @@ class PointsHistoryPage extends ConsumerWidget { // Timestamp Text( 'Thời gian: ${dateFormatter.format(entry.timestamp)}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), @@ -177,9 +179,9 @@ class PointsHistoryPage extends ConsumerWidget { const SizedBox(height: 2), Text( 'Giao dịch: ${currencyFormatter.format(transactionAmount)}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -203,8 +205,8 @@ class PointsHistoryPage extends ConsumerWidget { horizontal: 12, vertical: 6, ), - side: const BorderSide(color: AppColors.grey500), - foregroundColor: AppColors.grey900, + side: BorderSide(color: colorScheme.onSurfaceVariant), + foregroundColor: colorScheme.onSurface, textStyle: const TextStyle(fontSize: 12), minimumSize: const Size(0, 32), shape: RoundedRectangleBorder( @@ -235,7 +237,7 @@ class PointsHistoryPage extends ConsumerWidget { ? AppColors.success : entry.points < 0 ? AppColors.danger - : AppColors.grey900, + : colorScheme.onSurface, ), ), const SizedBox(height: 2), @@ -243,9 +245,9 @@ class PointsHistoryPage extends ConsumerWidget { // New balance Text( 'Điểm mới: ${entry.balanceAfter}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], @@ -259,7 +261,7 @@ class PointsHistoryPage extends ConsumerWidget { } /// Build empty state - Widget _buildEmptyState() { + Widget _buildEmptyState(ColorScheme colorScheme) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -267,21 +269,21 @@ class PointsHistoryPage extends ConsumerWidget { FaIcon( FontAwesomeIcons.clockRotateLeft, size: 80, - color: AppColors.grey500.withValues(alpha: 0.5), + color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), const SizedBox(height: 16), - const Text( + Text( 'Chưa có lịch sử điểm', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), - const Text( + Text( 'Kéo xuống để làm mới', - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ], ), @@ -289,7 +291,7 @@ class PointsHistoryPage extends ConsumerWidget { } /// Build error state - Widget _buildErrorState(Object error) { + Widget _buildErrorState(Object error, ColorScheme colorScheme) { print(error.toString()); return Center( child: Column( @@ -301,18 +303,18 @@ class PointsHistoryPage extends ConsumerWidget { color: AppColors.danger.withValues(alpha: 0.7), ), const SizedBox(height: 16), - const Text( + Text( 'Có lỗi xảy ra', style: TextStyle( fontSize: 18, 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, ), ], diff --git a/lib/features/loyalty/presentation/pages/points_records_page.dart b/lib/features/loyalty/presentation/pages/points_records_page.dart index 966d043..a896c66 100644 --- a/lib/features/loyalty/presentation/pages/points_records_page.dart +++ b/lib/features/loyalty/presentation/pages/points_records_page.dart @@ -19,30 +19,31 @@ class PointsRecordsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final recordsAsync = ref.watch(filteredPointsRecordsProvider); final filter = ref.watch(pointsRecordsFilterProvider); final selectedStatus = filter.selectedStatus; return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( leading: IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.arrowLeft, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () => context.pop(), ), - title: const Text( + title: Text( 'Danh sách Ghi nhận điểm', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), actions: [ IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.plus, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () { @@ -57,7 +58,7 @@ class PointsRecordsPage extends ConsumerWidget { const SizedBox(width: AppSpacing.sm), ], elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, centerTitle: false, ), body: Column( @@ -68,16 +69,16 @@ class PointsRecordsPage extends ConsumerWidget { child: TextField( decoration: InputDecoration( hintText: 'Mã yêu cầu', - prefixIcon: const Icon(Icons.search, color: AppColors.grey500), + prefixIcon: Icon(Icons.search, color: colorScheme.onSurfaceVariant), filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), ), onChanged: (value) { @@ -95,6 +96,7 @@ class PointsRecordsPage extends ConsumerWidget { _buildFilterChip( context, ref, + colorScheme, label: 'Tất cả', isSelected: selectedStatus == null, onTap: () => @@ -107,6 +109,7 @@ class PointsRecordsPage extends ConsumerWidget { child: _buildFilterChip( context, ref, + colorScheme, label: status.displayName, isSelected: selectedStatus == status, onTap: () => @@ -134,28 +137,28 @@ class PointsRecordsPage extends ConsumerWidget { children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.5, - child: const Center( + child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ FaIcon( FontAwesomeIcons.folderOpen, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: 16), + const SizedBox(height: 16), Text( 'Không có ghi nhận điểm nào', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - SizedBox(height: 8), + const SizedBox(height: 8), Text( 'Không tìm thấy ghi nhận điểm phù hợp', - style: TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), @@ -175,7 +178,7 @@ class PointsRecordsPage extends ConsumerWidget { itemCount: records.length, itemBuilder: (context, index) { final record = records[index]; - return _buildRecordCard(context, record); + return _buildRecordCard(context, colorScheme, record); }, ), ); @@ -202,24 +205,24 @@ class PointsRecordsPage extends ConsumerWidget { color: AppColors.danger, ), const SizedBox(height: 16), - const Text( + Text( 'Có lỗi xảy ra', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), Text( error.toString(), - style: const TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: 16), - const Text( + Text( 'Kéo xuống để thử lại', - style: TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), @@ -237,7 +240,8 @@ class PointsRecordsPage extends ConsumerWidget { Widget _buildFilterChip( BuildContext context, - WidgetRef ref, { + WidgetRef ref, + ColorScheme colorScheme, { required String label, required bool isSelected, required VoidCallback onTap, @@ -247,16 +251,16 @@ class PointsRecordsPage extends ConsumerWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( - color: isSelected ? AppColors.primaryBlue : AppColors.white, + color: isSelected ? colorScheme.primary : colorScheme.surface, borderRadius: BorderRadius.circular(20), border: Border.all( - color: isSelected ? AppColors.primaryBlue : AppColors.grey100, + color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest, ), ), child: Text( label, style: TextStyle( - color: isSelected ? AppColors.white : AppColors.grey900, + color: isSelected ? colorScheme.surface : colorScheme.onSurface, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), @@ -264,7 +268,7 @@ class PointsRecordsPage extends ConsumerWidget { ); } - Widget _buildRecordCard(BuildContext context, PointsRecord record) { + Widget _buildRecordCard(BuildContext context, ColorScheme colorScheme, PointsRecord record) { final currencyFormat = NumberFormat.currency( locale: 'vi_VN', symbol: '₫', @@ -293,29 +297,29 @@ class PointsRecordsPage extends ConsumerWidget { children: [ Text( '#${record.recordId}', - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - _buildStatusBadge(record.status), + _buildStatusBadge(colorScheme, record.status), ], ), const SizedBox(height: 8), Text( 'Ngày gửi: ${DateFormat('dd/MM/yyyy').format(record.submittedAt)}', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( 'Giá trị đơn hàng: ${currencyFormat.format(record.invoiceAmount)}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (record.rejectReason != null) ...[ @@ -354,7 +358,7 @@ class PointsRecordsPage extends ConsumerWidget { ); } - Widget _buildStatusBadge(PointsStatus status) { + Widget _buildStatusBadge(ColorScheme colorScheme, PointsStatus status) { final color = _getStatusColor(status); return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), diff --git a/lib/features/loyalty/presentation/pages/rewards_page.dart b/lib/features/loyalty/presentation/pages/rewards_page.dart index 03075d4..ce62fc9 100644 --- a/lib/features/loyalty/presentation/pages/rewards_page.dart +++ b/lib/features/loyalty/presentation/pages/rewards_page.dart @@ -28,28 +28,29 @@ class RewardsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final filteredGifts = ref.watch(filteredGiftsProvider); final selectedCategory = ref.watch(selectedGiftCategoryProvider); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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( 'Đổi quà tặng', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ IconButton( - icon: const FaIcon(FontAwesomeIcons.circleInfo, color: Colors.black, size: 20), - onPressed: () => _showInfoDialog(context), + icon: FaIcon(FontAwesomeIcons.circleInfo, color: colorScheme.onSurface, size: 20), + onPressed: () => _showInfoDialog(context, colorScheme), ), const SizedBox(width: AppSpacing.sm), ], @@ -71,14 +72,14 @@ class RewardsPage extends ConsumerWidget { // Category Filter Pills SliverToBoxAdapter( - child: _buildCategoryFilter(context, ref, selectedCategory), + child: _buildCategoryFilter(context, ref, selectedCategory, colorScheme), ), // Rewards Grid SliverPadding( padding: const EdgeInsets.fromLTRB(12, 8, 12, 24), sliver: filteredGifts.isEmpty - ? _buildEmptyState() + ? _buildEmptyState(colorScheme) : SliverGrid( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( @@ -91,7 +92,7 @@ class RewardsPage extends ConsumerWidget { final gift = filteredGifts[index]; return RewardCard( gift: gift, - onRedeem: () => _handleRedeemGift(context, ref, gift), + onRedeem: () => _handleRedeemGift(context, ref, gift, colorScheme), ); }, childCount: filteredGifts.length), ), @@ -103,7 +104,7 @@ class RewardsPage extends ConsumerWidget { } /// Show info dialog with usage instructions - void _showInfoDialog(BuildContext context) { + void _showInfoDialog(BuildContext context, ColorScheme colorScheme) { showDialog( context: context, builder: (context) => AlertDialog( @@ -123,18 +124,23 @@ class RewardsPage extends ConsumerWidget { const SizedBox(height: 12), _buildInfoItem( 'Sử dụng điểm tích lũy của bạn để đổi các phần quà giá trị trong danh mục.', + colorScheme, ), _buildInfoItem( 'Bấm vào một phần quà để xem chi tiết và điều kiện áp dụng.', + colorScheme, ), _buildInfoItem( 'Khi xác nhận đổi quà, bạn có thể chọn "Nhận hàng tại Showroom".', + colorScheme, ), _buildInfoItem( 'Nếu chọn "Nhận hàng tại Showroom", bạn sẽ cần chọn Showroom bạn muốn đến nhận từ danh sách thả xuống.', + colorScheme, ), _buildInfoItem( 'Quà đã đổi sẽ được chuyển vào mục "Quà của tôi" (trong trang Hội viên).', + colorScheme, ), ], ), @@ -143,8 +149,8 @@ class RewardsPage extends ConsumerWidget { ElevatedButton( onPressed: () => Navigator.pop(context), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, minimumSize: const Size(double.infinity, 44), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -161,19 +167,19 @@ class RewardsPage extends ConsumerWidget { } /// Build info item with bullet point - Widget _buildInfoItem(String text) { + Widget _buildInfoItem(String text, ColorScheme colorScheme) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Padding( - padding: EdgeInsets.only(top: 6), - child: FaIcon(FontAwesomeIcons.solidCircle, size: 6, color: AppColors.grey500), + Padding( + padding: const EdgeInsets.only(top: 6), + child: FaIcon(FontAwesomeIcons.solidCircle, size: 6, color: colorScheme.onSurfaceVariant), ), const SizedBox(width: 12), Expanded( - child: Text(text, style: TextStyle(fontSize: 14, height: 1.5)), + child: Text(text, style: const TextStyle(fontSize: 14, height: 1.5)), ), ], ), @@ -185,6 +191,7 @@ class RewardsPage extends ConsumerWidget { BuildContext context, WidgetRef ref, GiftCategory? selectedCategory, + ColorScheme colorScheme, ) { return Container( height: 48, @@ -201,6 +208,7 @@ class RewardsPage extends ConsumerWidget { onTap: () { ref.read(selectedGiftCategoryProvider.notifier).clearSelection(); }, + colorScheme: colorScheme, ), const SizedBox(width: 8), @@ -215,6 +223,7 @@ class RewardsPage extends ConsumerWidget { .read(selectedGiftCategoryProvider.notifier) .setCategory(GiftCategory.voucher); }, + colorScheme: colorScheme, ), const SizedBox(width: 8), @@ -229,6 +238,7 @@ class RewardsPage extends ConsumerWidget { .read(selectedGiftCategoryProvider.notifier) .setCategory(GiftCategory.product); }, + colorScheme: colorScheme, ), const SizedBox(width: 8), @@ -243,6 +253,7 @@ class RewardsPage extends ConsumerWidget { .read(selectedGiftCategoryProvider.notifier) .setCategory(GiftCategory.service); }, + colorScheme: colorScheme, ), const SizedBox(width: 8), @@ -257,6 +268,7 @@ class RewardsPage extends ConsumerWidget { .read(selectedGiftCategoryProvider.notifier) .setCategory(GiftCategory.discount); }, + colorScheme: colorScheme, ), ], ), @@ -270,13 +282,14 @@ class RewardsPage extends ConsumerWidget { required String label, required bool isSelected, required VoidCallback onTap, + required ColorScheme colorScheme, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( - color: isSelected ? AppColors.primaryBlue : AppColors.grey100, + color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), ), child: Center( @@ -285,7 +298,7 @@ class RewardsPage extends ConsumerWidget { style: TextStyle( fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, - color: isSelected ? Colors.white : AppColors.grey900, + color: isSelected ? colorScheme.onPrimary : colorScheme.onSurface, ), ), ), @@ -294,7 +307,7 @@ class RewardsPage extends ConsumerWidget { } /// Build empty state - Widget _buildEmptyState() { + Widget _buildEmptyState(ColorScheme colorScheme) { return SliverFillRemaining( child: Center( child: Column( @@ -303,21 +316,21 @@ class RewardsPage extends ConsumerWidget { FaIcon( FontAwesomeIcons.gift, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), const SizedBox(height: 16), - const Text( + Text( 'Không có quà tặng nào', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), - const Text( + Text( 'Vui lòng thử lại sau', - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ], ), @@ -330,6 +343,7 @@ class RewardsPage extends ConsumerWidget { BuildContext context, WidgetRef ref, GiftCatalog gift, + ColorScheme colorScheme, ) { final numberFormat = NumberFormat('#,###', 'vi_VN'); final pointsState = ref.read(loyaltyPointsProvider); @@ -342,15 +356,15 @@ class RewardsPage extends ConsumerWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( + const Text( 'Bạn có chắc muốn đổi quà này?', - style: const TextStyle(fontSize: 14), + style: TextStyle(fontSize: 14), ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(8), ), child: Column( @@ -370,10 +384,10 @@ class RewardsPage extends ConsumerWidget { const Text('Chi phí:', style: TextStyle(fontSize: 13)), Text( '${numberFormat.format(gift.pointsCost)} điểm', - style: const TextStyle( + style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], @@ -411,8 +425,8 @@ class RewardsPage extends ConsumerWidget { _processRedemption(context, ref, gift); }, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, ), child: const Text('Xác nhận'), ), diff --git a/lib/features/loyalty/presentation/widgets/points_balance_card.dart b/lib/features/loyalty/presentation/widgets/points_balance_card.dart index 7a720e7..5a5c83f 100644 --- a/lib/features/loyalty/presentation/widgets/points_balance_card.dart +++ b/lib/features/loyalty/presentation/widgets/points_balance_card.dart @@ -22,6 +22,7 @@ class PointsBalanceCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + // Note: This widget uses brand colors (gradient) that don't change with theme final pointsState = ref.watch(loyaltyPointsProvider); final numberFormat = NumberFormat('#,###', 'vi_VN'); final dateFormat = DateFormat('dd/MM/yyyy', 'vi_VN'); diff --git a/lib/features/loyalty/presentation/widgets/reward_card.dart b/lib/features/loyalty/presentation/widgets/reward_card.dart index 2ff8400..262f39a 100644 --- a/lib/features/loyalty/presentation/widgets/reward_card.dart +++ b/lib/features/loyalty/presentation/widgets/reward_card.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:intl/intl.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/loyalty/domain/entities/gift_catalog.dart'; import 'package:worker/features/loyalty/presentation/providers/loyalty_points_provider.dart'; @@ -27,6 +26,7 @@ class RewardCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final hasEnoughPoints = ref.watch(hasEnoughPointsProvider(gift.pointsCost)); final numberFormat = NumberFormat('#,###', 'vi_VN'); @@ -39,7 +39,7 @@ class RewardCard extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Gift Image - _buildImage(), + _buildImage(colorScheme), // Gift Info Expanded( @@ -51,10 +51,10 @@ class RewardCard extends ConsumerWidget { // Gift Name Text( gift.name, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.3, ), maxLines: 2, @@ -66,9 +66,9 @@ class RewardCard extends ConsumerWidget { if (gift.description != null && gift.description!.isNotEmpty) Text( gift.description!, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, height: 1.2, ), maxLines: 1, @@ -81,10 +81,10 @@ class RewardCard extends ConsumerWidget { // Points Cost (at bottom) Text( '${numberFormat.format(gift.pointsCost)} điểm', - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), const SizedBox(height: 8), @@ -101,8 +101,8 @@ class RewardCard extends ConsumerWidget { FontAwesomeIcons.gift, size: 14, color: hasEnoughPoints && gift.isAvailable - ? Colors.white - : AppColors.grey500, + ? colorScheme.onPrimary + : colorScheme.onSurfaceVariant, ), label: Text( hasEnoughPoints && gift.isAvailable @@ -115,11 +115,11 @@ class RewardCard extends ConsumerWidget { ), style: ElevatedButton.styleFrom( backgroundColor: hasEnoughPoints && gift.isAvailable - ? AppColors.primaryBlue - : AppColors.grey100, + ? colorScheme.primary + : colorScheme.surfaceContainerHighest, foregroundColor: hasEnoughPoints && gift.isAvailable - ? Colors.white - : AppColors.grey500, + ? colorScheme.onPrimary + : colorScheme.onSurfaceVariant, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -141,7 +141,7 @@ class RewardCard extends ConsumerWidget { } /// Build gift image - Widget _buildImage() { + Widget _buildImage(ColorScheme colorScheme) { return SizedBox( height: 120, child: gift.imageUrl != null && gift.imageUrl!.isNotEmpty @@ -149,26 +149,26 @@ class RewardCard extends ConsumerWidget { imageUrl: gift.imageUrl!, fit: BoxFit.cover, placeholder: (context, url) => Container( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), ), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.gift, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ) : Container( - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.gift, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ); diff --git a/lib/features/news/presentation/pages/news_detail_page.dart b/lib/features/news/presentation/pages/news_detail_page.dart index 5f9d430..b479428 100644 --- a/lib/features/news/presentation/pages/news_detail_page.dart +++ b/lib/features/news/presentation/pages/news_detail_page.dart @@ -6,7 +6,6 @@ library; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -17,7 +16,6 @@ import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/news/domain/entities/news_article.dart'; import 'package:worker/features/news/presentation/providers/news_provider.dart'; -import 'package:worker/features/news/presentation/widgets/highlight_box.dart'; import 'package:worker/features/news/presentation/widgets/related_article_card.dart'; /// News Detail Page @@ -49,49 +47,53 @@ class _NewsDetailPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final articleAsync = ref.watch(newsArticleByIdProvider(widget.articleId)); return Scaffold( - backgroundColor: Colors.white, + backgroundColor: colorScheme.surface, appBar: _buildAppBar(context), body: articleAsync.when( data: (article) { if (article == null) { - return _buildNotFoundState(); + return _buildNotFoundState(context); } return _buildContent(context, article); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => _buildErrorState(error.toString()), + error: (error, stack) => _buildErrorState(context, error.toString()), ), ); } /// Build AppBar PreferredSizeWidget _buildAppBar(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, title: Text( 'Chi tiết bài viết', - style: const TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), centerTitle: false, leading: IconButton( - icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20), onPressed: () => context.pop(), ), actions: [ // Share button IconButton( - icon: const FaIcon(FontAwesomeIcons.shareNodes, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.onSurface, size: 20), onPressed: _onShareTap, ), // Bookmark button IconButton( icon: FaIcon( _isBookmarked ? FontAwesomeIcons.solidBookmark : FontAwesomeIcons.bookmark, - color: _isBookmarked ? AppColors.warning : Colors.black, + // Keep AppColors.warning for bookmarked state - semantic status color + color: _isBookmarked ? AppColors.warning : colorScheme.onSurface, size: 20, ), onPressed: _onBookmarkTap, @@ -103,6 +105,7 @@ class _NewsDetailPageState extends ConsumerState { /// Build content Widget _buildContent(BuildContext context, NewsArticle article) { + final colorScheme = Theme.of(context).colorScheme; final relatedArticles = ref .watch(filteredNewsArticlesProvider) .value @@ -122,16 +125,16 @@ class _NewsDetailPageState extends ConsumerState { fit: BoxFit.cover, placeholder: (context, url) => Container( height: 250, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center(child: CircularProgressIndicator()), ), errorWidget: (context, url, error) => Container( height: 250, - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -143,17 +146,17 @@ class _NewsDetailPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Metadata - _buildMetadata(article), + _buildMetadata(context, article), const SizedBox(height: 16), // Title Text( article.title, - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, height: 1.3, ), ), @@ -171,29 +174,29 @@ class _NewsDetailPageState extends ConsumerState { padding: HtmlPaddings.zero, fontSize: FontSize(16), lineHeight: const LineHeight(1.7), - color: const Color(0xFF1E293B), + color: colorScheme.onSurface, ), "h2": Style( fontSize: FontSize(20), fontWeight: FontWeight.w600, - color: const Color(0xFF1E293B), + color: colorScheme.onSurface, margin: Margins.only(top: 32, bottom: 16), ), "h3": Style( fontSize: FontSize(18), fontWeight: FontWeight.w600, - color: const Color(0xFF1E293B), + color: colorScheme.onSurface, margin: Margins.only(top: 24, bottom: 12), ), "p": Style( fontSize: FontSize(16), - color: const Color(0xFF1E293B), + color: colorScheme.onSurface, lineHeight: const LineHeight(1.7), margin: Margins.only(bottom: 16), ), "strong": Style( fontWeight: FontWeight.w600, - color: const Color(0xFF1E293B), + color: colorScheme.onSurface, ), "img": Style( margin: Margins.symmetric(vertical: 16), @@ -206,14 +209,14 @@ class _NewsDetailPageState extends ConsumerState { ), "li": Style( fontSize: FontSize(16), - color: const Color(0xFF1E293B), + color: colorScheme.onSurface, lineHeight: const LineHeight(1.5), margin: Margins.only(bottom: 8), ), "blockquote": Style( - backgroundColor: const Color(0xFFF0F9FF), - border: const Border( - left: BorderSide(color: AppColors.primaryBlue, width: 4), + backgroundColor: colorScheme.primaryContainer, + border: Border( + left: BorderSide(color: colorScheme.primary, width: 4), ), padding: HtmlPaddings.all(16), margin: Margins.symmetric(vertical: 24), @@ -235,18 +238,18 @@ class _NewsDetailPageState extends ConsumerState { const SizedBox(height: 32), // Tags Section - if (article.tags.isNotEmpty) _buildTagsSection(article.tags), + if (article.tags.isNotEmpty) _buildTagsSection(context, article.tags), const SizedBox(height: 32), // Social Actions - _buildSocialActions(article), + _buildSocialActions(context, article), const SizedBox(height: 32), // Related Articles if (relatedArticles != null && relatedArticles.isNotEmpty) - _buildRelatedArticles(relatedArticles), + _buildRelatedArticles(context, relatedArticles), ], ), ), @@ -256,7 +259,9 @@ class _NewsDetailPageState extends ConsumerState { } /// Build metadata - Widget _buildMetadata(NewsArticle article) { + Widget _buildMetadata(BuildContext context, NewsArticle article) { + final colorScheme = Theme.of(context).colorScheme; + return Row( spacing: 16, children: [ @@ -264,27 +269,28 @@ class _NewsDetailPageState extends ConsumerState { Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( - color: AppColors.primaryBlue, + color: colorScheme.primary, borderRadius: BorderRadius.circular(16), ), child: Text( article.category.displayName, - style: const TextStyle( + style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, - color: Colors.white, + color: colorScheme.onPrimary, ), ), ), // Date - _buildMetaItem(FontAwesomeIcons.calendar, article.formattedDate), + _buildMetaItem(context, FontAwesomeIcons.calendar, article.formattedDate), // Reading time - // _buildMetaItem(Icons.schedule, article.readingTimeText), + // _buildMetaItem(context, Icons.schedule, article.readingTimeText), // Views // _buildMetaItem( + // context, // Icons.visibility, // '${article.formattedViewCount} lượt xem', // ), @@ -293,37 +299,41 @@ class _NewsDetailPageState extends ConsumerState { } /// Build metadata item - Widget _buildMetaItem(IconData icon, String text) { + Widget _buildMetaItem(BuildContext context, IconData icon, String text) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisSize: MainAxisSize.min, children: [ - FaIcon(icon, size: 12, color: const Color(0xFF64748B)), + FaIcon(icon, size: 12, color: colorScheme.onSurfaceVariant), const SizedBox(width: 4), Text( text, - style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), ), ], ); } /// Build tags section - Widget _buildTagsSection(List tags) { + Widget _buildTagsSection(BuildContext context, List tags) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFFF8FAFC), + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(AppRadius.lg), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Thẻ liên quan', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 12), @@ -338,15 +348,15 @@ class _NewsDetailPageState extends ConsumerState { vertical: 4, ), decoration: BoxDecoration( - color: Colors.white, - border: Border.all(color: const Color(0xFFE2E8F0)), + color: colorScheme.surface, + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(16), ), child: Text( tag, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF64748B), + color: colorScheme.onSurfaceVariant, ), ), ), @@ -359,84 +369,96 @@ class _NewsDetailPageState extends ConsumerState { } /// Build social actions section - Widget _buildSocialActions(NewsArticle article) { + Widget _buildSocialActions(BuildContext context, NewsArticle article) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.all(16), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border.symmetric( - horizontal: BorderSide(color: Color(0xFFE2E8F0)), + horizontal: BorderSide(color: colorScheme.outlineVariant), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildActionButton( + context, icon: _isLiked ? FontAwesomeIcons.solidHeart : FontAwesomeIcons.heart, onPressed: _onLikeTap, color: _isLiked ? Colors.red : null, ), const SizedBox(width: 8), _buildActionButton( + context, icon: _isBookmarked ? FontAwesomeIcons.solidBookmark : FontAwesomeIcons.bookmark, onPressed: _onBookmarkTap, + // Keep AppColors.warning for bookmarked state - semantic status color color: _isBookmarked ? AppColors.warning : null, ), const SizedBox(width: 8), - _buildActionButton(icon: FontAwesomeIcons.shareNodes, onPressed: _onShareTap), + _buildActionButton(context, icon: FontAwesomeIcons.shareNodes, onPressed: _onShareTap), ], ), ); } /// Build stat item - Widget _buildStatItem(IconData icon, String text) { + Widget _buildStatItem(BuildContext context, IconData icon, String text) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(icon, size: 14, color: const Color(0xFF64748B)), + Icon(icon, size: 14, color: colorScheme.onSurfaceVariant), const SizedBox(width: 4), Text( text, - style: const TextStyle(fontSize: 14, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ], ); } /// Build action button - Widget _buildActionButton({ + Widget _buildActionButton( + BuildContext context, { required IconData icon, required VoidCallback onPressed, Color? color, }) { + final colorScheme = Theme.of(context).colorScheme; + return OutlinedButton( onPressed: onPressed, style: OutlinedButton.styleFrom( padding: const EdgeInsets.all(12), - side: BorderSide(color: color ?? const Color(0xFFE2E8F0), width: 2), + side: BorderSide(color: color ?? colorScheme.outlineVariant, width: 2), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), - child: FaIcon(icon, size: 18, color: color ?? const Color(0xFF64748B)), + child: FaIcon(icon, size: 18, color: color ?? colorScheme.onSurfaceVariant), ); } /// Build related articles section - Widget _buildRelatedArticles(List articles) { + Widget _buildRelatedArticles(BuildContext context, List articles) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFFF8FAFC), + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(AppRadius.lg), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Bài viết liên quan', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), @@ -455,25 +477,27 @@ class _NewsDetailPageState extends ConsumerState { } /// Build not found state - Widget _buildNotFoundState() { + Widget _buildNotFoundState(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - FaIcon(FontAwesomeIcons.fileLines, size: 64, color: AppColors.grey500), + FaIcon(FontAwesomeIcons.fileLines, size: 64, color: colorScheme.onSurfaceVariant), const SizedBox(height: 16), - const Text( + Text( 'Không tìm thấy bài viết', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), - const Text( + Text( 'Bài viết này không tồn tại hoặc đã bị xóa', - style: TextStyle(fontSize: 14, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: 24), @@ -487,19 +511,22 @@ class _NewsDetailPageState extends ConsumerState { } /// Build error state - Widget _buildErrorState(String error) { + Widget _buildErrorState(BuildContext context, String error) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + // Keep AppColors.danger for error state - semantic status color FaIcon(FontAwesomeIcons.circleExclamation, size: 64, color: AppColors.danger), const SizedBox(height: 16), - const Text( + Text( 'Không thể tải bài viết', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), @@ -507,7 +534,7 @@ class _NewsDetailPageState extends ConsumerState { padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( error, - style: const TextStyle(fontSize: 14, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ), diff --git a/lib/features/news/presentation/pages/news_list_page.dart b/lib/features/news/presentation/pages/news_list_page.dart index a368447..3d57d67 100644 --- a/lib/features/news/presentation/pages/news_list_page.dart +++ b/lib/features/news/presentation/pages/news_list_page.dart @@ -38,6 +38,8 @@ class _NewsListPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + // Watch providers final featuredArticleAsync = ref.watch(featuredArticleProvider); final newsArticlesAsync = ref.watch(newsArticlesProvider); @@ -52,7 +54,7 @@ class _NewsListPageState extends ConsumerState { }); return Scaffold( - backgroundColor: Colors.white, + backgroundColor: colorScheme.surface, appBar: _buildAppBar(context), body: RefreshIndicator( onRefresh: () async { @@ -107,23 +109,23 @@ class _NewsListPageState extends ConsumerState { child: SizedBox(height: AppSpacing.xl), ), // Latest News Section - const SliverToBoxAdapter( + SliverToBoxAdapter( child: Padding( - padding: EdgeInsets.symmetric(horizontal: AppSpacing.md), + padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), child: Row( children: [ FaIcon( FontAwesomeIcons.newspaper, size: 16, - color: AppColors.primaryBlue, + color: Theme.of(context).colorScheme.primary, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Mới nhất', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: Theme.of(context).colorScheme.onSurface, ), ), ], @@ -173,13 +175,15 @@ class _NewsListPageState extends ConsumerState { /// Build standard AppBar PreferredSizeWidget _buildAppBar(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, - title: const Text( + title: Text( 'Tin tức & chuyên môn', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.bold, ), @@ -191,24 +195,26 @@ class _NewsListPageState extends ConsumerState { /// Build empty state Widget _buildEmptyState() { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - FaIcon(FontAwesomeIcons.newspaper, size: 64, color: AppColors.grey500), + FaIcon(FontAwesomeIcons.newspaper, size: 64, color: colorScheme.onSurfaceVariant), const SizedBox(height: 16), - const Text( + Text( 'Chưa có tin tức', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), - const Text( + Text( 'Hãy quay lại sau để xem các bài viết mới', - style: TextStyle(fontSize: 14, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ], @@ -218,18 +224,21 @@ class _NewsListPageState extends ConsumerState { /// Build error state Widget _buildErrorState(String error) { + final colorScheme = Theme.of(context).colorScheme; + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + // Keep AppColors.danger for error state - semantic status color FaIcon(FontAwesomeIcons.circleExclamation, size: 64, color: AppColors.danger), const SizedBox(height: 16), - const Text( + Text( 'Không thể tải tin tức', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), @@ -237,7 +246,7 @@ class _NewsListPageState extends ConsumerState { padding: const EdgeInsets.symmetric(horizontal: 32), child: Text( error, - style: const TextStyle(fontSize: 14, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), ), diff --git a/lib/features/news/presentation/widgets/category_filter_chips.dart b/lib/features/news/presentation/widgets/category_filter_chips.dart index 5aaf2e4..92b9a9d 100644 --- a/lib/features/news/presentation/widgets/category_filter_chips.dart +++ b/lib/features/news/presentation/widgets/category_filter_chips.dart @@ -9,7 +9,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/news/domain/entities/blog_category.dart'; import 'package:worker/features/news/presentation/providers/news_provider.dart'; @@ -42,14 +41,14 @@ class CategoryFilterChips extends ConsumerWidget { final categoriesAsync = ref.watch(blogCategoriesProvider); return categoriesAsync.when( - data: (categories) => _buildCategoryChips(categories), - loading: () => _buildLoadingState(), - error: (error, stack) => _buildErrorState(error, ref), + data: (categories) => _buildCategoryChips(context, categories), + loading: () => _buildLoadingState(context), + error: (error, stack) => _buildErrorState(context, error, ref), ); } /// Build category chips with data - Widget _buildCategoryChips(List categories) { + Widget _buildCategoryChips(BuildContext context, List categories) { return SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), @@ -57,6 +56,7 @@ class CategoryFilterChips extends ConsumerWidget { children: [ // "Tất cả" chip _buildCategoryChip( + context, label: 'Tất cả', isSelected: selectedCategoryName == null, onTap: () => onCategorySelected(null), @@ -69,6 +69,7 @@ class CategoryFilterChips extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(right: AppSpacing.sm), child: _buildCategoryChip( + context, label: category.title, isSelected: selectedCategoryName == category.name, onTap: () => onCategorySelected(category.name), @@ -81,7 +82,9 @@ class CategoryFilterChips extends ConsumerWidget { } /// Build loading state with shimmer placeholders - Widget _buildLoadingState() { + Widget _buildLoadingState(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), @@ -93,7 +96,7 @@ class CategoryFilterChips extends ConsumerWidget { width: 80, height: 32, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(24), ), ), @@ -104,7 +107,9 @@ class CategoryFilterChips extends ConsumerWidget { } /// Build error state with retry - Widget _buildErrorState(Object error, WidgetRef ref) { + Widget _buildErrorState(BuildContext context, Object error, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + return SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), @@ -116,25 +121,25 @@ class CategoryFilterChips extends ConsumerWidget { vertical: AppSpacing.sm, ), decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(24), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - FaIcon(FontAwesomeIcons.circleExclamation, size: 16, color: AppColors.grey500), + FaIcon(FontAwesomeIcons.circleExclamation, size: 16, color: colorScheme.onSurfaceVariant), const SizedBox(width: AppSpacing.xs), Text( 'Lỗi tải danh mục', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(width: AppSpacing.xs), GestureDetector( onTap: () => ref.refresh(blogCategoriesProvider), - child: FaIcon(FontAwesomeIcons.arrowsRotate, size: 14, color: AppColors.primaryBlue), + child: FaIcon(FontAwesomeIcons.arrowsRotate, size: 14, color: colorScheme.primary), ), ], ), @@ -145,11 +150,14 @@ class CategoryFilterChips extends ConsumerWidget { } /// Build individual category chip - Widget _buildCategoryChip({ + Widget _buildCategoryChip( + BuildContext context, { required String label, required bool isSelected, required VoidCallback onTap, }) { + final colorScheme = Theme.of(context).colorScheme; + return GestureDetector( onTap: onTap, child: Container( @@ -158,7 +166,7 @@ class CategoryFilterChips extends ConsumerWidget { vertical: AppSpacing.sm, ), decoration: BoxDecoration( - color: isSelected ? AppColors.primaryBlue : AppColors.grey100, + color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(24), ), child: Text( @@ -166,7 +174,7 @@ class CategoryFilterChips extends ConsumerWidget { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: isSelected ? Colors.white : AppColors.grey500, + color: isSelected ? colorScheme.onPrimary : colorScheme.onSurfaceVariant, ), ), ), diff --git a/lib/features/news/presentation/widgets/featured_news_card.dart b/lib/features/news/presentation/widgets/featured_news_card.dart index da09409..6282d12 100644 --- a/lib/features/news/presentation/widgets/featured_news_card.dart +++ b/lib/features/news/presentation/widgets/featured_news_card.dart @@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/news/domain/entities/news_article.dart'; /// Featured News Card @@ -32,14 +31,16 @@ class FeaturedNewsCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.xl), - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.08), @@ -63,16 +64,16 @@ class FeaturedNewsCard extends StatelessWidget { fit: BoxFit.cover, placeholder: (context, url) => Container( height: 200, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center(child: CircularProgressIndicator()), ), errorWidget: (context, url, error) => Container( height: 200, - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -87,10 +88,10 @@ class FeaturedNewsCard extends StatelessWidget { // Title Text( article.title, - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, height: 1.4, ), ), @@ -100,9 +101,9 @@ class FeaturedNewsCard extends StatelessWidget { // Excerpt Text( article.excerpt, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Color(0xFF64748B), + color: colorScheme.onSurfaceVariant, height: 1.5, ), maxLines: 3, @@ -123,18 +124,21 @@ class FeaturedNewsCard extends StatelessWidget { children: [ // Date _buildMetaItem( + context, icon: FontAwesomeIcons.calendar, text: article.formattedDate, ), // // Views // _buildMetaItem( + // context, // icon: Icons.visibility, // text: '${article.formattedViewCount} lượt xem', // ), // // // Reading time // _buildMetaItem( + // context, // icon: Icons.schedule, // text: article.readingTimeText, // ), @@ -149,15 +153,15 @@ class FeaturedNewsCard extends StatelessWidget { vertical: 4, ), decoration: BoxDecoration( - color: AppColors.primaryBlue, + color: colorScheme.primary, borderRadius: BorderRadius.circular(16), ), child: Text( article.category.displayName, - style: const TextStyle( + style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, - color: Colors.white, + color: colorScheme.onPrimary, ), ), ), @@ -173,15 +177,17 @@ class FeaturedNewsCard extends StatelessWidget { } /// Build metadata item - Widget _buildMetaItem({required IconData icon, required String text}) { + Widget _buildMetaItem(BuildContext context, {required IconData icon, required String text}) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisSize: MainAxisSize.min, children: [ - FaIcon(icon, size: 12, color: const Color(0xFF64748B)), + FaIcon(icon, size: 12, color: colorScheme.onSurfaceVariant), const SizedBox(width: 4), Text( text, - style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), ), ], ); diff --git a/lib/features/news/presentation/widgets/news_card.dart b/lib/features/news/presentation/widgets/news_card.dart index 5719641..3159b30 100644 --- a/lib/features/news/presentation/widgets/news_card.dart +++ b/lib/features/news/presentation/widgets/news_card.dart @@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/news/domain/entities/news_article.dart'; /// News Card @@ -31,15 +30,17 @@ class NewsCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.only(bottom: AppSpacing.md), padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, @@ -55,7 +56,7 @@ class NewsCard extends StatelessWidget { placeholder: (context, url) => Container( width: 80, height: 80, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: SizedBox( width: 20, @@ -67,11 +68,11 @@ class NewsCard extends StatelessWidget { errorWidget: (context, url, error) => Container( width: 80, height: 80, - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, size: 24, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -87,10 +88,10 @@ class NewsCard extends StatelessWidget { // Title (max 2 lines) Text( article.title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, height: 1.3, ), maxLines: 2, @@ -102,9 +103,9 @@ class NewsCard extends StatelessWidget { // Excerpt (max 2 lines) Text( article.excerpt, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF64748B), + color: colorScheme.onSurfaceVariant, height: 1.4, ), maxLines: 2, @@ -117,17 +118,17 @@ class NewsCard extends StatelessWidget { Row( children: [ // Date - const FaIcon( + FaIcon( FontAwesomeIcons.calendar, size: 12, - color: Color(0xFF64748B), + color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 4), Text( article.formattedDate, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF64748B), + color: colorScheme.onSurfaceVariant, ), ), @@ -137,14 +138,14 @@ class NewsCard extends StatelessWidget { // Icon( // Icons.visibility, // size: 12, - // color: const Color(0xFF64748B), + // color: colorScheme.onSurfaceVariant, // ), // const SizedBox(width: 4), // Text( // '${article.formattedViewCount} lượt xem', - // style: const TextStyle( + // style: TextStyle( // fontSize: 12, - // color: Color(0xFF64748B), + // color: colorScheme.onSurfaceVariant, // ), // ), ], diff --git a/lib/features/news/presentation/widgets/related_article_card.dart b/lib/features/news/presentation/widgets/related_article_card.dart index d25ee24..e9137a2 100644 --- a/lib/features/news/presentation/widgets/related_article_card.dart +++ b/lib/features/news/presentation/widgets/related_article_card.dart @@ -8,7 +8,6 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/news/domain/entities/news_article.dart'; /// Related Article Card @@ -31,15 +30,17 @@ class RelatedArticleCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return GestureDetector( onTap: onTap, child: Container( margin: const EdgeInsets.only(bottom: AppSpacing.md), padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.lg), - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), ), child: Row( children: [ @@ -54,7 +55,7 @@ class RelatedArticleCard extends StatelessWidget { placeholder: (context, url) => Container( width: 60, height: 60, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: SizedBox( width: 16, @@ -66,11 +67,11 @@ class RelatedArticleCard extends StatelessWidget { errorWidget: (context, url, error) => Container( width: 60, height: 60, - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, size: 20, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -86,10 +87,10 @@ class RelatedArticleCard extends StatelessWidget { // Title (max 2 lines) Text( article.title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF1E293B), + color: colorScheme.onSurface, height: 1.3, ), maxLines: 2, @@ -101,9 +102,9 @@ class RelatedArticleCard extends StatelessWidget { // Metadata Text( '${article.formattedDate} • ${article.formattedViewCount} lượt xem', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF64748B), + color: colorScheme.onSurfaceVariant, ), ), ], diff --git a/lib/features/orders/presentation/pages/order_detail_page.dart b/lib/features/orders/presentation/pages/order_detail_page.dart index b3ea481..d7be466 100644 --- a/lib/features/orders/presentation/pages/order_detail_page.dart +++ b/lib/features/orders/presentation/pages/order_detail_page.dart @@ -33,6 +33,7 @@ class OrderDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final orderDetailAsync = ref.watch(orderDetailProvider(orderId)); return PopScope( @@ -44,19 +45,19 @@ class OrderDetailPage extends ConsumerWidget { } }, child: Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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( 'Chi tiết đơn hàng', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), actions: [ IconButton( - icon: const FaIcon(FontAwesomeIcons.shareNodes, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.onSurface, size: 20), onPressed: () { // TODO: Implement share functionality ScaffoldMessenger.of(context).showSnackBar( @@ -67,7 +68,7 @@ class OrderDetailPage extends ConsumerWidget { }, ), IconButton( - icon: const FaIcon(FontAwesomeIcons.print, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.print, color: colorScheme.onSurface, size: 20), onPressed: () { // TODO: Implement print functionality ScaffoldMessenger.of(context).showSnackBar( @@ -77,8 +78,8 @@ class OrderDetailPage extends ConsumerWidget { ), ], elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, ), body: orderDetailAsync.when( @@ -90,7 +91,7 @@ class OrderDetailPage extends ConsumerWidget { child: Column( children: [ // Status Timeline Card - _buildStatusTimelineCard(orderDetail), + _buildStatusTimelineCard(context, orderDetail), // Delivery/Address Information Card _buildAddressInfoCard(context, ref, orderDetail), @@ -102,10 +103,10 @@ class OrderDetailPage extends ConsumerWidget { _buildInvoicesListCard(context, orderDetail), // Products List Card - _buildProductsListCard(orderDetail), + _buildProductsListCard(context, orderDetail), // Order Summary Card - _buildOrderSummaryCard(orderDetail), + _buildOrderSummaryCard(context, orderDetail), // Payment History Card if (orderDetail.order.totalRemaining > 0) ...[ @@ -145,20 +146,20 @@ class OrderDetailPage extends ConsumerWidget { color: AppColors.danger, ), const SizedBox(height: 16), - const Text( + Text( 'Không thể tải thông tin đơn hàng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), Text( error.toString(), - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -170,8 +171,8 @@ class OrderDetailPage extends ConsumerWidget { icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 16), label: const Text('Thử lại'), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 12, @@ -187,7 +188,8 @@ class OrderDetailPage extends ConsumerWidget { } /// Build Status Timeline Card - Widget _buildStatusTimelineCard(OrderDetail orderDetail) { + Widget _buildStatusTimelineCard(BuildContext context, OrderDetail orderDetail) { + final colorScheme = Theme.of(context).colorScheme; final order = orderDetail.order; final timeline = orderDetail.timeline; @@ -203,10 +205,10 @@ class OrderDetailPage extends ConsumerWidget { // Order Number and Status Badge Text( '#${order.name}', - style: const TextStyle( + style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), @@ -222,6 +224,7 @@ class OrderDetailPage extends ConsumerWidget { final isLast = index == timeline.length - 1; return _buildTimelineItem( + context, title: item.label, date: item.value, status: item.status, @@ -235,12 +238,14 @@ class OrderDetailPage extends ConsumerWidget { } /// Build Timeline Item - Widget _buildTimelineItem({ + Widget _buildTimelineItem( + BuildContext context, { required String title, String? date, required String status, // 'Success', 'Warning', 'Secondary', etc. required bool isLast, }) { + final colorScheme = Theme.of(context).colorScheme; final statusColor = StatusColor.fromString(status) ?? StatusColor.secondary; Color iconColor; @@ -248,17 +253,17 @@ class OrderDetailPage extends ConsumerWidget { IconData iconData; if (statusColor == StatusColor.success) { - iconColor = Colors.white; + iconColor = colorScheme.onPrimary; iconBgColor = statusColor.color; iconData = FontAwesomeIcons.check; } else if (statusColor == StatusColor.warning) { - iconColor = Colors.white; + iconColor = colorScheme.onPrimary; iconBgColor = statusColor.color; iconData = FontAwesomeIcons.gear; } else { // Secondary or other - iconColor = AppColors.grey500; - iconBgColor = AppColors.grey100; + iconColor = colorScheme.onSurfaceVariant; + iconBgColor = colorScheme.surfaceContainerHighest; iconData = _getIconForTitle(title); } @@ -281,7 +286,7 @@ class OrderDetailPage extends ConsumerWidget { Container( width: 2, height: 40, - color: AppColors.grey100, + color: colorScheme.outlineVariant, margin: const EdgeInsets.symmetric(vertical: 4), ), ], @@ -298,18 +303,18 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( date ?? 'Đang cập nhật', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -361,6 +366,7 @@ class OrderDetailPage extends ConsumerWidget { WidgetRef ref, OrderDetail orderDetail, ) { + final colorScheme = Theme.of(context).colorScheme; final order = orderDetail.order; final shippingAddress = orderDetail.shippingAddress; final dateFormatter = DateFormat('dd/MM/yyyy'); @@ -374,20 +380,20 @@ class OrderDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.truck, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Thông tin giao hàng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -399,12 +405,12 @@ class OrderDetailPage extends ConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( 'Địa chỉ nhận hàng', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Builder( @@ -482,17 +488,17 @@ class OrderDetailPage extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, - side: const BorderSide(color: AppColors.grey100), + side: BorderSide(color: colorScheme.outlineVariant), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), - child: const Text( + child: Text( 'Cập nhật', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ); @@ -510,7 +516,7 @@ class OrderDetailPage extends ConsumerWidget { padding: const EdgeInsets.all(12), width: double.infinity, decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Column( @@ -518,26 +524,26 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( shippingAddress.addressTitle, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( shippingAddress.phone, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( '${shippingAddress.addressLine1}\n${shippingAddress.wardName}, ${shippingAddress.cityName}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -547,42 +553,42 @@ class OrderDetailPage extends ConsumerWidget { const SizedBox(height: 16), // Pickup Date - const Text( + Text( 'Ngày lấy hàng', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( dateFormatter.format(DateTime.parse(order.deliveryDate)), - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (order.description.isNotEmpty) ...[ const SizedBox(height: 16), // Notes - const Text( + Text( 'Ghi chú', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( order.description, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -598,6 +604,7 @@ class OrderDetailPage extends ConsumerWidget { WidgetRef ref, OrderDetail orderDetail, ) { + final colorScheme = Theme.of(context).colorScheme; final billingAddress = orderDetail.billingAddress; return Card( @@ -613,20 +620,20 @@ class OrderDetailPage extends ConsumerWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.fileInvoice, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Thông tin hóa đơn', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -706,17 +713,17 @@ class OrderDetailPage extends ConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, - side: const BorderSide(color: AppColors.grey100), + side: BorderSide(color: colorScheme.outlineVariant), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), - child: const Text( + child: Text( 'Cập nhật', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ); @@ -734,7 +741,7 @@ class OrderDetailPage extends ConsumerWidget { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Column( @@ -742,10 +749,10 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( billingAddress.addressTitle, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (billingAddress.taxCode != null && @@ -753,34 +760,34 @@ class OrderDetailPage extends ConsumerWidget { const SizedBox(height: 2), Text( 'Mã số thuế: ${billingAddress.taxCode}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], const SizedBox(height: 2), Text( 'Số điện thoại: ${billingAddress.phone}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 2), Text( 'Email: ${billingAddress.email}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 2), Text( 'Địa chỉ: ${billingAddress.addressLine1}, ${billingAddress.wardName}, ${billingAddress.cityName}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -794,6 +801,7 @@ class OrderDetailPage extends ConsumerWidget { /// Build Invoices List Card Widget _buildInvoicesListCard(BuildContext context, OrderDetail orderDetail) { + final colorScheme = Theme.of(context).colorScheme; final invoices = orderDetail.invoices; if (invoices.isEmpty) { @@ -809,20 +817,20 @@ class OrderDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.fileInvoiceDollar, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Hóa đơn đã xuất', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -832,6 +840,7 @@ class OrderDetailPage extends ConsumerWidget { // Invoice Items (Mock data for now) ...invoices.map((e) => _buildInvoiceItem( + context, invoiceId: e.name, date: e.postingDate, amount: e.grandTotal.toVNCurrency, @@ -849,19 +858,21 @@ class OrderDetailPage extends ConsumerWidget { } /// Build Invoice Item - Widget _buildInvoiceItem({ + Widget _buildInvoiceItem( + BuildContext context, { required String invoiceId, required String date, required String amount, required VoidCallback onTap, }) { + final colorScheme = Theme.of(context).colorScheme; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -870,17 +881,17 @@ class OrderDetailPage extends ConsumerWidget { width: 40, height: 40, decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [AppColors.primaryBlue, Color(0xFF1d4ed8)], + gradient: LinearGradient( + colors: [colorScheme.primary, colorScheme.primaryContainer], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(8), ), - child: const Center( + child: Center( child: FaIcon( FontAwesomeIcons.fileInvoice, - color: Colors.white, + color: colorScheme.onPrimary, size: 18, ), ), @@ -892,18 +903,18 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( invoiceId, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( 'Ngày xuất: $date', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -919,10 +930,10 @@ class OrderDetailPage extends ConsumerWidget { ), ), const SizedBox(width: 8), - const FaIcon( + FaIcon( FontAwesomeIcons.chevronRight, size: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ], ), @@ -931,7 +942,8 @@ class OrderDetailPage extends ConsumerWidget { } /// Build Products List Card - Widget _buildProductsListCard(OrderDetail orderDetail) { + Widget _buildProductsListCard(BuildContext context, OrderDetail orderDetail) { + final colorScheme = Theme.of(context).colorScheme; final items = orderDetail.items; return Card( @@ -943,20 +955,20 @@ class OrderDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.box, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Sản phẩm đặt hàng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -969,7 +981,7 @@ class OrderDetailPage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -987,7 +999,7 @@ class OrderDetailPage extends ConsumerWidget { placeholder: (context, url) => Container( width: 60, height: 60, - color: AppColors.grey50, + color: colorScheme.surfaceContainerHighest, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), @@ -995,10 +1007,10 @@ class OrderDetailPage extends ConsumerWidget { errorWidget: (context, url, error) => Container( width: 60, height: 60, - color: AppColors.grey50, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 28, ), ), @@ -1009,12 +1021,12 @@ class OrderDetailPage extends ConsumerWidget { width: 60, height: 60, decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(6), ), - child: const FaIcon( + child: FaIcon( FontAwesomeIcons.image, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 28, ), ), @@ -1028,18 +1040,18 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( item.itemName, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( 'Mã: ${item.itemCode}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), @@ -1049,19 +1061,19 @@ class OrderDetailPage extends ConsumerWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Số lượng:', style: TextStyle( fontSize: 11, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Text( '${item.qtyOfSm} m²', - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -1071,9 +1083,9 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( '${item.price.toVNCurrency}/m²', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Text( @@ -1102,7 +1114,8 @@ class OrderDetailPage extends ConsumerWidget { } /// Build Order Summary Card - Widget _buildOrderSummaryCard(OrderDetail orderDetail) { + Widget _buildOrderSummaryCard(BuildContext context, OrderDetail orderDetail) { + final colorScheme = Theme.of(context).colorScheme; final order = orderDetail.order; final paymentTerms = orderDetail.paymentTerms; @@ -1115,20 +1128,20 @@ class OrderDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.receipt, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Tổng kết đơn hàng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -1136,11 +1149,12 @@ class OrderDetailPage extends ConsumerWidget { const SizedBox(height: 16), - _buildSummaryRow('Tổng tiền hàng:', order.total.toVNCurrency), + _buildSummaryRow(context, 'Tổng tiền hàng:', order.total.toVNCurrency), const SizedBox(height: 8), if (order.totalRemaining > 0) ...[ _buildSummaryRow( + context, 'Còn lại:', order.totalRemaining.toVNCurrency, valueColor: AppColors.warning, @@ -1151,6 +1165,7 @@ class OrderDetailPage extends ConsumerWidget { const Divider(height: 24), _buildSummaryRow( + context, 'Tổng cộng:', order.grandTotal.toVNCurrency, isTotal: true, @@ -1159,35 +1174,35 @@ class OrderDetailPage extends ConsumerWidget { const Divider(height: 24), // Payment Terms - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.creditCard, size: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(width: 6), + const SizedBox(width: 6), Text( 'Điều khoản thanh toán:', - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ], ), const SizedBox(height: 4), Text( paymentTerms.name, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( paymentTerms.description, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, height: 1.4, ), ), @@ -1200,6 +1215,7 @@ class OrderDetailPage extends ConsumerWidget { /// Build Payment History Card Widget _buildPaymentHistoryCard(BuildContext context, OrderDetail orderDetail) { + final colorScheme = Theme.of(context).colorScheme; final order = orderDetail.order; final payments = orderDetail.payments; @@ -1214,20 +1230,20 @@ class OrderDetailPage extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, spacing: 16, children: [ - const Row( + Row( children: [ FaIcon( FontAwesomeIcons.clockRotateLeft, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Lịch sử thanh toán', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -1235,6 +1251,7 @@ class OrderDetailPage extends ConsumerWidget { ...payments.map((e) => _buildPaymentItem( + context, paymentId: e.name, date: e.creationDate, amount: e.amount.toVNCurrency, @@ -1250,20 +1267,20 @@ class OrderDetailPage extends ConsumerWidget { // Payment Summary Container( padding: const EdgeInsets.only(top: 12), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - top: BorderSide(color: AppColors.grey100, width: 1), + top: BorderSide(color: colorScheme.outlineVariant, width: 1), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( 'Còn lại:', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Text( @@ -1284,19 +1301,21 @@ class OrderDetailPage extends ConsumerWidget { } /// Build Payment Item - Widget _buildPaymentItem({ + Widget _buildPaymentItem( + BuildContext context, { required String paymentId, required String date, required String amount, required VoidCallback onTap, }) { + final colorScheme = Theme.of(context).colorScheme; return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -1323,18 +1342,18 @@ class OrderDetailPage extends ConsumerWidget { children: [ Text( paymentId, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 2), Text( date, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -1350,10 +1369,10 @@ class OrderDetailPage extends ConsumerWidget { ), ), const SizedBox(width: 8), - const FaIcon( + FaIcon( FontAwesomeIcons.chevronRight, size: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ], ), @@ -1363,11 +1382,13 @@ class OrderDetailPage extends ConsumerWidget { /// Build Summary Row Widget _buildSummaryRow( + BuildContext context, String label, String value, { bool isTotal = false, Color? valueColor, }) { + final colorScheme = Theme.of(context).colorScheme; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -1376,7 +1397,7 @@ class OrderDetailPage extends ConsumerWidget { style: TextStyle( fontSize: isTotal ? 14 : 14, fontWeight: isTotal ? FontWeight.w600 : FontWeight.normal, - color: isTotal ? AppColors.grey900 : AppColors.grey500, + color: isTotal ? colorScheme.onSurface : colorScheme.onSurfaceVariant, ), ), Text( @@ -1385,7 +1406,7 @@ class OrderDetailPage extends ConsumerWidget { fontSize: isTotal ? 16 : 14, fontWeight: isTotal ? FontWeight.w700 : FontWeight.w500, color: - valueColor ?? (isTotal ? AppColors.danger : AppColors.grey900), + valueColor ?? (isTotal ? AppColors.danger : colorScheme.onSurface), ), ), ], @@ -1394,12 +1415,13 @@ class OrderDetailPage extends ConsumerWidget { /// Build Action Buttons Widget _buildActionButtons(BuildContext context, OrderDetail orderDetail) { + final colorScheme = Theme.of(context).colorScheme; return Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.1), + color: colorScheme.shadow.withValues(alpha: 0.1), blurRadius: 15, offset: const Offset(0, -4), ), @@ -1421,8 +1443,8 @@ class OrderDetailPage extends ConsumerWidget { label: const Text('Thanh toán'), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -1445,10 +1467,10 @@ class OrderDetailPage extends ConsumerWidget { style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), side: BorderSide( - color: AppColors.grey100, + color: colorScheme.outlineVariant, width: 2, ), - foregroundColor: AppColors.grey900, + foregroundColor: colorScheme.onSurface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -1487,7 +1509,7 @@ class OrderDetailPage extends ConsumerWidget { onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( backgroundColor: AppColors.danger, - foregroundColor: Colors.white, + foregroundColor: Theme.of(context).colorScheme.onPrimary, ), child: const Text('Hủy đơn'), ), @@ -1499,7 +1521,7 @@ class OrderDetailPage extends ConsumerWidget { if (confirmed == true && context.mounted) { // Show loading indicator ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( + SnackBar( content: Row( children: [ SizedBox( @@ -1507,14 +1529,14 @@ class OrderDetailPage extends ConsumerWidget { height: 20, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.white), + valueColor: AlwaysStoppedAnimation(Theme.of(context).colorScheme.onPrimary), ), ), - SizedBox(width: 12), - Text('Đang hủy đơn hàng...'), + const SizedBox(width: 12), + const Text('Đang hủy đơn hàng...'), ], ), - duration: Duration(seconds: 2), + duration: const Duration(seconds: 2), ), ); diff --git a/lib/features/orders/presentation/pages/order_success_page.dart b/lib/features/orders/presentation/pages/order_success_page.dart index 6790a2b..ca5e109 100644 --- a/lib/features/orders/presentation/pages/order_success_page.dart +++ b/lib/features/orders/presentation/pages/order_success_page.dart @@ -33,11 +33,12 @@ class OrderSuccessPage extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final now = DateTime.now(); final dateFormat = DateFormat('dd/MM/yyyy HH:mm'); return Scaffold( - backgroundColor: Colors.white, + backgroundColor: colorScheme.surface, body: SafeArea( child: Center( child: SingleChildScrollView( @@ -67,10 +68,10 @@ class OrderSuccessPage extends StatelessWidget { isNegotiation ? 'Gửi yêu cầu thành công!' : 'Tạo đơn hàng thành công!', - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), textAlign: TextAlign.center, ), @@ -82,9 +83,9 @@ class OrderSuccessPage extends StatelessWidget { isNegotiation ? 'Chúng tôi sẽ liên hệ với bạn để đàm phán giá trong vòng 24 giờ.' : 'Cảm ơn bạn đã đặt hàng. Chúng tôi sẽ liên hệ xác nhận trong vòng 24 giờ.', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -96,7 +97,7 @@ class OrderSuccessPage extends StatelessWidget { width: double.infinity, padding: const EdgeInsets.all(AppSpacing.md), decoration: BoxDecoration( - color: const Color(0xFFF4F6F8), + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(AppRadius.card), ), child: Column( @@ -104,20 +105,20 @@ class OrderSuccessPage extends StatelessWidget { // Order Number Column( children: [ - const Text( + Text( 'Mã đơn hàng', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( orderNumber, - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], @@ -127,6 +128,7 @@ class OrderSuccessPage extends StatelessWidget { // Order Date _buildInfoRow( + context, 'Ngày đặt', dateFormat.format(now), ), @@ -136,12 +138,13 @@ class OrderSuccessPage extends StatelessWidget { // Total Amount if (total != null) _buildInfoRow( + context, 'Tổng tiền', _formatCurrency(total!), - valueStyle: const TextStyle( + valueStyle: TextStyle( fontSize: 14, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), @@ -150,6 +153,7 @@ class OrderSuccessPage extends StatelessWidget { // Payment Method if (paymentMethod != null && !isNegotiation) _buildInfoRow( + context, 'Phương thức thanh toán', paymentMethod!, ), @@ -159,14 +163,13 @@ class OrderSuccessPage extends StatelessWidget { // Status _buildInfoRow( + context, 'Trạng thái', isNegotiation ? 'Chờ đàm phán' : 'Chờ xác nhận', - valueStyle: TextStyle( + valueStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: isNegotiation - ? AppColors.warning - : AppColors.warning, + color: AppColors.warning, ), ), ], @@ -195,8 +198,8 @@ class OrderSuccessPage extends StatelessWidget { ), ), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, padding: const EdgeInsets.symmetric(vertical: 16), elevation: 0, shape: RoundedRectangleBorder( @@ -225,9 +228,9 @@ class OrderSuccessPage extends StatelessWidget { ), ), style: OutlinedButton.styleFrom( - foregroundColor: AppColors.grey900, + foregroundColor: colorScheme.onSurface, side: BorderSide( - color: AppColors.grey100, + color: colorScheme.outlineVariant, width: 1.5, ), padding: const EdgeInsets.symmetric(vertical: 16), @@ -247,26 +250,29 @@ class OrderSuccessPage extends StatelessWidget { /// Build info row Widget _buildInfoRow( + BuildContext context, String label, String value, { TextStyle? valueStyle, }) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Text( value, style: valueStyle ?? - const TextStyle( + TextStyle( fontSize: 14, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ], diff --git a/lib/features/orders/presentation/pages/orders_page.dart b/lib/features/orders/presentation/pages/orders_page.dart index ce2177d..f2c71c0 100644 --- a/lib/features/orders/presentation/pages/orders_page.dart +++ b/lib/features/orders/presentation/pages/orders_page.dart @@ -50,24 +50,25 @@ class _OrdersPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final ordersAsync = ref.watch(ordersProvider); final selectedStatus = ref.watch(selectedOrderStatusProvider); final searchQuery = ref.watch(orderSearchQueryProvider); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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( 'Danh sách đơn hàng', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: const [SizedBox(width: AppSpacing.sm)], ), @@ -82,9 +83,9 @@ class _OrdersPageState extends ConsumerState { pinned: true, delegate: _SearchBarDelegate( child: Container( - color: const Color(0xFFF4F6F8), + color: colorScheme.surfaceContainerLowest, padding: const EdgeInsets.all(16), - child: _buildSearchBar(), + child: _buildSearchBar(colorScheme), ), ), ), @@ -94,8 +95,8 @@ class _OrdersPageState extends ConsumerState { pinned: true, delegate: _FilterPillsDelegate( child: Container( - color: const Color(0xFFF4F6F8), - child: _buildFilterPills(selectedStatus), + color: colorScheme.surfaceContainerLowest, + child: _buildFilterPills(selectedStatus, colorScheme), ), ), ), @@ -134,7 +135,7 @@ class _OrdersPageState extends ConsumerState { }); if (filtered.isEmpty) { - return _buildEmptyState(); + return _buildEmptyState(colorScheme); } return SliverList( delegate: SliverChildBuilderDelegate((context, index) { @@ -149,7 +150,7 @@ class _OrdersPageState extends ConsumerState { ); }, loading: () => _buildLoadingState(), - error: (error, stack) => _buildErrorState(error), + error: (error, stack) => _buildErrorState(error, colorScheme), ), ), ], @@ -159,14 +160,14 @@ class _OrdersPageState extends ConsumerState { } /// Build search bar - Widget _buildSearchBar() { + Widget _buildSearchBar(ColorScheme colorScheme) { return Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withValues(alpha: 0.05), + color: colorScheme.onSurface.withValues(alpha: 0.05), blurRadius: 4, offset: const Offset(0, 2), ), @@ -176,20 +177,20 @@ class _OrdersPageState extends ConsumerState { controller: _searchController, decoration: InputDecoration( hintText: 'Mã đơn hàng', - hintStyle: const TextStyle(color: AppColors.grey500, fontSize: 14), - prefixIcon: const Padding( - padding: EdgeInsets.all(14), + hintStyle: TextStyle(color: colorScheme.onSurfaceVariant, fontSize: 14), + prefixIcon: Padding( + padding: const EdgeInsets.all(14), child: FaIcon( FontAwesomeIcons.magnifyingGlass, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 18, ), ), suffixIcon: _searchController.text.isNotEmpty ? IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.xmark, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 18, ), onPressed: () { @@ -211,7 +212,7 @@ class _OrdersPageState extends ConsumerState { } /// Build filter pills (dynamically from cached status list) - Widget _buildFilterPills(String? selectedStatus) { + Widget _buildFilterPills(String? selectedStatus, ColorScheme colorScheme) { final statusListAsync = ref.watch(orderStatusListProvider); return SizedBox( @@ -229,6 +230,7 @@ class _OrdersPageState extends ConsumerState { onTap: () { ref.read(selectedOrderStatusProvider.notifier).clearSelection(); }, + colorScheme: colorScheme, ), const SizedBox(width: 8), @@ -244,6 +246,7 @@ class _OrdersPageState extends ConsumerState { .read(selectedOrderStatusProvider.notifier) .selectStatus(status.label); }, + colorScheme: colorScheme, ), ); }), @@ -260,6 +263,7 @@ class _OrdersPageState extends ConsumerState { label: 'Tất cả', isSelected: true, onTap: () {}, + colorScheme: colorScheme, ), ], ); @@ -274,6 +278,7 @@ class _OrdersPageState extends ConsumerState { label: 'Tất cả', isSelected: true, onTap: () {}, + colorScheme: colorScheme, ), ], ); @@ -287,13 +292,14 @@ class _OrdersPageState extends ConsumerState { required String label, required bool isSelected, required VoidCallback onTap, + required ColorScheme colorScheme, }) { return GestureDetector( onTap: onTap, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( - color: isSelected ? AppColors.primaryBlue : AppColors.grey100, + color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), ), child: Center( @@ -302,7 +308,7 @@ class _OrdersPageState extends ConsumerState { style: TextStyle( fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, - color: isSelected ? Colors.white : AppColors.grey900, + color: isSelected ? colorScheme.onPrimary : colorScheme.onSurface, ), ), ), @@ -311,7 +317,7 @@ class _OrdersPageState extends ConsumerState { } /// Build empty state - Widget _buildEmptyState() { + Widget _buildEmptyState(ColorScheme colorScheme) { return SliverFillRemaining( child: Center( child: Column( @@ -320,21 +326,21 @@ class _OrdersPageState extends ConsumerState { FaIcon( FontAwesomeIcons.receipt, size: 80, - color: AppColors.grey500.withValues(alpha: 0.5), + color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5), ), const SizedBox(height: 16), - const Text( + Text( 'Không có đơn hàng nào', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), - const Text( + Text( 'Thử tìm kiếm với từ khóa khác', - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ], ), @@ -350,7 +356,7 @@ class _OrdersPageState extends ConsumerState { } /// Build error state - Widget _buildErrorState(Object error) { + Widget _buildErrorState(Object error, ColorScheme colorScheme) { return SliverFillRemaining( child: Center( child: Column( @@ -362,18 +368,18 @@ class _OrdersPageState extends ConsumerState { color: AppColors.danger.withValues(alpha: 0.7), ), const SizedBox(height: 16), - const Text( + Text( 'Có lỗi xảy ra', style: TextStyle( fontSize: 18, 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, ), ], diff --git a/lib/features/orders/presentation/pages/payment_detail_page.dart b/lib/features/orders/presentation/pages/payment_detail_page.dart index 9097d7e..4a7176b 100644 --- a/lib/features/orders/presentation/pages/payment_detail_page.dart +++ b/lib/features/orders/presentation/pages/payment_detail_page.dart @@ -30,26 +30,27 @@ class PaymentDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final invoicesAsync = ref.watch(invoicesProvider); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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( 'Chi tiết Hóa đơn', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ IconButton( - icon: const FaIcon(FontAwesomeIcons.shareNodes, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.onSurface, size: 20), onPressed: () { // TODO: Implement share functionality ScaffoldMessenger.of( @@ -75,6 +76,7 @@ class PaymentDetailPage extends ConsumerWidget { children: [ // Invoice Header Card _buildInvoiceHeader( + context, invoice.invoiceNumber, invoice.orderId, invoice.issueDate, @@ -87,6 +89,7 @@ class PaymentDetailPage extends ConsumerWidget { // Dates and Customer Info Card _buildCustomerInfo( + context, invoice.issueDate, invoice.dueDate, invoice.isOverdue, @@ -94,18 +97,19 @@ class PaymentDetailPage extends ConsumerWidget { // Product List Card - _buildProductList(), + _buildProductList(context), // Payment History Card _buildPaymentHistory( + context, invoice.amountPaid, invoice.issueDate ), // Download Section Card - _buildDownloadSection(invoice.invoiceNumber), + _buildDownloadSection(context, invoice.invoiceNumber), // Support Button @@ -126,11 +130,11 @@ class PaymentDetailPage extends ConsumerWidget { vertical: 16, horizontal: 16, ), - side: const BorderSide( - color: AppColors.grey100, + side: BorderSide( + color: colorScheme.outlineVariant, width: 2, ), - foregroundColor: AppColors.grey900, + foregroundColor: colorScheme.onSurface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), @@ -172,10 +176,10 @@ class PaymentDetailPage extends ConsumerWidget { (invoice.status == InvoiceStatus.paid || invoice.isPaid) ? AppColors.success - : AppColors.primaryBlue, + : colorScheme.primary, disabledBackgroundColor: AppColors.success, - foregroundColor: Colors.white, - disabledForegroundColor: Colors.white, + foregroundColor: colorScheme.surface, + disabledForegroundColor: colorScheme.surface, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -201,9 +205,10 @@ class PaymentDetailPage extends ConsumerWidget { const SizedBox(height: 16), Text( 'Không tìm thấy hóa đơn', - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, + color: colorScheme.onSurface, ), ), const SizedBox(height: 24), @@ -220,6 +225,7 @@ class PaymentDetailPage extends ConsumerWidget { /// Build invoice header section Widget _buildInvoiceHeader( + BuildContext context, String invoiceNumber, String? orderId, DateTime issueDate, @@ -228,6 +234,8 @@ class PaymentDetailPage extends ConsumerWidget { double amountPaid, double amountRemaining, ) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -240,19 +248,19 @@ class PaymentDetailPage extends ConsumerWidget { children: [ Text( '#$invoiceNumber', - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), if (orderId != null) Text( 'Đơn hàng: #$orderId | Ngày đặt: ${_formatDate(issueDate)}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 12), @@ -269,17 +277,19 @@ class PaymentDetailPage extends ConsumerWidget { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(8), ), child: Column( children: [ _buildSummaryRow( + context, 'Tổng tiền hóa đơn:', totalAmount.toVNCurrency, ), const SizedBox(height: 12), _buildSummaryRow( + context, 'Đã thanh toán:', amountPaid.toVNCurrency, ), @@ -288,6 +298,7 @@ class PaymentDetailPage extends ConsumerWidget { child: Divider(height: 2, thickness: 2), ), _buildSummaryRow( + context, 'Còn lại:', amountRemaining.toVNCurrency, isHighlighted: true, @@ -306,10 +317,13 @@ class PaymentDetailPage extends ConsumerWidget { /// Build customer info and dates section Widget _buildCustomerInfo( + BuildContext context, DateTime issueDate, DateTime dueDate, bool isOverdue, ) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -324,20 +338,20 @@ class PaymentDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Ngày đặt hàng', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( _formatDate(issueDate), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -347,11 +361,11 @@ class PaymentDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Hạn thanh toán', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), @@ -362,7 +376,7 @@ class PaymentDetailPage extends ConsumerWidget { fontWeight: FontWeight.w600, color: isOverdue ? AppColors.danger - : AppColors.grey900, + : colorScheme.onSurface, ), ), ], @@ -377,28 +391,28 @@ class PaymentDetailPage extends ConsumerWidget { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(8), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Thông tin khách hàng', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), - const Text( + Text( 'Công ty TNHH Xây Dựng Minh An\n' 'Địa chỉ: 123 Nguyễn Văn Linh, Quận 7, TP.HCM\n' 'SĐT: 0901234567 | Email: contact@minhan.com', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, height: 1.5, ), ), @@ -412,7 +426,9 @@ class PaymentDetailPage extends ConsumerWidget { } /// Build product list section - Widget _buildProductList() { + Widget _buildProductList(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + // Mock product data - in real app, this would come from order items final products = [ { @@ -445,14 +461,14 @@ class PaymentDetailPage extends ConsumerWidget { children: [ Row( children: [ - FaIcon(FontAwesomeIcons.box, color: AppColors.primaryBlue, size: 18), + FaIcon(FontAwesomeIcons.box, color: colorScheme.primary, size: 18), const SizedBox(width: 8), - const Text( + Text( 'Danh sách sản phẩm', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -465,7 +481,7 @@ class PaymentDetailPage extends ConsumerWidget { margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -476,12 +492,12 @@ class PaymentDetailPage extends ConsumerWidget { width: 60, height: 60, decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(8), ), - child: const FaIcon( + child: FaIcon( FontAwesomeIcons.image, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 22, ), ), @@ -493,18 +509,18 @@ class PaymentDetailPage extends ConsumerWidget { children: [ Text( product['name']!, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( 'SKU: ${product['sku']}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), @@ -514,17 +530,17 @@ class PaymentDetailPage extends ConsumerWidget { children: [ Text( 'Số lượng: ${product['quantity']}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Text( product['price']!, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -545,9 +561,11 @@ class PaymentDetailPage extends ConsumerWidget { /// Build payment history section Widget _buildPaymentHistory( + BuildContext context, double amountPaid, DateTime paymentDate, ) { + final colorScheme = Theme.of(context).colorScheme; final hasHistory = amountPaid > 0; return Card( @@ -558,16 +576,16 @@ class PaymentDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ - FaIcon(FontAwesomeIcons.clockRotateLeft, color: AppColors.primaryBlue, size: 18), - SizedBox(width: 8), + FaIcon(FontAwesomeIcons.clockRotateLeft, color: colorScheme.primary, size: 18), + const SizedBox(width: 8), Text( 'Lịch sử thanh toán', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -578,7 +596,7 @@ class PaymentDetailPage extends ConsumerWidget { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -603,28 +621,28 @@ class PaymentDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Thanh toán lần 1', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), - const Text( + Text( 'Chuyển khoản | Ref: TK20241020001', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), Text( '${_formatDate(paymentDate)} - 14:30', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -650,23 +668,23 @@ class PaymentDetailPage extends ConsumerWidget { FaIcon( FontAwesomeIcons.receipt, size: 48, - color: AppColors.grey100, + color: colorScheme.outlineVariant, ), const SizedBox(height: 12), - const Text( + Text( 'Chưa có lịch sử thanh toán', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 4), - const Text( + Text( 'Hóa đơn này chưa được thanh toán', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -680,7 +698,9 @@ class PaymentDetailPage extends ConsumerWidget { } /// Build download section - Widget _buildDownloadSection(String invoiceNumber) { + Widget _buildDownloadSection(BuildContext context, String invoiceNumber) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -689,22 +709,23 @@ class PaymentDetailPage extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ - FaIcon(FontAwesomeIcons.download, color: AppColors.primaryBlue, size: 18), - SizedBox(width: 8), + FaIcon(FontAwesomeIcons.download, color: colorScheme.primary, size: 18), + const SizedBox(width: 8), Text( 'Tải chứng từ', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], ), const SizedBox(height: 16), _buildDownloadButton( + context, icon: FontAwesomeIcons.filePdf, label: 'Hóa đơn PDF', onTap: () { @@ -713,6 +734,7 @@ class PaymentDetailPage extends ConsumerWidget { ), const SizedBox(height: 12), _buildDownloadButton( + context, icon: FontAwesomeIcons.receipt, label: 'Phiếu thu PDF', onTap: () { @@ -726,33 +748,36 @@ class PaymentDetailPage extends ConsumerWidget { } /// Build download button - Widget _buildDownloadButton({ + Widget _buildDownloadButton( + BuildContext context, { required IconData icon, required String label, required VoidCallback onTap, }) { + final colorScheme = Theme.of(context).colorScheme; + return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(8), child: Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), decoration: BoxDecoration( - color: AppColors.grey50, - border: Border.all(color: AppColors.grey100), + color: colorScheme.surfaceContainerLowest, + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - FaIcon(icon, size: 18, color: AppColors.grey500), + FaIcon(icon, size: 18, color: colorScheme.onSurfaceVariant), const SizedBox(width: 8), Flexible( child: Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -788,11 +813,14 @@ class PaymentDetailPage extends ConsumerWidget { /// Build summary row Widget _buildSummaryRow( + BuildContext context, String label, String value, { bool isHighlighted = false, Color? valueColor, }) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -801,7 +829,7 @@ class PaymentDetailPage extends ConsumerWidget { style: TextStyle( fontSize: isHighlighted ? 18 : 16, fontWeight: isHighlighted ? FontWeight.w700 : FontWeight.w400, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), Text( @@ -809,7 +837,7 @@ class PaymentDetailPage extends ConsumerWidget { style: TextStyle( fontSize: isHighlighted ? 18 : 16, fontWeight: isHighlighted ? FontWeight.w700 : FontWeight.w600, - color: valueColor ?? AppColors.grey900, + color: valueColor ?? colorScheme.onSurface, ), ), ], @@ -822,7 +850,7 @@ class PaymentDetailPage extends ConsumerWidget { case InvoiceStatus.draft: return AppColors.grey500; case InvoiceStatus.issued: - return const Color(0xFFF59E0B); + return AppColors.warning; case InvoiceStatus.partiallyPaid: return AppColors.info; case InvoiceStatus.paid: @@ -832,7 +860,7 @@ class PaymentDetailPage extends ConsumerWidget { case InvoiceStatus.cancelled: return AppColors.grey500; case InvoiceStatus.refunded: - return const Color(0xFFF97316); + return AppColors.warning; } } diff --git a/lib/features/orders/presentation/pages/payment_qr_page.dart b/lib/features/orders/presentation/pages/payment_qr_page.dart index f20e45c..2840adf 100644 --- a/lib/features/orders/presentation/pages/payment_qr_page.dart +++ b/lib/features/orders/presentation/pages/payment_qr_page.dart @@ -38,6 +38,8 @@ class PaymentQrPage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; + // QR code data state final qrCodeData = useState?>(null); final isLoadingQr = useState(true); @@ -93,18 +95,18 @@ class PaymentQrPage extends HookConsumerWidget { '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}'; 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( 'Thanh toán', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.bold, ), @@ -112,7 +114,7 @@ class PaymentQrPage extends HookConsumerWidget { centerTitle: false, actions: [ IconButton( - icon: const FaIcon(FontAwesomeIcons.circleInfo, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.circleInfo, color: colorScheme.onSurface, size: 20), onPressed: () => _showInfoDialog(context), ), const SizedBox(width: AppSpacing.sm), @@ -125,6 +127,7 @@ class PaymentQrPage extends HookConsumerWidget { // QR Code Card _buildQrCodeCard( + context, orderId, qrCodeData.value?['qr_code'] as String?, isLoadingQr.value, @@ -169,7 +172,7 @@ class PaymentQrPage extends HookConsumerWidget { const SizedBox(height: AppSpacing.md), // Timer - _buildTimer(timerDisplay, remainingSeconds.value), + _buildTimer(context, timerDisplay, remainingSeconds.value), const SizedBox(height: AppSpacing.lg), ], @@ -180,19 +183,22 @@ class PaymentQrPage extends HookConsumerWidget { /// Build QR code card Widget _buildQrCodeCard( + BuildContext context, String orderId, String? qrCodeData, bool isLoading, ) { + final colorScheme = Theme.of(context).colorScheme; + return Container( 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.onSurface.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -200,12 +206,12 @@ class PaymentQrPage extends HookConsumerWidget { ), child: Column( children: [ - const Text( + Text( 'Quét mã QR để thanh toán', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.md), @@ -214,9 +220,9 @@ class PaymentQrPage extends HookConsumerWidget { height: 220, padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), - border: Border.all(color: const Color(0xFFE2E8F0)), + border: Border.all(color: colorScheme.outlineVariant), ), child: isLoading ? const Center( @@ -227,26 +233,26 @@ class PaymentQrPage extends HookConsumerWidget { data: qrCodeData, version: QrVersions.auto, size: 200.0, - backgroundColor: Colors.white, + backgroundColor: colorScheme.surface, errorCorrectionLevel: QrErrorCorrectLevel.M, ) - : const Column( + : Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - FaIcon(FontAwesomeIcons.qrcode, size: 80, color: AppColors.grey500), - SizedBox(height: 8), + FaIcon(FontAwesomeIcons.qrcode, size: 80, color: colorScheme.onSurfaceVariant), + const SizedBox(height: 8), Text( 'Không thể tải mã QR', - style: TextStyle(fontSize: 12, color: AppColors.grey500), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), ), ], ), ), const SizedBox(height: AppSpacing.md), - const Text( + Text( 'Quét mã QR bằng ứng dụng ngân hàng để thanh toán nhanh chóng', textAlign: TextAlign.center, - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ], ), @@ -262,15 +268,17 @@ class PaymentQrPage extends HookConsumerWidget { String? accountName, bool isLoading, ) { + final colorScheme = Theme.of(context).colorScheme; + return Container( 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.onSurface.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -279,12 +287,12 @@ class PaymentQrPage extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Thông tin chuyển khoản', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.md), @@ -329,25 +337,25 @@ class PaymentQrPage extends HookConsumerWidget { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFFE3F2FD), + color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(AppRadius.card), - border: Border.all(color: const Color(0xFF90CAF9)), + border: Border.all(color: colorScheme.primary.withValues(alpha: 0.3)), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.lightbulb, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 18, ), const SizedBox(width: 8), Expanded( child: RichText( text: TextSpan( - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: Color(0xFF1565C0), + color: colorScheme.primary, height: 1.4, ), children: [ @@ -377,28 +385,30 @@ class PaymentQrPage extends HookConsumerWidget { required String label, required String value, }) { + final colorScheme = Theme.of(context).colorScheme; + return Row( children: [ Expanded( flex: 2, child: Text( label, - style: const TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), ), Expanded( flex: 3, child: Text( value, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), ), IconButton( - icon: const FaIcon(FontAwesomeIcons.copy, size: 18, color: AppColors.primaryBlue), + icon: FaIcon(FontAwesomeIcons.copy, size: 18, color: colorScheme.primary), onPressed: () => _copyToClipboard(context, value), padding: EdgeInsets.zero, constraints: const BoxConstraints(), @@ -413,15 +423,17 @@ class PaymentQrPage extends HookConsumerWidget { String? imagePath, VoidCallback onSelectImage, ) { + final colorScheme = Theme.of(context).colorScheme; + return Container( 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.onSurface.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, 2), ), @@ -430,12 +442,12 @@ class PaymentQrPage extends HookConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Ảnh hóa đơn', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.md), @@ -448,10 +460,10 @@ class PaymentQrPage extends HookConsumerWidget { width: double.infinity, height: 200, decoration: BoxDecoration( - color: const Color(0xFFF4F6F8), + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(AppRadius.card), border: Border.all( - color: const Color(0xFFE2E8F0), + color: colorScheme.outlineVariant, width: 2, style: BorderStyle.solid, ), @@ -472,7 +484,7 @@ class PaymentQrPage extends HookConsumerWidget { right: 8, child: Container( decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.6), + color: colorScheme.onSurface.withValues(alpha: 0.6), borderRadius: BorderRadius.circular(20), ), padding: const EdgeInsets.symmetric( @@ -481,17 +493,17 @@ class PaymentQrPage extends HookConsumerWidget { ), child: Row( mainAxisSize: MainAxisSize.min, - children: const [ + children: [ FaIcon( FontAwesomeIcons.pen, - color: Colors.white, + color: colorScheme.surface, size: 12, ), - SizedBox(width: 6), + const SizedBox(width: 6), Text( 'Đổi ảnh', style: TextStyle( - color: Colors.white, + color: colorScheme.surface, fontSize: 12, fontWeight: FontWeight.w500, ), @@ -510,30 +522,30 @@ class PaymentQrPage extends HookConsumerWidget { width: 60, height: 60, decoration: BoxDecoration( - color: AppColors.primaryBlue.withValues(alpha: 0.1), + color: colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(30), ), - child: const Icon( + child: Icon( FontAwesomeIcons.image, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 24, ), ), const SizedBox(height: 12), - const Text( + Text( 'Chạm để chọn ảnh hóa đơn', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 4), - const Text( + Text( 'Hỗ trợ: JPG, PNG', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -553,6 +565,8 @@ class PaymentQrPage extends HookConsumerWidget { bool hasImage, VoidCallback onUpload, ) { + final colorScheme = Theme.of(context).colorScheme; + return Padding( padding: const EdgeInsets.symmetric(horizontal: AppSpacing.md), child: Column( @@ -563,12 +577,12 @@ class PaymentQrPage extends HookConsumerWidget { child: ElevatedButton.icon( onPressed: (isUploading || !hasImage) ? null : onUpload, icon: isUploading - ? const SizedBox( + ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(Colors.white), + valueColor: AlwaysStoppedAnimation(colorScheme.surface), ), ) : const FaIcon(FontAwesomeIcons.camera, size: 18), @@ -577,15 +591,15 @@ class PaymentQrPage extends HookConsumerWidget { style: const TextStyle(fontSize: 15, 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( borderRadius: BorderRadius.circular(AppRadius.button), ), - disabledBackgroundColor: AppColors.grey100, - disabledForegroundColor: AppColors.grey500, + disabledBackgroundColor: colorScheme.surfaceContainerLowest, + disabledForegroundColor: colorScheme.onSurfaceVariant, ), ), ), @@ -602,9 +616,9 @@ class PaymentQrPage extends HookConsumerWidget { style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600), ), style: OutlinedButton.styleFrom( - foregroundColor: AppColors.grey900, - side: const BorderSide( - color: AppColors.grey100, + foregroundColor: colorScheme.onSurface, + side: BorderSide( + color: colorScheme.outlineVariant, width: 1.5, ), padding: const EdgeInsets.symmetric(vertical: 14), @@ -620,22 +634,24 @@ class PaymentQrPage extends HookConsumerWidget { } /// Build countdown timer - Widget _buildTimer(String timerDisplay, int remainingSeconds) { + Widget _buildTimer(BuildContext context, String timerDisplay, int remainingSeconds) { + final colorScheme = Theme.of(context).colorScheme; + return Container( margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md), padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(AppRadius.card), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const FaIcon(FontAwesomeIcons.clock, size: 16, color: AppColors.grey500), + FaIcon(FontAwesomeIcons.clock, size: 16, color: colorScheme.onSurfaceVariant), const SizedBox(width: 8), - const Text( + Text( 'Thời gian thanh toán: ', - style: TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), Text( timerDisplay, @@ -644,7 +660,7 @@ class PaymentQrPage extends HookConsumerWidget { fontWeight: FontWeight.bold, color: remainingSeconds < 300 ? AppColors.danger - : AppColors.grey900, + : colorScheme.onSurface, ), ), ], @@ -715,6 +731,8 @@ class PaymentQrPage extends HookConsumerWidget { BuildContext context, ValueNotifier selectedImagePath, ) async { + final colorScheme = Theme.of(context).colorScheme; + // Show bottom sheet to select camera or gallery final ImageSource? source = await showModalBottomSheet( context: context, @@ -726,27 +744,27 @@ class PaymentQrPage extends HookConsumerWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const Text( + Text( 'Chọn ảnh hóa đơn', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, - color: Color(0xFF212121), + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.md), ListTile( - leading: const FaIcon( + leading: FaIcon( FontAwesomeIcons.camera, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), title: const Text('Chụp ảnh'), onTap: () => Navigator.of(context).pop(ImageSource.camera), ), ListTile( - leading: const FaIcon( + leading: FaIcon( FontAwesomeIcons.image, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), title: const Text('Chọn từ thư viện'), onTap: () => Navigator.of(context).pop(ImageSource.gallery), @@ -811,6 +829,8 @@ class PaymentQrPage extends HookConsumerWidget { ValueNotifier selectedImagePath, ValueNotifier isUploadingBill, ) async { + final colorScheme = Theme.of(context).colorScheme; + if (selectedImagePath.value == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -839,7 +859,7 @@ class PaymentQrPage extends HookConsumerWidget { ElevatedButton( onPressed: () => Navigator.of(context).pop(true), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, + backgroundColor: colorScheme.primary, ), child: const Text('Upload'), ), diff --git a/lib/features/orders/presentation/pages/payments_page.dart b/lib/features/orders/presentation/pages/payments_page.dart index c086043..6d20402 100644 --- a/lib/features/orders/presentation/pages/payments_page.dart +++ b/lib/features/orders/presentation/pages/payments_page.dart @@ -26,7 +26,7 @@ class PaymentsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final paymentsAsync = ref.watch(paymentsProvider); - final colorScheme = context.colorScheme; + final colorScheme = Theme.of(context).colorScheme; return Scaffold( backgroundColor: colorScheme.surfaceContainerLowest, @@ -79,7 +79,7 @@ class PaymentsPage extends ConsumerWidget { /// Build error state Widget _buildErrorState(BuildContext context, WidgetRef ref, Object error) { - final colorScheme = context.colorScheme; + final colorScheme = Theme.of(context).colorScheme; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -116,7 +116,7 @@ class PaymentsPage extends ConsumerWidget { /// Build empty state Widget _buildEmptyState(BuildContext context, WidgetRef ref) { - final colorScheme = context.colorScheme; + final colorScheme = Theme.of(context).colorScheme; return RefreshIndicator( onRefresh: () async { await ref.read(paymentsProvider.notifier).refresh(); @@ -164,7 +164,7 @@ class PaymentsPage extends ConsumerWidget { /// Show transaction detail modal void _showTransactionDetail(BuildContext context, Payment payment) { - final colorScheme = context.colorScheme; + final colorScheme = Theme.of(context).colorScheme; final currencyFormatter = NumberFormat.currency( locale: 'vi_VN', symbol: 'đ', @@ -302,8 +302,8 @@ class _TransactionCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final dateFormatter = DateFormat('dd/MM/yyyy'); - final colorScheme = context.colorScheme; return Card( margin: const EdgeInsets.only(bottom: 12), @@ -416,7 +416,7 @@ class _DetailRow extends StatelessWidget { @override Widget build(BuildContext context) { - final colorScheme = context.colorScheme; + final colorScheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( diff --git a/lib/features/orders/presentation/widgets/invoice_card.dart b/lib/features/orders/presentation/widgets/invoice_card.dart index 680248c..d7fc873 100644 --- a/lib/features/orders/presentation/widgets/invoice_card.dart +++ b/lib/features/orders/presentation/widgets/invoice_card.dart @@ -32,6 +32,8 @@ class InvoiceCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + final currencyFormatter = NumberFormat.currency( locale: 'vi_VN', symbol: 'đ', @@ -61,10 +63,10 @@ class InvoiceCard extends StatelessWidget { // Invoice number Text( 'Mã hoá đơn #${invoice.invoiceNumber}', - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), @@ -72,9 +74,9 @@ class InvoiceCard extends StatelessWidget { if (invoice.orderId != null) Text( 'Đơn hàng: #${invoice.orderId}', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -88,10 +90,18 @@ class InvoiceCard extends StatelessWidget { const SizedBox(height: 12), // Invoice dates - _buildDetailRow('Ngày hóa đơn:', _formatDate(invoice.issueDate)), + _buildDetailRow( + context, + 'Ngày hóa đơn:', + _formatDate(invoice.issueDate), + ), const SizedBox(height: 6), - _buildDetailRow('Hạn thanh toán:', _formatDate(invoice.dueDate)), + _buildDetailRow( + context, + 'Hạn thanh toán:', + _formatDate(invoice.dueDate), + ), const SizedBox(height: 12), @@ -99,13 +109,14 @@ class InvoiceCard extends StatelessWidget { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: Column( spacing: 2, children: [ _buildPaymentRow( + context, 'Tổng tiền:', currencyFormatter.format(invoice.totalAmount), fontWeight: FontWeight.w600, @@ -113,6 +124,7 @@ class InvoiceCard extends StatelessWidget { if (invoice.amountPaid > 0) ...[ const SizedBox(height: 6), _buildPaymentRow( + context, 'Đã thanh toán:', currencyFormatter.format(invoice.amountPaid), valueColor: AppColors.success, @@ -122,6 +134,7 @@ class InvoiceCard extends StatelessWidget { if (invoice.amountRemaining > 0) ...[ const SizedBox(height: 6), _buildPaymentRow( + context, 'Còn lại:', currencyFormatter.format(invoice.amountRemaining), valueColor: invoice.isOverdue @@ -137,7 +150,7 @@ class InvoiceCard extends StatelessWidget { const SizedBox(height: 12), // Action button - _buildActionButton(), + _buildActionButton(context), ], ), ), @@ -146,7 +159,9 @@ class InvoiceCard extends StatelessWidget { } /// Build detail row - Widget _buildDetailRow(String label, String value) { + Widget _buildDetailRow(BuildContext context, String label, String value) { + final colorScheme = Theme.of(context).colorScheme; + return Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -154,14 +169,14 @@ class InvoiceCard extends StatelessWidget { children: [ Text( label, - style: const TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), Text( value, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, fontWeight: FontWeight.bold, ), ), @@ -171,24 +186,27 @@ class InvoiceCard extends StatelessWidget { /// Build payment summary row Widget _buildPaymentRow( + BuildContext context, String label, String value, { Color? valueColor, FontWeight? fontWeight, }) { + final colorScheme = Theme.of(context).colorScheme; + return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, - style: const TextStyle(fontSize: 13, color: AppColors.grey500), + style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant), ), Text( value, style: TextStyle( fontSize: 14, fontWeight: fontWeight ?? FontWeight.w400, - color: valueColor ?? AppColors.grey900, + color: valueColor ?? colorScheme.onSurface, ), ), ], @@ -219,10 +237,11 @@ class InvoiceCard extends StatelessWidget { } /// Build action button - Widget _buildActionButton() { + Widget _buildActionButton(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final isPaid = invoice.status == InvoiceStatus.paid || invoice.isPaid; final buttonText = isPaid ? 'Đã hoàn tất' : 'Thanh toán'; - final buttonColor = isPaid ? AppColors.success : AppColors.primaryBlue; + final buttonColor = isPaid ? AppColors.success : colorScheme.primary; return SizedBox( width: double.infinity, @@ -230,9 +249,9 @@ class InvoiceCard extends StatelessWidget { onPressed: isPaid ? null : onPaymentTap, style: ElevatedButton.styleFrom( backgroundColor: buttonColor, - disabledBackgroundColor: AppColors.grey100, - foregroundColor: Colors.white, - disabledForegroundColor: AppColors.grey500, + disabledBackgroundColor: colorScheme.surfaceContainerHighest, + foregroundColor: colorScheme.surface, + disabledForegroundColor: colorScheme.onSurfaceVariant, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), elevation: 0, diff --git a/lib/features/orders/presentation/widgets/order_card.dart b/lib/features/orders/presentation/widgets/order_card.dart index f9794c8..9a4a6a8 100644 --- a/lib/features/orders/presentation/widgets/order_card.dart +++ b/lib/features/orders/presentation/widgets/order_card.dart @@ -6,7 +6,6 @@ library; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:worker/core/enums/status_color.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/orders/domain/entities/order.dart'; /// Order Card Widget @@ -23,6 +22,7 @@ class OrderCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final currencyFormatter = NumberFormat.currency( locale: 'vi_VN', symbol: 'đ', @@ -49,20 +49,20 @@ class OrderCard extends StatelessWidget { // Order number Text( '#${order.name}', - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), // Amount Text( currencyFormatter.format(order.grandTotal), - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], @@ -71,13 +71,21 @@ class OrderCard extends StatelessWidget { const SizedBox(height: 12), // Order details - _buildDetailRow('Ngày đặt:', _formatDate(order.transactionDate)), + _buildDetailRow( + context, + 'Ngày đặt:', + _formatDate(order.transactionDate), + ), const SizedBox(height: 6), - _buildDetailRow('Ngày giao:', _formatDate(order.deliveryDate)), + _buildDetailRow( + context, + 'Ngày giao:', + _formatDate(order.deliveryDate), + ), const SizedBox(height: 6), - _buildDetailRow('Địa chỉ:', order.address), + _buildDetailRow(context, 'Địa chỉ:', order.address), const SizedBox(height: 12), // Status badge @@ -90,19 +98,27 @@ class OrderCard extends StatelessWidget { } /// Build detail row - Widget _buildDetailRow(String label, String value) { + Widget _buildDetailRow(BuildContext context, String label, String value) { + final colorScheme = Theme.of(context).colorScheme; + return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, - style: const TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle( + fontSize: 14, + color: colorScheme.onSurfaceVariant, + ), ), const SizedBox(width: 8), Expanded( child: Text( value, - style: const TextStyle(fontSize: 14, color: AppColors.grey900), + style: TextStyle( + fontSize: 14, + color: colorScheme.onSurface, + ), ), ), ], diff --git a/lib/features/price_policy/presentation/pages/price_policy_page.dart b/lib/features/price_policy/presentation/pages/price_policy_page.dart index 8517afb..f1a832e 100644 --- a/lib/features/price_policy/presentation/pages/price_policy_page.dart +++ b/lib/features/price_policy/presentation/pages/price_policy_page.dart @@ -38,24 +38,26 @@ class _PricePolicyPageState extends ConsumerState @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( - backgroundColor: AppColors.grey50, + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), + icon: Icon(Icons.arrow_back, color: colorScheme.onSurface), onPressed: () => context.pop(), ), - title: const Text( + title: Text( 'Chính sách giá', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ IconButton( - icon: const Icon(Icons.info_outline, color: Colors.black), + icon: Icon(Icons.info_outline, color: colorScheme.onSurface), onPressed: _showInfoDialog, ), const SizedBox(width: AppSpacing.sm), @@ -69,16 +71,16 @@ class _PricePolicyPageState extends ConsumerState child: Container( height: 40, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: TabBar( controller: _tabController, - labelColor: AppColors.white, - unselectedLabelColor: AppColors.grey900, + labelColor: colorScheme.surface, + unselectedLabelColor: colorScheme.onSurface, indicatorSize: TabBarIndicatorSize.tab, indicator: BoxDecoration( - color: AppColors.primaryBlue, + color: colorScheme.primary, borderRadius: BorderRadius.circular(8), ), dividerColor: Colors.transparent, @@ -115,24 +117,25 @@ class _PricePolicyPageState extends ConsumerState } Widget _buildDocumentList(DocumentCategory category) { + final colorScheme = Theme.of(context).colorScheme; final documentsAsync = ref.watch(filteredPriceDocumentsProvider(category)); return documentsAsync.when( data: (documents) { if (documents.isEmpty) { - return const Center( + return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.description_outlined, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: AppSpacing.md), + const SizedBox(height: AppSpacing.md), Text( 'Chưa có tài liệu', - style: TextStyle(fontSize: 16, color: AppColors.grey500), + style: TextStyle(fontSize: 16, color: colorScheme.onSurfaceVariant), ), ], ), @@ -166,11 +169,11 @@ class _PricePolicyPageState extends ConsumerState child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.error_outline, size: 64, color: AppColors.danger), + const Icon(Icons.error_outline, size: 64, color: AppColors.danger), const SizedBox(height: AppSpacing.md), Text( 'Không thể tải tài liệu', - style: TextStyle(fontSize: 16, color: AppColors.grey500), + style: TextStyle(fontSize: 16, color: colorScheme.onSurfaceVariant), ), const SizedBox(height: AppSpacing.sm), ElevatedButton( diff --git a/lib/features/price_policy/presentation/widgets/document_card.dart b/lib/features/price_policy/presentation/widgets/document_card.dart index 63b1507..f6a776c 100644 --- a/lib/features/price_policy/presentation/widgets/document_card.dart +++ b/lib/features/price_policy/presentation/widgets/document_card.dart @@ -16,11 +16,13 @@ class DocumentCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(12), - border: Border.all(color: AppColors.grey100), + border: Border.all(color: colorScheme.surfaceContainerHighest), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), @@ -47,15 +49,15 @@ class DocumentCard extends StatelessWidget { children: [ Row( children: [ - _buildIcon(), + _buildIcon(colorScheme), const SizedBox(width: AppSpacing.md), - Expanded(child: _buildInfo()), + Expanded(child: _buildInfo(colorScheme)), ], ), const SizedBox(height: AppSpacing.md), SizedBox( width: double.infinity, - child: _buildDownloadButton(), + child: _buildDownloadButton(colorScheme), ), ], ); @@ -63,11 +65,11 @@ class DocumentCard extends StatelessWidget { return Row( children: [ - _buildIcon(), + _buildIcon(colorScheme), const SizedBox(width: AppSpacing.md), - Expanded(child: _buildInfo()), + Expanded(child: _buildInfo(colorScheme)), const SizedBox(width: AppSpacing.md), - _buildDownloadButton(), + _buildDownloadButton(colorScheme), ], ); }, @@ -78,7 +80,7 @@ class DocumentCard extends StatelessWidget { ); } - Widget _buildIcon() { + Widget _buildIcon(ColorScheme colorScheme) { final iconData = document.isPdf ? Icons.picture_as_pdf : Icons.table_chart; final iconColor = document.isPdf ? Colors.red.shade600 @@ -88,46 +90,46 @@ class DocumentCard extends StatelessWidget { width: 50, height: 50, decoration: BoxDecoration( - color: AppColors.grey50, + color: colorScheme.surfaceContainerLowest, borderRadius: BorderRadius.circular(8), ), child: Icon(iconData, size: 28, color: iconColor), ); } - Widget _buildInfo() { + Widget _buildInfo(ColorScheme colorScheme) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( document.title, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), Row( children: [ - const Icon( + Icon( Icons.calendar_today, size: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), const SizedBox(width: 4), Text( document.formattedDateWithPrefix, - style: const TextStyle(fontSize: 13, color: AppColors.grey500), + style: TextStyle(fontSize: 13, color: colorScheme.onSurfaceVariant), ), ], ), const SizedBox(height: 6), Text( document.title, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, height: 1.4, ), maxLines: 2, @@ -137,9 +139,9 @@ class DocumentCard extends StatelessWidget { ); } - Widget _buildDownloadButton() { + Widget _buildDownloadButton(ColorScheme colorScheme) { final isDownloaded = document.filePath != null; - final buttonColor = isDownloaded ? AppColors.success : AppColors.primaryBlue; + final buttonColor = isDownloaded ? AppColors.success : colorScheme.primary; final buttonIcon = isDownloaded ? Icons.folder_open : Icons.download; final buttonText = isDownloaded ? 'Mở file' : 'Tải về'; diff --git a/lib/features/products/presentation/pages/product_detail_page.dart b/lib/features/products/presentation/pages/product_detail_page.dart index dad7598..679690d 100644 --- a/lib/features/products/presentation/pages/product_detail_page.dart +++ b/lib/features/products/presentation/pages/product_detail_page.dart @@ -79,14 +79,16 @@ class _ProductDetailPageState extends ConsumerState { } void _shareProduct(Product product) { + final colorScheme = Theme.of(context).colorScheme; + // Show share options showModalBottomSheet( context: context, backgroundColor: Colors.transparent, builder: (context) => Container( - decoration: const BoxDecoration( - color: AppColors.white, - borderRadius: BorderRadius.vertical( + decoration: BoxDecoration( + color: colorScheme.surface, + borderRadius: const BorderRadius.vertical( top: Radius.circular(AppRadius.xl), ), ), @@ -99,7 +101,7 @@ class _ProductDetailPageState extends ConsumerState { width: 40, height: 4, decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.outlineVariant, borderRadius: BorderRadius.circular(2), ), ), @@ -114,7 +116,7 @@ class _ProductDetailPageState extends ConsumerState { // Share options ListTile( - leading: const FaIcon(FontAwesomeIcons.message, color: AppColors.primaryBlue, size: 20), + leading: FaIcon(FontAwesomeIcons.message, color: colorScheme.primary, size: 20), title: const Text('Chia sẻ qua tin nhắn'), onTap: () { Navigator.pop(context); @@ -127,7 +129,7 @@ class _ProductDetailPageState extends ConsumerState { }, ), ListTile( - leading: const FaIcon(FontAwesomeIcons.shareNodes, color: AppColors.primaryBlue, size: 20), + leading: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.primary, size: 20), title: const Text('Chia sẻ khác'), onTap: () { Navigator.pop(context); @@ -135,7 +137,7 @@ class _ProductDetailPageState extends ConsumerState { }, ), ListTile( - leading: const FaIcon(FontAwesomeIcons.copy, color: AppColors.primaryBlue, size: 20), + leading: FaIcon(FontAwesomeIcons.copy, color: colorScheme.primary, size: 20), title: const Text('Sao chép link'), onTap: () { Navigator.pop(context); @@ -171,6 +173,8 @@ class _ProductDetailPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + // Use productDetailProvider with productId parameter final productAsync = ref.watch(productDetailProvider(productId: widget.productId)); @@ -178,24 +182,24 @@ class _ProductDetailPageState extends ConsumerState { final isFavorite = ref.watch(isFavoriteProvider(widget.productId)); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( 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( 'Chi tiết sản phẩm', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ // Share button IconButton( - icon: const FaIcon(FontAwesomeIcons.shareNodes, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.shareNodes, color: colorScheme.onSurface, size: 20), onPressed: () { productAsync.whenData((product) { _shareProduct(product); @@ -206,7 +210,7 @@ class _ProductDetailPageState extends ConsumerState { IconButton( icon: Icon( isFavorite ? FontAwesomeIcons.solidHeart : FontAwesomeIcons.heart, - color: isFavorite ? AppColors.danger : Colors.black, + color: isFavorite ? AppColors.danger : colorScheme.onSurface, ), onPressed: _toggleFavorite, ), @@ -258,8 +262,8 @@ class _ProductDetailPageState extends ConsumerState { ], ); }, - loading: () => const Center( - child: CircularProgressIndicator(color: AppColors.primaryBlue), + loading: () => Center( + child: CircularProgressIndicator(color: colorScheme.primary), ), error: (error, stack) => Center( child: Padding( @@ -281,9 +285,9 @@ class _ProductDetailPageState extends ConsumerState { const SizedBox(height: AppSpacing.sm), Text( error.toString(), - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -296,8 +300,8 @@ class _ProductDetailPageState extends ConsumerState { icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 18), label: const Text('Thử lại'), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, ), ), ], diff --git a/lib/features/products/presentation/pages/products_page.dart b/lib/features/products/presentation/pages/products_page.dart index ab5251d..5afc747 100644 --- a/lib/features/products/presentation/pages/products_page.dart +++ b/lib/features/products/presentation/pages/products_page.dart @@ -32,6 +32,7 @@ class ProductsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final l10n = AppLocalizations.of(context); final productsAsync = ref.watch(productsProvider); @@ -39,17 +40,17 @@ class ProductsPage extends ConsumerWidget { ref.watch(productFilterOptionsProvider); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), // Match HTML background + backgroundColor: colorScheme.surfaceContainerLowest, endDrawer: const ProductFilterDrawer(), appBar: AppBar( 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('Sản phẩm', style: TextStyle(color: Colors.black)), + title: Text('Sản phẩm', style: TextStyle(color: colorScheme.onSurface)), elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, - foregroundColor: AppColors.grey900, + backgroundColor: colorScheme.surface, + foregroundColor: colorScheme.onSurface, centerTitle: false, actions: [ // Cart Icon with Badge (extracted to Consumer to prevent full page rebuild) @@ -60,9 +61,9 @@ class ProductsPage extends ConsumerWidget { icon: Badge( label: Text('$cartItemCount'), backgroundColor: AppColors.danger, - textColor: AppColors.white, + textColor: colorScheme.surface, isLabelVisible: cartItemCount > 0, - child: const FaIcon(FontAwesomeIcons.cartShopping, color: Colors.black, size: 20), + child: FaIcon(FontAwesomeIcons.cartShopping, color: colorScheme.onSurface, size: 20), ), onPressed: () => context.push(RouteNames.cart), ); @@ -88,7 +89,7 @@ class ProductsPage extends ConsumerWidget { height: InputFieldSpecs.height, width: InputFieldSpecs.height, decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), ), child: IconButton( @@ -97,7 +98,7 @@ class ProductsPage extends ConsumerWidget { Scaffold.of(scaffoldContext).openEndDrawer(); }, icon: const FaIcon(FontAwesomeIcons.sliders, size: 18), - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -112,7 +113,7 @@ class ProductsPage extends ConsumerWidget { child: productsAsync.when( data: (products) { if (products.isEmpty) { - return _buildEmptyState(context, l10n); + return _buildEmptyState(context, l10n, colorScheme); } final productsNotifier = ref.read(productsProvider.notifier); @@ -159,8 +160,8 @@ class ProductsPage extends ConsumerWidget { }, ); }, - loading: () => _buildLoadingState(), - error: (error, stack) => _buildErrorState(context, l10n, error, ref), + loading: () => _buildLoadingState(colorScheme), + error: (error, stack) => _buildErrorState(context, l10n, error, ref, colorScheme), ), ), ], @@ -171,31 +172,31 @@ class ProductsPage extends ConsumerWidget { } /// Build empty state - Widget _buildEmptyState(BuildContext context, AppLocalizations l10n) { + Widget _buildEmptyState(BuildContext context, AppLocalizations l10n, ColorScheme colorScheme) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - FaIcon(FontAwesomeIcons.boxOpen, size: 80.0, color: AppColors.grey500.withAlpha(128)), + FaIcon(FontAwesomeIcons.boxOpen, size: 80.0, color: colorScheme.onSurfaceVariant.withAlpha(128)), const SizedBox(height: AppSpacing.lg), Text( l10n.noProductsFound, - style: const TextStyle(fontSize: 18.0, fontWeight: FontWeight.w500, color: AppColors.grey900), + style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w500, color: colorScheme.onSurface), ), const SizedBox(height: AppSpacing.sm), - Text(l10n.noResults, style: const TextStyle(fontSize: 14.0, color: AppColors.grey500)), + Text(l10n.noResults, style: TextStyle(fontSize: 14.0, color: colorScheme.onSurfaceVariant)), ], ), ); } /// Build loading state - Widget _buildLoadingState() { - return const Center(child: CircularProgressIndicator(color: AppColors.primaryBlue)); + Widget _buildLoadingState(ColorScheme colorScheme) { + return Center(child: CircularProgressIndicator(color: colorScheme.primary)); } /// Build error state - Widget _buildErrorState(BuildContext context, AppLocalizations l10n, Object error, WidgetRef ref) { + Widget _buildErrorState(BuildContext context, AppLocalizations l10n, Object error, WidgetRef ref, ColorScheme colorScheme) { return Center( child: Padding( padding: const EdgeInsets.all(AppSpacing.xl), @@ -206,12 +207,12 @@ class ProductsPage extends ConsumerWidget { const SizedBox(height: AppSpacing.lg), Text( l10n.error, - style: const TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600, color: AppColors.grey900), + style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600, color: colorScheme.onSurface), ), const SizedBox(height: AppSpacing.sm), Text( error.toString(), - style: const TextStyle(fontSize: 14.0, color: AppColors.grey500), + style: TextStyle(fontSize: 14.0, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: AppSpacing.lg), @@ -222,8 +223,8 @@ class ProductsPage extends ConsumerWidget { icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 18), label: Text(l10n.tryAgain), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, padding: const EdgeInsets.symmetric(horizontal: AppSpacing.lg, vertical: AppSpacing.md), ), ), diff --git a/lib/features/products/presentation/pages/write_review_page.dart b/lib/features/products/presentation/pages/write_review_page.dart index f178f43..52e710c 100644 --- a/lib/features/products/presentation/pages/write_review_page.dart +++ b/lib/features/products/presentation/pages/write_review_page.dart @@ -102,14 +102,16 @@ class _WriteReviewPageState extends ConsumerState { ); if (mounted) { + final colorScheme = Theme.of(context).colorScheme; + // Show success message ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( + SnackBar( content: Row( children: [ - Icon(FontAwesomeIcons.circleCheck, color: AppColors.white), - SizedBox(width: 12), - Expanded( + Icon(FontAwesomeIcons.circleCheck, color: colorScheme.onPrimary), + const SizedBox(width: 12), + const Expanded( child: Text('Đánh giá của bạn đã được gửi thành công!'), ), ], @@ -128,15 +130,16 @@ class _WriteReviewPageState extends ConsumerState { } catch (e) { if (mounted) { setState(() => _isSubmitting = false); + final colorScheme = Theme.of(context).colorScheme; // Show error message ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Row( children: [ - const Icon( + Icon( FontAwesomeIcons.triangleExclamation, - color: AppColors.white, + color: colorScheme.onPrimary, ), const SizedBox(width: 12), Expanded( @@ -154,26 +157,27 @@ class _WriteReviewPageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final productAsync = ref.watch(productDetailProvider(productId: widget.productId)); return Scaffold( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, // Standard AppBar appBar: AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, centerTitle: false, leading: IconButton( - icon: const Icon( + icon: Icon( Icons.arrow_back, - color: AppColors.grey900, + color: colorScheme.onSurface, ), onPressed: () => context.pop(), ), - title: const Text( + title: Text( 'Viết đánh giá sản phẩm', style: TextStyle( - color: AppColors.grey900, + color: colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.w600, ), @@ -187,17 +191,17 @@ class _WriteReviewPageState extends ConsumerState { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( FontAwesomeIcons.circleExclamation, size: 48, - color: AppColors.danger, + color: colorScheme.error, ), const SizedBox(height: 16), Text( 'Không thể tải thông tin sản phẩm', - style: const TextStyle( + style: TextStyle( fontSize: 16, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 24), @@ -213,6 +217,8 @@ class _WriteReviewPageState extends ConsumerState { } Widget _buildForm(Product product) { + final colorScheme = Theme.of(context).colorScheme; + return SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(16), @@ -223,8 +229,8 @@ class _WriteReviewPageState extends ConsumerState { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: AppColors.white, - border: Border.all(color: const Color(0xFFe0e0e0), width: 2), + color: colorScheme.surface, + border: Border.all(color: colorScheme.outlineVariant, width: 2), borderRadius: BorderRadius.circular(12), ), child: Row( @@ -242,7 +248,7 @@ class _WriteReviewPageState extends ConsumerState { placeholder: (context, url) => Container( width: 80, height: 80, - color: AppColors.grey100, + color: colorScheme.surfaceContainerLowest, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), @@ -250,10 +256,10 @@ class _WriteReviewPageState extends ConsumerState { errorWidget: (context, url, error) => Container( width: 80, height: 80, - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerLowest, + child: Icon( FontAwesomeIcons.image, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -268,10 +274,10 @@ class _WriteReviewPageState extends ConsumerState { children: [ Text( product.name, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), maxLines: 2, overflow: TextOverflow.ellipsis, @@ -279,9 +285,9 @@ class _WriteReviewPageState extends ConsumerState { const SizedBox(height: 6), Text( 'Mã: ${product.productId}', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -298,27 +304,27 @@ class _WriteReviewPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( - text: const TextSpan( + text: TextSpan( text: 'Xếp hạng của bạn', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), children: [ TextSpan( text: ' *', - style: TextStyle(color: AppColors.danger), + style: TextStyle(color: colorScheme.error), ), ], ), ), const SizedBox(height: 8), - const Text( + Text( 'Bấm vào ngôi sao để chọn đánh giá', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), @@ -339,17 +345,17 @@ class _WriteReviewPageState extends ConsumerState { const SizedBox(height: 8), Row( children: [ - const Icon( + Icon( FontAwesomeIcons.triangleExclamation, size: 14, - color: AppColors.danger, + color: colorScheme.error, ), const SizedBox(width: 6), Text( _ratingError!, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.danger, + color: colorScheme.error, ), ), ], @@ -365,17 +371,17 @@ class _WriteReviewPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( - text: const TextSpan( + text: TextSpan( text: 'Nội dung đánh giá', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), children: [ TextSpan( text: ' *', - style: TextStyle(color: AppColors.danger), + style: TextStyle(color: colorScheme.error), ), ], ), @@ -390,38 +396,38 @@ class _WriteReviewPageState extends ConsumerState { decoration: InputDecoration( hintText: 'Chia sẻ trải nghiệm của bạn về sản phẩm này...', - hintStyle: const TextStyle( + hintStyle: TextStyle( fontSize: 15, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: Color(0xFFe0e0e0), + borderSide: BorderSide( + color: colorScheme.outlineVariant, width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: Color(0xFFe0e0e0), + borderSide: BorderSide( + color: colorScheme.outlineVariant, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), contentPadding: const EdgeInsets.all(14), counterText: '', // Hide default counter ), - style: const TextStyle( + style: TextStyle( fontSize: 15, height: 1.6, - color: AppColors.grey900, + color: colorScheme.onSurface, ), onChanged: (value) { setState(() { @@ -441,15 +447,15 @@ class _WriteReviewPageState extends ConsumerState { style: TextStyle( fontSize: 13, color: _contentController.text.length < _minContentLength - ? AppColors.danger - : AppColors.grey500, + ? colorScheme.error + : colorScheme.onSurfaceVariant, ), ), Text( ' / $_maxContentLength ký tự', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -460,18 +466,18 @@ class _WriteReviewPageState extends ConsumerState { const SizedBox(height: 6), Row( children: [ - const Icon( + Icon( FontAwesomeIcons.triangleExclamation, size: 14, - color: AppColors.danger, + color: colorScheme.error, ), const SizedBox(width: 6), Expanded( child: Text( _contentError!, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.danger, + color: colorScheme.error, ), ), ), @@ -494,8 +500,9 @@ class _WriteReviewPageState extends ConsumerState { child: ElevatedButton( onPressed: _isSubmitting ? null : _submitReview, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - disabledBackgroundColor: const Color(0xFFe0e0e0), + backgroundColor: colorScheme.primary, + disabledBackgroundColor: colorScheme.outlineVariant, + foregroundColor: colorScheme.onPrimary, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -503,30 +510,30 @@ class _WriteReviewPageState extends ConsumerState { elevation: 0, ), child: _isSubmitting - ? const SizedBox( + ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: - AlwaysStoppedAnimation(AppColors.white), + AlwaysStoppedAnimation(colorScheme.onPrimary), ), ) : Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon( + Icon( FontAwesomeIcons.paperPlane, size: 16, - color: AppColors.white, + color: colorScheme.onPrimary, ), const SizedBox(width: 10), - const Text( + Text( 'Gửi đánh giá', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.white, + color: colorScheme.onPrimary, ), ), ], diff --git a/lib/features/products/presentation/widgets/brand_filter_chips.dart b/lib/features/products/presentation/widgets/brand_filter_chips.dart index 769e5a0..04ee78f 100644 --- a/lib/features/products/presentation/widgets/brand_filter_chips.dart +++ b/lib/features/products/presentation/widgets/brand_filter_chips.dart @@ -6,7 +6,6 @@ library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/products/presentation/providers/product_filter_options_provider.dart'; import 'package:worker/features/products/presentation/providers/product_filters_provider.dart'; @@ -20,6 +19,7 @@ class BrandFilterChips extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final filtersState = ref.watch(productFiltersProvider); final filterOptionsAsync = ref.watch(productFilterOptionsProvider); @@ -55,7 +55,7 @@ class BrandFilterChips extends ConsumerWidget { style: TextStyle( fontSize: 14.0, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - color: isSelected ? AppColors.white : AppColors.grey900, + color: colorScheme.onSurface, ), ), selected: isSelected, @@ -84,11 +84,11 @@ class BrandFilterChips extends ConsumerWidget { } } }, - backgroundColor: AppColors.white, - selectedColor: AppColors.primaryBlue, - checkmarkColor: AppColors.white, + backgroundColor: colorScheme.surface, + selectedColor: colorScheme.primary, + checkmarkColor: colorScheme.onPrimary, side: BorderSide( - color: isSelected ? AppColors.primaryBlue : AppColors.grey100, + color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest, width: isSelected ? 2.0 : 1.0, ), shape: RoundedRectangleBorder( diff --git a/lib/features/products/presentation/widgets/product_card.dart b/lib/features/products/presentation/widgets/product_card.dart index f82febf..6de577e 100644 --- a/lib/features/products/presentation/widgets/product_card.dart +++ b/lib/features/products/presentation/widgets/product_card.dart @@ -38,7 +38,7 @@ class ProductCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final l10n = AppLocalizations.of(context); - // final isFavorited = ref.watch(isFavoriteProvider(product.productId)); + final colorScheme = Theme.of(context).colorScheme; return Card( elevation: ProductCardSpecs.elevation, @@ -62,23 +62,25 @@ class ProductCard extends ConsumerWidget { top: Radius.circular(ProductCardSpecs.borderRadius), ), child: CachedNetworkImage( - imageUrl: product.thumbnail ?? '', + imageUrl: product.thumbnail, width: double.infinity, height: double.infinity, fit: BoxFit.cover, memCacheWidth: ImageSpecs.productImageCacheWidth, memCacheHeight: ImageSpecs.productImageCacheHeight, placeholder: (context, url) => Shimmer.fromColors( - baseColor: AppColors.grey100, - highlightColor: AppColors.grey50, - child: Container(color: AppColors.grey100), + baseColor: colorScheme.surfaceContainerHighest, + highlightColor: colorScheme.surfaceContainerLowest, + child: Container( + color: colorScheme.surfaceContainerHighest, + ), ), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerHighest, + child: Icon( FontAwesomeIcons.image, size: 48.0, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -170,7 +172,7 @@ class ProductCard extends ConsumerWidget { // width: 36, // height: 36, // decoration: BoxDecoration( - // color: AppColors.white, + // color: colorScheme.surface, // shape: BoxShape.circle, // boxShadow: [ // BoxShadow( @@ -186,7 +188,7 @@ class ProductCard extends ConsumerWidget { // : Icons.favorite_border, // color: isFavorited // ? AppColors.danger - // : AppColors.grey500, + // : colorScheme.onSurfaceVariant, // size: 20, // ), // ), @@ -221,10 +223,10 @@ class ProductCard extends ConsumerWidget { // Price Text( '${_formatPrice(product.effectivePrice)}/m²', - style: const TextStyle( + style: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), @@ -241,8 +243,8 @@ class ProductCard extends ConsumerWidget { child: ElevatedButton.icon( onPressed: onAddToCart, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( @@ -254,11 +256,12 @@ class ProductCard extends ConsumerWidget { ), ), icon: const FaIcon(FontAwesomeIcons.cartShopping, size: 14.0), - label: const Text( + label: Text( 'Thêm vào giỏ', style: TextStyle( fontSize: 12.0, fontWeight: FontWeight.w600, + color: colorScheme.surface ), ), ), @@ -283,9 +286,9 @@ class ProductCard extends ConsumerWidget { ); }, style: OutlinedButton.styleFrom( - foregroundColor: AppColors.primaryBlue, - side: const BorderSide( - color: AppColors.primaryBlue, + foregroundColor: colorScheme.primary, + side: BorderSide( + color: colorScheme.primary, width: 1.5, ), elevation: 0, @@ -299,11 +302,12 @@ class ProductCard extends ConsumerWidget { ), ), icon: const FaIcon(FontAwesomeIcons.cube, size: 14.0), - label: const Text( + label: Text( 'Phối cảnh 360°', style: TextStyle( fontSize: 12.0, fontWeight: FontWeight.w600, + color: colorScheme.onSurface ), ), ), diff --git a/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart b/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart index 38274a5..b62918a 100644 --- a/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart +++ b/lib/features/products/presentation/widgets/product_detail/image_gallery_section.dart @@ -8,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:shimmer/shimmer.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/products/domain/entities/product.dart'; /// Image Gallery Section @@ -75,10 +74,11 @@ class _ImageGallerySectionState extends State { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; final images = widget.product.images; return Container( - color: AppColors.white, + color: colorScheme.surface, child: Column( children: [ // Main Image with PageView @@ -102,16 +102,16 @@ class _ImageGallerySectionState extends State { imageUrl: images[index], fit: BoxFit.cover, placeholder: (context, url) => Shimmer.fromColors( - baseColor: AppColors.grey100, - highlightColor: AppColors.grey50, - child: Container(color: AppColors.grey100), + baseColor: colorScheme.surfaceContainerHighest, + highlightColor: colorScheme.surface, + child: Container(color: colorScheme.surfaceContainerHighest), ), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerHighest, + child: Icon( FontAwesomeIcons.image, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -144,10 +144,10 @@ class _ImageGallerySectionState extends State { builder: (context, double value, child) { return Transform.rotate( angle: value * 2 * 3.14159, - child: const Icon( + child: Icon( FontAwesomeIcons.arrowsRotate, size: 10, - color: AppColors.white, + color: colorScheme.surface, ), ); }, @@ -157,10 +157,10 @@ class _ImageGallerySectionState extends State { }, ), const SizedBox(width: 6), - const Text( + Text( '360°', style: TextStyle( - color: AppColors.white, + color: colorScheme.surface, fontSize: 12, fontWeight: FontWeight.w600, ), @@ -189,8 +189,8 @@ class _ImageGallerySectionState extends State { decoration: BoxDecoration( shape: BoxShape.circle, color: _currentImageIndex == index - ? AppColors.white - : AppColors.white.withAlpha(128), + ? colorScheme.surface + : colorScheme.surface.withAlpha(128), ), ), ), @@ -228,10 +228,10 @@ class _ImageGallerySectionState extends State { padding: const EdgeInsets.only(bottom: 4), child: Text( imageName, - style: const TextStyle( + style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -246,7 +246,7 @@ class _ImageGallerySectionState extends State { borderRadius: BorderRadius.circular(8), border: Border.all( color: isActive - ? AppColors.primaryBlue + ? colorScheme.primary : Colors.transparent, width: 2, ), @@ -257,13 +257,13 @@ class _ImageGallerySectionState extends State { imageUrl: imageUrl, fit: BoxFit.cover, placeholder: (context, url) => - Container(color: AppColors.grey100), + Container(color: colorScheme.surfaceContainerHighest), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerHighest, + child: Icon( FontAwesomeIcons.image, size: 20, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -336,11 +336,13 @@ class _ImageLightboxState extends State<_ImageLightbox> { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.transparent, - foregroundColor: AppColors.white, + foregroundColor: colorScheme.surface, elevation: 0, leading: IconButton( icon: const FaIcon(FontAwesomeIcons.xmark, size: 28), @@ -348,7 +350,7 @@ class _ImageLightboxState extends State<_ImageLightbox> { ), title: Text( '${_currentIndex + 1} / ${widget.images.length}', - style: const TextStyle(color: AppColors.white, fontSize: 16), + style: TextStyle(color: colorScheme.surface, fontSize: 16), ), ), body: Stack( @@ -370,9 +372,9 @@ class _ImageLightboxState extends State<_ImageLightbox> { child: CachedNetworkImage( imageUrl: widget.images[index], fit: BoxFit.contain, - errorWidget: (context, url, error) => const Icon( + errorWidget: (context, url, error) => Icon( FontAwesomeIcons.circleExclamation, - color: AppColors.white, + color: colorScheme.surface, size: 64, ), ), @@ -391,9 +393,9 @@ class _ImageLightboxState extends State<_ImageLightbox> { bottom: 0, child: Center( child: IconButton( - icon: const Icon( + icon: Icon( FontAwesomeIcons.chevronLeft, - color: AppColors.white, + color: colorScheme.surface, size: 32, ), onPressed: _previousImage, @@ -412,9 +414,9 @@ class _ImageLightboxState extends State<_ImageLightbox> { bottom: 0, child: Center( child: IconButton( - icon: const Icon( + icon: Icon( FontAwesomeIcons.chevronRight, - color: AppColors.white, + color: colorScheme.surface, size: 32, ), onPressed: _nextImage, @@ -443,7 +445,7 @@ class _ImageLightboxState extends State<_ImageLightbox> { ), child: Text( widget.imageCaptions[widget.images[_currentIndex]] ?? '', - style: const TextStyle(color: AppColors.white, fontSize: 16), + style: TextStyle(color: colorScheme.surface, fontSize: 16), textAlign: TextAlign.center, ), ), diff --git a/lib/features/products/presentation/widgets/product_detail/product_info_section.dart b/lib/features/products/presentation/widgets/product_detail/product_info_section.dart index acb5820..e579417 100644 --- a/lib/features/products/presentation/widgets/product_detail/product_info_section.dart +++ b/lib/features/products/presentation/widgets/product_detail/product_info_section.dart @@ -29,8 +29,10 @@ class ProductInfoSection extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( - color: AppColors.white, + color: colorScheme.surface, padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -38,7 +40,7 @@ class ProductInfoSection extends StatelessWidget { // SKU Text( 'SKU: ${product.erpnextItemCode ?? product.productId}', - style: const TextStyle(fontSize: 12, color: AppColors.grey500), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), ), const SizedBox(height: 8), @@ -46,10 +48,10 @@ class ProductInfoSection extends StatelessWidget { // Product Title Text( product.name, - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.3, ), ), @@ -62,10 +64,10 @@ class ProductInfoSection extends StatelessWidget { // Current Price Text( '${_formatPrice(product.basePrice)}/m²', - style: const TextStyle( + style: TextStyle( fontSize: 22, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), @@ -74,9 +76,9 @@ class ProductInfoSection extends StatelessWidget { const SizedBox(width: 12), Text( _formatPrice(product.basePrice * 1.12), // Mock original price - style: const TextStyle( + style: TextStyle( fontSize: 16, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, decoration: TextDecoration.lineThrough, ), ), @@ -95,8 +97,8 @@ class ProductInfoSection extends StatelessWidget { ), child: Text( '-${product.discountPercentage}%', - style: const TextStyle( - color: AppColors.white, + style: TextStyle( + color: colorScheme.surface, fontSize: 12, fontWeight: FontWeight.w600, ), @@ -109,10 +111,10 @@ class ProductInfoSection extends StatelessWidget { const SizedBox(height: 16), // Rating & Reviews Section - const Row( + Row( children: [ // Rating Stars - Row( + const Row( children: [ Icon(FontAwesomeIcons.solidStar, color: Color(0xFFffc107), size: 16), SizedBox(width: 2), @@ -126,14 +128,14 @@ class ProductInfoSection extends StatelessWidget { ], ), - SizedBox(width: 12), + const SizedBox(width: 12), // Rating Text Text( '4.8 (125 đánh giá)', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -142,9 +144,9 @@ class ProductInfoSection extends StatelessWidget { const SizedBox(height: 16), // Intro attributes quick info cards (dynamic based on non-null values) - if (_buildIntroAttributeCards(product).isNotEmpty) + if (_buildIntroAttributeCards(product, colorScheme).isNotEmpty) Row( - children: _buildIntroAttributeCards(product), + children: _buildIntroAttributeCards(product, colorScheme), ), ], ), @@ -152,7 +154,7 @@ class ProductInfoSection extends StatelessWidget { } /// Build intro attribute cards dynamically based on non-null values - List _buildIntroAttributeCards(Product product) { + List _buildIntroAttributeCards(Product product, ColorScheme colorScheme) { final cards = []; // Define available intro attributes with their display info @@ -184,6 +186,7 @@ class ProductInfoSection extends StatelessWidget { icon: attr['icon'] as IconData, label: attr['label'] as String, value: value, + colorScheme: colorScheme, ), ), ); @@ -210,30 +213,32 @@ class _QuickInfoCard extends StatelessWidget { required this.icon, required this.label, required this.value, + required this.colorScheme, }); final IconData icon; final String label; final String value; + final ColorScheme colorScheme; @override Widget build(BuildContext context) { return Column( children: [ - Icon(icon, color: AppColors.primaryBlue, size: 24), + Icon(icon, color: colorScheme.primary, size: 24), const SizedBox(height: 4), Text( label, - style: const TextStyle(fontSize: 12, color: AppColors.grey500), + style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: 2), Text( value, - style: const TextStyle( + style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), textAlign: TextAlign.center, ), diff --git a/lib/features/products/presentation/widgets/product_detail/product_tabs_section.dart b/lib/features/products/presentation/widgets/product_detail/product_tabs_section.dart index 201b741..71b3463 100644 --- a/lib/features/products/presentation/widgets/product_detail/product_tabs_section.dart +++ b/lib/features/products/presentation/widgets/product_detail/product_tabs_section.dart @@ -50,21 +50,23 @@ class _ProductTabsSectionState extends State @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( - color: AppColors.white, + color: colorScheme.surface, child: Column( children: [ // Tab Navigation Container( - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - bottom: BorderSide(color: Color(0xFFe0e0e0), width: 1), + bottom: BorderSide(color: colorScheme.outlineVariant, width: 1), ), ), child: TabBar( controller: _tabController, - labelColor: AppColors.primaryBlue, - unselectedLabelColor: AppColors.grey500, + labelColor: colorScheme.primary, + unselectedLabelColor: colorScheme.onSurfaceVariant, labelStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, @@ -73,7 +75,7 @@ class _ProductTabsSectionState extends State fontSize: 14, fontWeight: FontWeight.w500, ), - indicatorColor: AppColors.primaryBlue, + indicatorColor: colorScheme.primary, indicatorWeight: 2, tabs: const [ Tab(text: 'Thông số'), @@ -104,18 +106,20 @@ class _DescriptionTab extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Main description - const Text( + Text( 'Bộ sưu tập Cao cấp', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 12), @@ -123,22 +127,22 @@ class _DescriptionTab extends StatelessWidget { Text( product.description ?? 'Sản phẩm gạch cao cấp với chất lượng vượt trội, mang đến vẻ đẹp tự nhiên và sang trọng cho không gian của bạn. Với bề mặt có texture tinh tế, sản phẩm tạo nên những đường vân tự nhiên chân thực.', - style: const TextStyle( + style: TextStyle( fontSize: 14, height: 1.6, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 20), // Features heading - const Text( + Text( 'Đặc điểm nổi bật:', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 12), @@ -165,9 +169,9 @@ class _DescriptionTab extends StatelessWidget { Expanded( child: Text( feature, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ), @@ -179,22 +183,22 @@ class _DescriptionTab extends StatelessWidget { const SizedBox(height: 20), // Application section - const Text( + Text( 'Ứng dụng:', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 12), - const Text( + Text( 'Phù hợp cho phòng khách, phòng ngủ, hành lang, văn phòng và các không gian thương mại. Đặc biệt phù hợp với phong cách nội thất hiện đại, tối giản và Scandinavian.', style: TextStyle( fontSize: 14, height: 1.6, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -211,6 +215,8 @@ class _SpecificationsTab extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + // Default specifications if not available final specs = product.specifications.isNotEmpty ? product.specifications @@ -230,15 +236,15 @@ class _SpecificationsTab extends StatelessWidget { return Container( margin: const EdgeInsets.all(12), decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFe0e0e0)), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Column( children: [ Container( - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - bottom: BorderSide(color: Color(0xFFe0e0e0)), + bottom: BorderSide(color: colorScheme.outlineVariant), ), ), child: IntrinsicHeight( @@ -249,15 +255,15 @@ class _SpecificationsTab extends StatelessWidget { Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), - color: const Color(0xFFF4F6F8), + color: colorScheme.surface, child: Align( alignment: Alignment.centerLeft, child: Text( "Thương hiệu", - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.5, ), ), @@ -268,7 +274,7 @@ class _SpecificationsTab extends StatelessWidget { // Divider Container( width: 1, - color: const Color(0xFFe0e0e0), + color: colorScheme.outlineVariant, ), // Value @@ -277,9 +283,9 @@ class _SpecificationsTab extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), child: Text( '${product.brand}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.5, ), softWrap: true, @@ -291,9 +297,9 @@ class _SpecificationsTab extends StatelessWidget { ), ), Container( - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - bottom: BorderSide(color: Color(0xFFe0e0e0)), + bottom: BorderSide(color: colorScheme.outlineVariant), ), ), child: IntrinsicHeight( @@ -304,15 +310,15 @@ class _SpecificationsTab extends StatelessWidget { Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), - color: const Color(0xFFF4F6F8), - child: const Align( + color: colorScheme.surface, + child: Align( alignment: Alignment.centerLeft, child: Text( "Dòng sản phẩm", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.5, ), ), @@ -323,7 +329,7 @@ class _SpecificationsTab extends StatelessWidget { // Divider Container( width: 1, - color: const Color(0xFFe0e0e0), + color: colorScheme.outlineVariant, ), // Value @@ -332,9 +338,9 @@ class _SpecificationsTab extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), child: Text( '${product.itemGroupName}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.5, ), softWrap: true, @@ -351,8 +357,8 @@ class _SpecificationsTab extends StatelessWidget { decoration: BoxDecoration( border: isLast ? null - : const Border( - bottom: BorderSide(color: Color(0xFFe0e0e0)), + : Border( + bottom: BorderSide(color: colorScheme.outlineVariant), ), ), child: IntrinsicHeight( @@ -363,15 +369,15 @@ class _SpecificationsTab extends StatelessWidget { Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), - color: const Color(0xFFF4F6F8), + color: colorScheme.surface, child: Align( alignment: Alignment.centerLeft, child: Text( entry.key, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.5, ), ), @@ -382,7 +388,7 @@ class _SpecificationsTab extends StatelessWidget { // Divider Container( width: 1, - color: const Color(0xFFe0e0e0), + color: colorScheme.outlineVariant, ), // Value @@ -391,9 +397,9 @@ class _SpecificationsTab extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12), child: Text( '${entry.value}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.5, ), softWrap: true, @@ -419,6 +425,7 @@ class _ReviewsTab extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final reviewsAsync = ref.watch(productReviewsProvider(productId)); final avgRatingAsync = ref.watch(productAverageRatingProvider(productId)); @@ -437,7 +444,7 @@ class _ReviewsTab extends ConsumerWidget { data: (reviews) { if (reviews.isEmpty) { // Empty state - return _buildEmptyState(); + return _buildEmptyState(colorScheme); } return Column( @@ -445,9 +452,9 @@ class _ReviewsTab extends ConsumerWidget { children: [ // Rating Overview avgRatingAsync.when( - data: (avgRating) => _buildRatingOverview(reviews, avgRating), - loading: () => _buildRatingOverview(reviews, 0), - error: (_, __) => _buildRatingOverview(reviews, 0), + data: (avgRating) => _buildRatingOverview(colorScheme, reviews, avgRating), + loading: () => _buildRatingOverview(colorScheme, reviews, 0), + error: (_, __) => _buildRatingOverview(colorScheme, reviews, 0), ), const SizedBox(height: 24), @@ -459,22 +466,22 @@ class _ReviewsTab extends ConsumerWidget { ], ); }, - loading: () => const Center( + loading: () => Center( child: Padding( - padding: EdgeInsets.all(40), + padding: const EdgeInsets.all(40), child: CircularProgressIndicator( - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ), - error: (error, stack) => _buildErrorState(error.toString()), + error: (error, stack) => _buildErrorState(colorScheme, error.toString()), ), ], ), ); } - Widget _buildRatingOverview(List reviews, double avgRating) { + Widget _buildRatingOverview(ColorScheme colorScheme, List reviews, double avgRating) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( @@ -486,10 +493,10 @@ class _ReviewsTab extends ConsumerWidget { // Rating Score Text( avgRating.toStringAsFixed(2), - style: const TextStyle( + style: TextStyle( fontSize: 36, fontWeight: FontWeight.w700, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), @@ -505,19 +512,19 @@ class _ReviewsTab extends ConsumerWidget { if (index < avgRating.floor()) { return const Icon( FontAwesomeIcons.solidStar, - color: Color(0xFFffc107), + color: AppColors.warning, size: 18, ); } else if (index < avgRating) { return const Icon( FontAwesomeIcons.starHalfStroke, - color: Color(0xFFffc107), + color: AppColors.warning, size: 18, ); } else { return const Icon( FontAwesomeIcons.star, - color: Color(0xFFffc107), + color: AppColors.warning, size: 18, ); } @@ -529,9 +536,9 @@ class _ReviewsTab extends ConsumerWidget { // Review count Text( '${reviews.length} đánh giá', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -541,33 +548,33 @@ class _ReviewsTab extends ConsumerWidget { ); } - Widget _buildEmptyState() { - return const Center( + Widget _buildEmptyState(ColorScheme colorScheme) { + return Center( child: Padding( - padding: EdgeInsets.all(40), + padding: const EdgeInsets.all(40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( FontAwesomeIcons.commentSlash, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: 16), + const SizedBox(height: 16), Text( 'Chưa có đánh giá nào', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - SizedBox(height: 8), + const SizedBox(height: 8), Text( 'Hãy là người đầu tiên đánh giá sản phẩm này', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -577,7 +584,7 @@ class _ReviewsTab extends ConsumerWidget { ); } - Widget _buildErrorState(String error) { + Widget _buildErrorState(ColorScheme colorScheme, String error) { return Center( child: Padding( padding: const EdgeInsets.all(40), @@ -590,20 +597,20 @@ class _ReviewsTab extends ConsumerWidget { color: AppColors.danger, ), const SizedBox(height: 16), - const Text( + Text( 'Không thể tải đánh giá', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), Text( error.length > 100 ? '${error.substring(0, 100)}...' : error, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -622,11 +629,13 @@ class _ReviewItem extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16), - decoration: const BoxDecoration( - border: Border(bottom: BorderSide(color: Color(0xFFe0e0e0))), + decoration: BoxDecoration( + border: Border(bottom: BorderSide(color: colorScheme.outlineVariant)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -642,9 +651,9 @@ class _ReviewItem extends StatelessWidget { shape: BoxShape.circle, color: Color(0xFFF4F6F8), ), - child: const Icon( + child: Icon( FontAwesomeIcons.solidUser, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: 20, ), ), @@ -658,17 +667,17 @@ class _ReviewItem extends StatelessWidget { children: [ Text( review.reviewerName ?? 'Người dùng', - style: const TextStyle( + style: TextStyle( fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (review.reviewDate != null) Text( _formatDate(review.reviewDate!), - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -687,7 +696,7 @@ class _ReviewItem extends StatelessWidget { index < review.starsRating ? FontAwesomeIcons.solidStar : FontAwesomeIcons.star, - color: const Color(0xFFffc107), + color: AppColors.warning, size: 14, ), ), @@ -698,10 +707,10 @@ class _ReviewItem extends StatelessWidget { // Review Text Text( review.comment, - style: const TextStyle( + style: TextStyle( fontSize: 14, height: 1.5, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], diff --git a/lib/features/products/presentation/widgets/product_detail/sticky_action_bar.dart b/lib/features/products/presentation/widgets/product_detail/sticky_action_bar.dart index 296a9bc..5190027 100644 --- a/lib/features/products/presentation/widgets/product_detail/sticky_action_bar.dart +++ b/lib/features/products/presentation/widgets/product_detail/sticky_action_bar.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; /// Sticky Action Bar /// @@ -61,11 +60,13 @@ class StickyActionBar extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( decoration: BoxDecoration( - color: AppColors.white, - border: const Border( - top: BorderSide(color: Color(0xFFe0e0e0), width: 1), + color: colorScheme.surface, + border: Border( + top: BorderSide(color: colorScheme.outlineVariant, width: 1), ), boxShadow: [ BoxShadow( @@ -88,9 +89,9 @@ class StickyActionBar extends StatelessWidget { // Label Text( 'Số lượng ($unit)', - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), @@ -101,7 +102,7 @@ class StickyActionBar extends StatelessWidget { Container( width: 142, decoration: BoxDecoration( - border: Border.all(color: const Color(0xFFe0e0e0)), + border: Border.all(color: colorScheme.outlineVariant), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -152,9 +153,9 @@ class StickyActionBar extends StatelessWidget { if (_getConversionText().isNotEmpty) Text( _getConversionText(), - style: const TextStyle( + style: TextStyle( fontSize: 11, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -167,8 +168,8 @@ class StickyActionBar extends StatelessWidget { child: ElevatedButton.icon( onPressed: onAddToCart, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, padding: const EdgeInsets.symmetric( horizontal: 12, @@ -179,11 +180,12 @@ class StickyActionBar extends StatelessWidget { ), ), icon: const FaIcon(FontAwesomeIcons.cartShopping, size: 18), - label: const Text( + label: Text( 'Thêm vào giỏ hàng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, + color: colorScheme.onSurface ), ), ), @@ -204,17 +206,19 @@ class _QuantityButton extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return SizedBox( width: 40, height: 40, child: Material( - color: const Color(0xFFF4F6F8), + color: colorScheme.surfaceContainerHighest, child: InkWell( onTap: onPressed, child: Icon( icon, size: 20, - color: onPressed != null ? AppColors.grey900 : AppColors.grey500, + color: onPressed != null ? colorScheme.onSurface : colorScheme.onSurfaceVariant, ), ), ), diff --git a/lib/features/products/presentation/widgets/product_detail/write_review_button.dart b/lib/features/products/presentation/widgets/product_detail/write_review_button.dart index 429c178..6a6ec85 100644 --- a/lib/features/products/presentation/widgets/product_detail/write_review_button.dart +++ b/lib/features/products/presentation/widgets/product_detail/write_review_button.dart @@ -6,12 +6,11 @@ library; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:go_router/go_router.dart'; -import 'package:worker/core/theme/colors.dart'; /// Write Review Button /// /// Displays a prominent button for users to write a review: -/// - Primary blue background +/// - Primary background from theme /// - Edit icon /// - Text: "Viết đánh giá của bạn" /// - Navigates to WriteReviewPage with productId @@ -26,6 +25,8 @@ class WriteReviewButton extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( width: double.infinity, margin: const EdgeInsets.only(bottom: 20), @@ -35,28 +36,28 @@ class WriteReviewButton extends StatelessWidget { context.push('/products/$productId/write-review'); }, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, + backgroundColor: colorScheme.primary, padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 28), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), elevation: 0, ), - child: const Row( + child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( FontAwesomeIcons.penToSquare, size: 16, - color: AppColors.white, + color: colorScheme.onPrimary, ), - SizedBox(width: 10), + const SizedBox(width: 10), Text( 'Viết đánh giá của bạn', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.white, + color: colorScheme.onPrimary, ), ), ], diff --git a/lib/features/products/presentation/widgets/product_filter_drawer.dart b/lib/features/products/presentation/widgets/product_filter_drawer.dart index 900ab23..df5188a 100644 --- a/lib/features/products/presentation/widgets/product_filter_drawer.dart +++ b/lib/features/products/presentation/widgets/product_filter_drawer.dart @@ -24,6 +24,7 @@ class ProductFilterDrawer extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final filtersState = ref.watch(productFiltersProvider); final filterOptionsAsync = ref.watch(productFilterOptionsProvider); @@ -35,26 +36,26 @@ class ProductFilterDrawer extends ConsumerWidget { // Header Container( padding: const EdgeInsets.all(AppSpacing.lg), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - bottom: BorderSide(color: AppColors.grey100, width: 1), + bottom: BorderSide(color: colorScheme.outlineVariant, width: 1), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text( + Text( 'Bộ lọc sản phẩm', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), IconButton( icon: const FaIcon(FontAwesomeIcons.xmark, size: 20), onPressed: () => Navigator.of(context).pop(), - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ], ), @@ -69,6 +70,7 @@ class ProductFilterDrawer extends ConsumerWidget { children: [ // Nhóm sản phẩm (Item Groups) - from API _buildFilterGroup( + context: context, title: 'Nhóm sản phẩm', options: filterOptions.groups, initiallyExpanded: true, @@ -82,6 +84,7 @@ class ProductFilterDrawer extends ConsumerWidget { // Thương hiệu (Brands) - from API _buildFilterGroup( + context: context, title: 'Thương hiệu', options: filterOptions.brands, selectedValues: filtersState.brands, @@ -103,6 +106,7 @@ class ProductFilterDrawer extends ConsumerWidget { return Column( children: [ _buildAttributeGroup( + context: context, title: attrGroup.attributeName, attributeGroup: attrGroup, selectedValues: selectedValues, @@ -126,9 +130,9 @@ class ProductFilterDrawer extends ConsumerWidget { // Footer Buttons Container( padding: const EdgeInsets.all(AppSpacing.lg), - decoration: const BoxDecoration( + decoration: BoxDecoration( border: Border( - top: BorderSide(color: AppColors.grey100, width: 1), + top: BorderSide(color: colorScheme.outlineVariant, width: 1), ), ), child: Row( @@ -140,9 +144,9 @@ class ProductFilterDrawer extends ConsumerWidget { ref.read(productFiltersProvider.notifier).reset(); }, style: OutlinedButton.styleFrom( - foregroundColor: AppColors.grey900, - side: const BorderSide( - color: AppColors.grey100, + foregroundColor: colorScheme.onSurface, + side: BorderSide( + color: colorScheme.outlineVariant, width: 1, ), padding: const EdgeInsets.symmetric( @@ -181,8 +185,8 @@ class ProductFilterDrawer extends ConsumerWidget { } }, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, elevation: 0, padding: const EdgeInsets.symmetric( vertical: AppSpacing.md, @@ -205,9 +209,9 @@ class ProductFilterDrawer extends ConsumerWidget { ), ], ), - loading: () => const Center( + loading: () => Center( child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation(AppColors.primaryBlue), + valueColor: AlwaysStoppedAnimation(colorScheme.primary), ), ), error: (error, stack) => Center( @@ -222,20 +226,20 @@ class ProductFilterDrawer extends ConsumerWidget { color: AppColors.danger, ), const SizedBox(height: AppSpacing.md), - const Text( + Text( 'Không thể tải bộ lọc', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: AppSpacing.sm), Text( error.toString(), - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.center, ), @@ -245,8 +249,8 @@ class ProductFilterDrawer extends ConsumerWidget { ref.invalidate(productFilterOptionsProvider); }, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, ), child: const Text('Thử lại'), ), @@ -260,12 +264,15 @@ class ProductFilterDrawer extends ConsumerWidget { } Widget _buildFilterGroup({ + required BuildContext context, required String title, required List options, required Set selectedValues, required Function(String) onToggle, bool initiallyExpanded = false, }) { + final colorScheme = Theme.of(context).colorScheme; + return Theme( data: ThemeData( dividerColor: Colors.transparent, @@ -277,10 +284,10 @@ class ProductFilterDrawer extends ConsumerWidget { childrenPadding: const EdgeInsets.only(left: 8), title: Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), initiallyExpanded: initiallyExpanded, @@ -288,9 +295,9 @@ class ProductFilterDrawer extends ConsumerWidget { return CheckboxListTile( title: Text( option.label, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), value: selectedValues.contains(option.value), @@ -300,7 +307,7 @@ class ProductFilterDrawer extends ConsumerWidget { controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, dense: true, - activeColor: AppColors.primaryBlue, + activeColor: colorScheme.primary, ); }).toList(), ), @@ -308,11 +315,14 @@ class ProductFilterDrawer extends ConsumerWidget { } Widget _buildAttributeGroup({ + required BuildContext context, required String title, required AttributeGroup attributeGroup, required Set selectedValues, required Function(String) onToggle, }) { + final colorScheme = Theme.of(context).colorScheme; + return Theme( data: ThemeData( dividerColor: Colors.transparent, @@ -324,10 +334,10 @@ class ProductFilterDrawer extends ConsumerWidget { childrenPadding: const EdgeInsets.only(left: 8), title: Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), initiallyExpanded: false, @@ -335,17 +345,17 @@ class ProductFilterDrawer extends ConsumerWidget { return CheckboxListTile( title: Text( value.attributeValue, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), // subtitle: value.abbr != null // ? Text( // 'Mã: ${value.abbr}', - // style: const TextStyle( + // style: TextStyle( // fontSize: 12, - // color: AppColors.grey500, + // color: colorScheme.onSurfaceVariant, // ), // ) // : null, @@ -356,7 +366,7 @@ class ProductFilterDrawer extends ConsumerWidget { controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, dense: true, - activeColor: AppColors.primaryBlue, + activeColor: colorScheme.primary, ); }).toList(), ), diff --git a/lib/features/products/presentation/widgets/product_grid.dart b/lib/features/products/presentation/widgets/product_grid.dart index aac9f56..d5362d6 100644 --- a/lib/features/products/presentation/widgets/product_grid.dart +++ b/lib/features/products/presentation/widgets/product_grid.dart @@ -11,6 +11,7 @@ import 'package:worker/features/products/presentation/widgets/product_card.dart' /// Product Grid Widget /// /// Displays products in a 2-column grid layout with scroll-to-load-more. +/// Fully theme-compliant using Material 3 ColorScheme. class ProductGrid extends StatefulWidget { final List products; final void Function(Product)? onProductTap; @@ -61,6 +62,8 @@ class _ProductGridState extends State { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return GridView.builder( controller: _scrollController, padding: const EdgeInsets.all(AppSpacing.xs), @@ -74,10 +77,12 @@ class _ProductGridState extends State { itemBuilder: (context, index) { // Show loading indicator at the end if (index == widget.products.length) { - return const Center( + return Center( child: Padding( - padding: EdgeInsets.all(AppSpacing.md), - child: CircularProgressIndicator(), + padding: const EdgeInsets.all(AppSpacing.md), + child: CircularProgressIndicator( + color: colorScheme.primary, + ), ), ); } diff --git a/lib/features/products/presentation/widgets/product_search_bar.dart b/lib/features/products/presentation/widgets/product_search_bar.dart index c424e68..374dca3 100644 --- a/lib/features/products/presentation/widgets/product_search_bar.dart +++ b/lib/features/products/presentation/widgets/product_search_bar.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/products/presentation/providers/search_query_provider.dart'; import 'package:worker/generated/l10n/app_localizations.dart'; @@ -62,6 +61,7 @@ class _ProductSearchBarState extends ConsumerState { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context); + final colorScheme = Theme.of(context).colorScheme; return SizedBox( height: InputFieldSpecs.height, @@ -71,27 +71,27 @@ class _ProductSearchBarState extends ConsumerState { onChanged: _onSearchChanged, decoration: InputDecoration( hintText: l10n.searchProducts, - hintStyle: const TextStyle( + hintStyle: TextStyle( fontSize: InputFieldSpecs.hintFontSize, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - prefixIcon: const Icon( + prefixIcon: Icon( FontAwesomeIcons.magnifyingGlass, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: AppIconSize.md, ), suffixIcon: _hasText ? IconButton( - icon: const Icon( + icon: Icon( FontAwesomeIcons.xmark, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, size: AppIconSize.md, ), onPressed: _onClearSearch, ) : null, filled: true, - fillColor: Colors.white, + fillColor: colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), borderSide: BorderSide.none, @@ -102,8 +102,8 @@ class _ProductSearchBarState extends ConsumerState { ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(InputFieldSpecs.borderRadius), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2.0, ), ), diff --git a/lib/features/products/presentation/widgets/write_review/review_guidelines_card.dart b/lib/features/products/presentation/widgets/write_review/review_guidelines_card.dart index 7cc087b..cb6167f 100644 --- a/lib/features/products/presentation/widgets/write_review/review_guidelines_card.dart +++ b/lib/features/products/presentation/widgets/write_review/review_guidelines_card.dart @@ -5,21 +5,17 @@ library; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:worker/core/theme/colors.dart'; /// Review Guidelines Card Widget /// /// Displays helpful tips for writing product reviews: -/// - Light blue background (#f0f7ff) -/// - Blue left border +/// - Light blue background (colorScheme.primaryContainer) +/// - Blue left border (colorScheme.primary) /// - Lightbulb icon /// - 4 bullet points with guidelines class ReviewGuidelinesCard extends StatelessWidget { const ReviewGuidelinesCard({super.key}); - // Guidelines background color - static const Color _backgroundColor = Color(0xFFF0F7FF); - // Guidelines list static const List _guidelines = [ 'Chia sẻ trải nghiệm thực tế của bạn về sản phẩm', @@ -30,14 +26,16 @@ class ReviewGuidelinesCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: _backgroundColor, + color: colorScheme.primaryContainer, borderRadius: BorderRadius.circular(8), - border: const Border( + border: Border( left: BorderSide( - color: AppColors.primaryBlue, + color: colorScheme.primary, width: 4, ), ), @@ -46,20 +44,20 @@ class ReviewGuidelinesCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header - const Row( + Row( children: [ Icon( FontAwesomeIcons.lightbulb, size: 16, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), - SizedBox(width: 8), + const SizedBox(width: 8), Text( 'Gợi ý viết đánh giá tốt', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: AppColors.primaryBlue, + color: colorScheme.primary, ), ), ], @@ -76,20 +74,20 @@ class ReviewGuidelinesCard extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( '• ', style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.6, ), ), Expanded( child: Text( _guidelines[index], - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, height: 1.6, ), ), diff --git a/lib/features/products/presentation/widgets/write_review/star_rating_selector.dart b/lib/features/products/presentation/widgets/write_review/star_rating_selector.dart index 2cc05ab..3b25123 100644 --- a/lib/features/products/presentation/widgets/write_review/star_rating_selector.dart +++ b/lib/features/products/presentation/widgets/write_review/star_rating_selector.dart @@ -6,7 +6,6 @@ library; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:worker/core/constants/ui_constants.dart'; -import 'package:worker/core/theme/colors.dart'; /// Star Rating Selector Widget /// @@ -44,7 +43,7 @@ class _StarRatingSelectorState extends State { 5: 'Rất hài lòng', }; - // Colors + // Star colors (semantic - keep as AppColors) static const Color _starUnselected = Color(0xFFe0e0e0); static const Color _starHover = Color(0xFFffc107); static const Color _starSelected = Color(0xFFff9800); @@ -54,6 +53,8 @@ class _StarRatingSelectorState extends State { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Column( children: [ // Stars Row @@ -99,7 +100,7 @@ class _StarRatingSelectorState extends State { decoration: BoxDecoration( color: widget.rating > 0 ? _labelBackgroundSelected - : AppColors.grey50, + : colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), ), child: Text( @@ -107,7 +108,7 @@ class _StarRatingSelectorState extends State { style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: widget.rating > 0 ? _starSelected : AppColors.grey500, + color: widget.rating > 0 ? _starSelected : colorScheme.onSurfaceVariant, ), ), ), diff --git a/lib/features/projects/presentation/pages/submission_create_page.dart b/lib/features/projects/presentation/pages/submission_create_page.dart index c47e730..daa9b8f 100644 --- a/lib/features/projects/presentation/pages/submission_create_page.dart +++ b/lib/features/projects/presentation/pages/submission_create_page.dart @@ -131,26 +131,28 @@ class _SubmissionCreatePageState extends ConsumerState { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( leading: IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.arrowLeft, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () => Navigator.of(context).pop(), ), title: Text( isEditing ? 'Chỉnh sửa Dự án' : 'Đăng ký Công trình', - style: const TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), actions: [ IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.circleInfo, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: _showInfoDialog, @@ -158,19 +160,19 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(width: AppSpacing.sm), ], elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, centerTitle: false, ), body: _isLoadingDetail - ? const Center( + ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - CircularProgressIndicator(), - SizedBox(height: 16), + const CircularProgressIndicator(), + const SizedBox(height: 16), Text( 'Đang tải thông tin dự án...', - style: TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), @@ -181,23 +183,23 @@ class _SubmissionCreatePageState extends ConsumerState { padding: const EdgeInsets.all(4), children: [ // Basic Information - _buildBasicInfoCard(), + _buildBasicInfoCard(colorScheme), const SizedBox(height: 16), // Project Details - _buildProjectDetailsCard(), + _buildProjectDetailsCard(colorScheme), const SizedBox(height: 16), // Additional Information - _buildAdditionalInfoCard(), + _buildAdditionalInfoCard(colorScheme), const SizedBox(height: 16), // File Upload - _buildFileUploadCard(), + _buildFileUploadCard(colorScheme), const SizedBox(height: 24), // Submit Button - _buildSubmitButton(), + _buildSubmitButton(colorScheme), const SizedBox(height: 40), ], ), @@ -205,7 +207,7 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildBasicInfoCard() { + Widget _buildBasicInfoCard(ColorScheme colorScheme) { return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -214,17 +216,18 @@ class _SubmissionCreatePageState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Thông tin cơ bản', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _projectNameController, label: 'Tên công trình', required: true, @@ -233,6 +236,7 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _addressController, label: 'Địa chỉ', required: true, @@ -242,6 +246,7 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _ownerController, label: 'Chủ đầu tư', required: true, @@ -250,6 +255,7 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _designUnitController, label: 'Đơn vị thiết kế', hint: 'Tên đơn vị thiết kế', @@ -257,6 +263,7 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _constructionUnitController, label: 'Đơn vị thi công', hint: 'Tên đơn vị thi công', @@ -267,7 +274,7 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildProjectDetailsCard() { + Widget _buildProjectDetailsCard(ColorScheme colorScheme) { return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -276,17 +283,18 @@ class _SubmissionCreatePageState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Chi tiết dự án', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _areaController, label: 'Tổng diện tích', required: true, @@ -296,6 +304,7 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _productsController, label: 'Sản phẩm đưa vào thiết kế', required: true, @@ -305,17 +314,17 @@ class _SubmissionCreatePageState extends ConsumerState { ), const SizedBox(height: 16), - _buildProgressDropdown(), + _buildProgressDropdown(colorScheme), const SizedBox(height: 16), - _buildExpectedDateField(), + _buildExpectedDateField(colorScheme), ], ), ), ); } - Widget _buildAdditionalInfoCard() { + Widget _buildAdditionalInfoCard(ColorScheme colorScheme) { return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -324,17 +333,18 @@ class _SubmissionCreatePageState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Thông tin bổ sung', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), _buildTextField( + colorScheme: colorScheme, controller: _descriptionController, label: 'Mô tả công trình', hint: 'Mô tả ngắn gọn về công trình, diện tích, đặc điểm nổi bật...', @@ -346,7 +356,7 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildFileUploadCard() { + Widget _buildFileUploadCard(ColorScheme colorScheme) { return Card( elevation: 1, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -355,12 +365,12 @@ class _SubmissionCreatePageState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Ảnh minh chứng', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 16), @@ -374,34 +384,34 @@ class _SubmissionCreatePageState extends ConsumerState { width: double.infinity, decoration: BoxDecoration( border: Border.all( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, width: 2, style: BorderStyle.solid, ), borderRadius: BorderRadius.circular(8), ), - child: const Column( + child: Column( children: [ FaIcon( FontAwesomeIcons.cloudArrowUp, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: 12), + const SizedBox(height: 12), Text( 'Kéo thả ảnh vào đây', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - SizedBox(height: 4), + const SizedBox(height: 4), Text( 'hoặc nhấn để chọn file', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -412,12 +422,12 @@ class _SubmissionCreatePageState extends ConsumerState { // Existing files from API if (_existingFiles.isNotEmpty) ...[ const SizedBox(height: 16), - const Text( + Text( 'Ảnh đã tải lên', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), @@ -426,7 +436,7 @@ class _SubmissionCreatePageState extends ConsumerState { final file = entry.value; return Padding( padding: const EdgeInsets.only(bottom: 8), - child: _buildExistingFileItem(file, index), + child: _buildExistingFileItem(file, index, colorScheme), ); }), ], @@ -435,12 +445,12 @@ class _SubmissionCreatePageState extends ConsumerState { if (_uploadedFiles.isNotEmpty) ...[ const SizedBox(height: 16), if (_existingFiles.isNotEmpty) - const Text( + Text( 'Ảnh mới', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), if (_existingFiles.isNotEmpty) const SizedBox(height: 8), @@ -449,17 +459,17 @@ class _SubmissionCreatePageState extends ConsumerState { final file = entry.value; return Padding( padding: const EdgeInsets.only(bottom: 8), - child: _buildFileItem(file, index), + child: _buildFileItem(file, index, colorScheme), ); }), ], const SizedBox(height: 8), - const Text( + Text( 'Hỗ trợ: JPG, PNG, PDF. Tối đa 10MB mỗi file.', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -469,6 +479,7 @@ class _SubmissionCreatePageState extends ConsumerState { } Widget _buildTextField({ + required ColorScheme colorScheme, required TextEditingController controller, required String label, String? hint, @@ -484,10 +495,10 @@ class _SubmissionCreatePageState extends ConsumerState { children: [ Text( label, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (required) @@ -507,20 +518,20 @@ class _SubmissionCreatePageState extends ConsumerState { keyboardType: keyboardType, decoration: InputDecoration( hintText: hint, - hintStyle: const TextStyle(color: AppColors.grey500), + hintStyle: TextStyle(color: colorScheme.onSurfaceVariant), filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.primaryBlue, width: 2), + borderSide: BorderSide(color: colorScheme.primary, width: 2), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), @@ -544,9 +555,9 @@ class _SubmissionCreatePageState extends ConsumerState { const SizedBox(height: 4), Text( helperText, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -554,23 +565,23 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildProgressDropdown() { + Widget _buildProgressDropdown(ColorScheme colorScheme) { final progressListAsync = ref.watch(projectProgressListProvider); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( + Row( children: [ Text( 'Tiến độ công trình', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - Text( + const Text( ' *', style: TextStyle( fontSize: 14, @@ -585,14 +596,14 @@ class _SubmissionCreatePageState extends ConsumerState { initialValue: _selectedProgress, decoration: InputDecoration( filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, @@ -621,26 +632,26 @@ class _SubmissionCreatePageState extends ConsumerState { loading: () => Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: AppColors.white, - border: Border.all(color: AppColors.grey100), + color: colorScheme.surface, + border: Border.all(color: colorScheme.surfaceContainerHighest), borderRadius: BorderRadius.circular(8), ), - child: const Row( + child: Row( children: [ - SizedBox( + const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ), - SizedBox(width: 12), - Text('Đang tải...', style: TextStyle(color: AppColors.grey500)), + const SizedBox(width: 12), + Text('Đang tải...', style: TextStyle(color: colorScheme.onSurfaceVariant)), ], ), ), error: (error, _) => Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, border: Border.all(color: AppColors.danger), borderRadius: BorderRadius.circular(8), ), @@ -666,16 +677,16 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildExpectedDateField() { + Widget _buildExpectedDateField(ColorScheme colorScheme) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Ngày dự kiến khởi công', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), @@ -684,8 +695,8 @@ class _SubmissionCreatePageState extends ConsumerState { child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: AppColors.white, - border: Border.all(color: AppColors.grey100), + color: colorScheme.surface, + border: Border.all(color: colorScheme.surfaceContainerHighest), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -697,14 +708,14 @@ class _SubmissionCreatePageState extends ConsumerState { : 'Chọn ngày', style: TextStyle( color: _expectedStartDate != null - ? AppColors.grey900 - : AppColors.grey500, + ? colorScheme.onSurface + : colorScheme.onSurfaceVariant, ), ), - const FaIcon( + FaIcon( FontAwesomeIcons.calendar, size: 16, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ], ), @@ -714,7 +725,7 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildFileItem(File file, int index) { + Widget _buildFileItem(File file, int index, ColorScheme colorScheme) { final fileName = file.path.split('/').last; final fileSizeInBytes = file.lengthSync(); final fileSizeInMB = (fileSizeInBytes / (1024 * 1024)).toStringAsFixed(2); @@ -728,13 +739,13 @@ class _SubmissionCreatePageState extends ConsumerState { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFFF8F9FA), + color: colorScheme.surfaceContainerLowest, border: Border.all( color: hasError ? AppColors.danger : isUploaded ? AppColors.success - : AppColors.grey100, + : colorScheme.surfaceContainerHighest, ), borderRadius: BorderRadius.circular(6), ), @@ -754,11 +765,11 @@ class _SubmissionCreatePageState extends ConsumerState { return Container( width: 48, height: 48, - color: AppColors.grey100, - child: const FaIcon( + color: colorScheme.surfaceContainerHighest, + child: FaIcon( FontAwesomeIcons.image, size: 24, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ); }, @@ -810,9 +821,9 @@ class _SubmissionCreatePageState extends ConsumerState { children: [ Text( fileName, - style: const TextStyle( + style: TextStyle( fontWeight: FontWeight.w500, - color: AppColors.grey900, + color: colorScheme.onSurface, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -832,7 +843,7 @@ class _SubmissionCreatePageState extends ConsumerState { ? AppColors.danger : isUploaded ? AppColors.success - : AppColors.grey500, + : colorScheme.onSurfaceVariant, ), ), ], @@ -857,16 +868,16 @@ class _SubmissionCreatePageState extends ConsumerState { ); } - Widget _buildExistingFileItem(ProjectFile file, int index) { + Widget _buildExistingFileItem(ProjectFile file, int index, ColorScheme colorScheme) { final fileName = file.fileUrl.split('/').last; final isDeleting = _deletingFileId == file.id; return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFFF8F9FA), + color: colorScheme.surfaceContainerLowest, border: Border.all( - color: isDeleting ? AppColors.grey500 : AppColors.success, + color: isDeleting ? colorScheme.onSurfaceVariant : AppColors.success, ), borderRadius: BorderRadius.circular(6), ), @@ -885,7 +896,7 @@ class _SubmissionCreatePageState extends ConsumerState { placeholder: (context, url) => Container( width: 48, height: 48, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: SizedBox( width: 16, @@ -897,12 +908,12 @@ class _SubmissionCreatePageState extends ConsumerState { errorWidget: (context, url, error) => Container( width: 48, height: 48, - color: AppColors.grey100, - child: const Center( + color: colorScheme.surfaceContainerHighest, + child: Center( child: FaIcon( FontAwesomeIcons.image, size: 24, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -939,7 +950,7 @@ class _SubmissionCreatePageState extends ConsumerState { fileName, style: TextStyle( fontWeight: FontWeight.w500, - color: isDeleting ? AppColors.grey500 : AppColors.grey900, + color: isDeleting ? colorScheme.onSurfaceVariant : colorScheme.onSurface, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -949,7 +960,7 @@ class _SubmissionCreatePageState extends ConsumerState { isDeleting ? 'Đang xóa...' : 'Đã tải lên', style: TextStyle( fontSize: 12, - color: isDeleting ? AppColors.grey500 : AppColors.success, + color: isDeleting ? colorScheme.onSurfaceVariant : AppColors.success, ), ), ], @@ -980,6 +991,7 @@ class _SubmissionCreatePageState extends ConsumerState { /// Show confirmation dialog before deleting an existing file Future _showDeleteConfirmDialog(ProjectFile file) async { final fileName = file.fileUrl.split('/').last; + final colorScheme = Theme.of(context).colorScheme; final confirmed = await showDialog( context: context, @@ -1004,7 +1016,7 @@ class _SubmissionCreatePageState extends ConsumerState { Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: Row( @@ -1019,12 +1031,12 @@ class _SubmissionCreatePageState extends ConsumerState { placeholder: (context, url) => Container( width: 48, height: 48, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, ), errorWidget: (context, url, error) => Container( width: 48, height: 48, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center(child: FaIcon(FontAwesomeIcons.image, size: 20)), ), ), @@ -1033,9 +1045,9 @@ class _SubmissionCreatePageState extends ConsumerState { Expanded( child: Text( fileName, - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), maxLines: 2, overflow: TextOverflow.ellipsis, @@ -1118,17 +1130,17 @@ class _SubmissionCreatePageState extends ConsumerState { } } - Widget _buildSubmitButton() { + Widget _buildSubmitButton(ColorScheme colorScheme) { return SizedBox( width: double.infinity, height: 48, child: ElevatedButton( onPressed: _isSubmitting ? null : _handleSubmit, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, - disabledBackgroundColor: AppColors.primaryBlue.withValues(alpha: 0.6), - disabledForegroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: Colors.white, + disabledBackgroundColor: colorScheme.primary.withValues(alpha: 0.6), + disabledForegroundColor: Colors.white, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -1140,7 +1152,7 @@ class _SubmissionCreatePageState extends ConsumerState { height: 24, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(AppColors.white), + valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : const Row( diff --git a/lib/features/projects/presentation/pages/submissions_page.dart b/lib/features/projects/presentation/pages/submissions_page.dart index 3d16572..4cd21ca 100644 --- a/lib/features/projects/presentation/pages/submissions_page.dart +++ b/lib/features/projects/presentation/pages/submissions_page.dart @@ -20,25 +20,26 @@ class SubmissionsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final submissionsAsync = ref.watch(filteredSubmissionsProvider); final statusListAsync = ref.watch(projectStatusListProvider); final filter = ref.watch(submissionsFilterProvider); final selectedStatus = filter.selectedStatus; return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( leading: IconButton( - icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.arrowLeft, color: colorScheme.onSurface, size: 20), onPressed: () => Navigator.of(context).pop(), ), - title: const Text( + title: Text( 'Danh sách Dự án', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), actions: [ IconButton( - icon: const FaIcon(FontAwesomeIcons.plus, color: Colors.black, size: 20), + icon: FaIcon(FontAwesomeIcons.plus, color: colorScheme.onSurface, size: 20), onPressed: () async { final result = await context.push(RouteNames.submissionCreate); if (result == true) { @@ -50,7 +51,7 @@ class SubmissionsPage extends ConsumerWidget { const SizedBox(width: AppSpacing.sm), ], elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, centerTitle: false, ), body: Column( @@ -61,16 +62,16 @@ class SubmissionsPage extends ConsumerWidget { child: TextField( decoration: InputDecoration( hintText: 'Mã dự án hoặc tên công trình', - prefixIcon: const Icon(Icons.search, color: AppColors.grey500), + prefixIcon: Icon(Icons.search, color: colorScheme.onSurfaceVariant), filled: true, - fillColor: AppColors.white, + fillColor: colorScheme.surface, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100), + borderSide: BorderSide(color: colorScheme.surfaceContainerHighest), ), ), onChanged: (value) { @@ -88,6 +89,7 @@ class SubmissionsPage extends ConsumerWidget { _buildFilterChip( context, ref, + colorScheme, label: 'Tất cả', isSelected: selectedStatus == null, onTap: () => ref.read(submissionsFilterProvider.notifier).clearStatusFilter(), @@ -101,6 +103,7 @@ class SubmissionsPage extends ConsumerWidget { child: _buildFilterChip( context, ref, + colorScheme, label: status.label, isSelected: selectedStatus == status.label, onTap: () => ref.read(submissionsFilterProvider.notifier).selectStatus(status.label), @@ -130,28 +133,28 @@ class SubmissionsPage extends ConsumerWidget { children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.5, - child: const Center( + child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ FaIcon( FontAwesomeIcons.folderOpen, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: 16), + const SizedBox(height: 16), Text( 'Không có dự án nào', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - SizedBox(height: 8), + const SizedBox(height: 8), Text( 'Không tìm thấy dự án phù hợp', - style: TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), @@ -171,7 +174,7 @@ class SubmissionsPage extends ConsumerWidget { itemCount: submissions.length, itemBuilder: (context, index) { final submission = submissions[index]; - return _buildSubmissionCard(context, ref, submission); + return _buildSubmissionCard(context, ref, colorScheme, submission); }, ), ); @@ -198,24 +201,24 @@ class SubmissionsPage extends ConsumerWidget { color: AppColors.danger, ), const SizedBox(height: 16), - const Text( + Text( 'Có lỗi xảy ra', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), Text( error.toString(), - style: const TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), textAlign: TextAlign.center, ), const SizedBox(height: 16), - const Text( + Text( 'Kéo xuống để thử lại', - style: TextStyle(color: AppColors.grey500), + style: TextStyle(color: colorScheme.onSurfaceVariant), ), ], ), @@ -233,7 +236,8 @@ class SubmissionsPage extends ConsumerWidget { Widget _buildFilterChip( BuildContext context, - WidgetRef ref, { + WidgetRef ref, + ColorScheme colorScheme, { required String label, required bool isSelected, required VoidCallback onTap, @@ -243,16 +247,16 @@ class SubmissionsPage extends ConsumerWidget { child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( - color: isSelected ? AppColors.primaryBlue : AppColors.white, + color: isSelected ? colorScheme.primary : colorScheme.surface, borderRadius: BorderRadius.circular(20), border: Border.all( - color: isSelected ? AppColors.primaryBlue : AppColors.grey100, + color: isSelected ? colorScheme.primary : colorScheme.surfaceContainerHighest, ), ), child: Text( label, style: TextStyle( - color: isSelected ? AppColors.white : AppColors.grey900, + color: isSelected ? colorScheme.onPrimary : colorScheme.onSurface, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), @@ -260,7 +264,7 @@ class SubmissionsPage extends ConsumerWidget { ); } - Widget _buildSubmissionCard(BuildContext context, WidgetRef ref, ProjectSubmission submission) { + Widget _buildSubmissionCard(BuildContext context, WidgetRef ref, ColorScheme colorScheme, ProjectSubmission submission) { return Card( margin: const EdgeInsets.only(bottom: 12), elevation: 1, @@ -288,13 +292,13 @@ class SubmissionsPage extends ConsumerWidget { children: [ Text( submission.designedArea, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), - _buildStatusBadge(submission.status, submission.statusColor), + _buildStatusBadge(colorScheme, submission.status, submission.statusColor), ], ), const SizedBox(height: 8), @@ -308,17 +312,17 @@ class SubmissionsPage extends ConsumerWidget { // const SizedBox(height: 4), Text( 'Ngày nộp: ${DateFormat('dd/MM/yyyy HH:mm').format(submission.requestDate)}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 4), Text( 'Diện tích: ${submission.designArea} m²', - style: const TextStyle( + style: TextStyle( fontSize: 13, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (submission.reasonForRejection != null) ...[ @@ -357,8 +361,8 @@ class SubmissionsPage extends ConsumerWidget { ); } - Widget _buildStatusBadge(String status, String statusColor) { - final color = _getColorFromStatusColor(statusColor); + Widget _buildStatusBadge(ColorScheme colorScheme, String status, String statusColor) { + final color = _getColorFromStatusColor(colorScheme, statusColor); return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( @@ -376,7 +380,7 @@ class SubmissionsPage extends ConsumerWidget { ); } - Color _getColorFromStatusColor(String statusColor) { + Color _getColorFromStatusColor(ColorScheme colorScheme, String statusColor) { switch (statusColor) { case 'Warning': return AppColors.warning; @@ -387,7 +391,7 @@ class SubmissionsPage extends ConsumerWidget { case 'Info': return AppColors.info; default: - return AppColors.grey500; + return colorScheme.onSurfaceVariant; } } } diff --git a/lib/features/showrooms/presentation/pages/design_request_create_page.dart b/lib/features/showrooms/presentation/pages/design_request_create_page.dart index d8d7ab8..49805c0 100644 --- a/lib/features/showrooms/presentation/pages/design_request_create_page.dart +++ b/lib/features/showrooms/presentation/pages/design_request_create_page.dart @@ -14,6 +14,8 @@ import 'package:intl/intl.dart'; import 'package:worker/core/constants/ui_constants.dart'; import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/showrooms/presentation/providers/design_request_provider.dart'; +import 'package:worker/features/showrooms/presentation/widgets/file_preview_item.dart'; +import 'package:worker/features/showrooms/presentation/widgets/form_field_widget.dart'; /// Design Request Create Page /// @@ -27,6 +29,7 @@ class DesignRequestCreatePage extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final formKey = useMemoized(() => GlobalKey()); final projectNameController = useTextEditingController(); final areaController = useTextEditingController(); @@ -227,19 +230,19 @@ class DesignRequestCreatePage extends HookConsumerWidget { } return Scaffold( - backgroundColor: AppColors.grey50, + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), + icon: Icon(Icons.arrow_back, color: colorScheme.onSurface), onPressed: () => Navigator.of(context).pop(), ), centerTitle: false, - title: const Text( + title: Text( 'Tạo yêu cầu thiết kế mới', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 20, fontWeight: FontWeight.w600, ), @@ -268,16 +271,16 @@ class DesignRequestCreatePage extends HookConsumerWidget { children: [ Icon( Icons.info_outline, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 20, ), const SizedBox(width: 8), - const Text( + Text( 'Thông tin cơ bản', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -286,7 +289,7 @@ class DesignRequestCreatePage extends HookConsumerWidget { const SizedBox(height: 20), // Project Name - _FormField( + FormFieldWidget( label: 'Tên dự án/Khách hàng', required: true, controller: projectNameController, @@ -302,7 +305,7 @@ class DesignRequestCreatePage extends HookConsumerWidget { const SizedBox(height: 20), // Area - _FormField( + FormFieldWidget( label: 'Diện tích (m²)', required: true, controller: areaController, @@ -323,7 +326,7 @@ class DesignRequestCreatePage extends HookConsumerWidget { const SizedBox(height: 20), // Location - _FormField( + FormFieldWidget( label: 'Khu vực (Tỉnh/ Thành phố)', required: true, controller: locationController, @@ -343,14 +346,14 @@ class DesignRequestCreatePage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( - text: const TextSpan( + text: TextSpan( text: 'Phong cách mong muốn', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), - children: [ + children: const [ TextSpan( text: ' *', style: TextStyle(color: AppColors.danger), @@ -367,22 +370,22 @@ class DesignRequestCreatePage extends HookConsumerWidget { hintText: '-- Chọn phong cách --', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -444,12 +447,12 @@ class DesignRequestCreatePage extends HookConsumerWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Text( + Text( 'Ngân sách dự kiến', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), @@ -461,22 +464,22 @@ class DesignRequestCreatePage extends HookConsumerWidget { hintText: '-- Chọn ngân sách --', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -525,14 +528,14 @@ class DesignRequestCreatePage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( - text: const TextSpan( + text: TextSpan( text: 'Thời hạn mong muốn', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), - children: [ + children: const [ TextSpan( text: ' *', style: TextStyle(color: AppColors.danger), @@ -550,22 +553,22 @@ class DesignRequestCreatePage extends HookConsumerWidget { suffixIcon: const Icon(Icons.calendar_today, size: 20), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -606,16 +609,16 @@ class DesignRequestCreatePage extends HookConsumerWidget { children: [ Icon( Icons.edit_outlined, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 20, ), const SizedBox(width: 8), - const Text( + Text( 'Yêu cầu chi tiết', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -628,14 +631,14 @@ class DesignRequestCreatePage extends HookConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ RichText( - text: const TextSpan( + text: TextSpan( text: 'Ghi chú chi tiết', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), - children: [ + children: const [ TextSpan( text: ' *', style: TextStyle(color: AppColors.danger), @@ -652,22 +655,22 @@ class DesignRequestCreatePage extends HookConsumerWidget { 'Mô tả chi tiết về yêu cầu thiết kế, số phòng, công năng sử dụng, sở thích cá nhân...', border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.grey100, + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, + borderSide: BorderSide( + color: colorScheme.primary, width: 2, ), ), @@ -706,16 +709,16 @@ class DesignRequestCreatePage extends HookConsumerWidget { children: [ Icon( Icons.cloud_upload_outlined, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 20, ), const SizedBox(width: 8), - const Text( + Text( 'Đính kèm tài liệu', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -730,34 +733,34 @@ class DesignRequestCreatePage extends HookConsumerWidget { padding: const EdgeInsets.all(24), decoration: BoxDecoration( border: Border.all( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, width: 2, style: BorderStyle.solid, ), borderRadius: BorderRadius.circular(8), - color: AppColors.grey50, + color: colorScheme.surfaceContainerLowest, ), child: Column( children: [ Icon( Icons.cloud_upload_outlined, size: 32, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), const SizedBox(height: 12), - const Text( + Text( 'Nhấn để chọn file hoặc kéo thả vào đây', style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), - const Text( + Text( 'Hỗ trợ: JPG, PNG, PDF (Tối đa 10MB mỗi file)', style: TextStyle( fontSize: 12, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -771,7 +774,7 @@ class DesignRequestCreatePage extends HookConsumerWidget { ...selectedFiles.value.asMap().entries.map((entry) { final index = entry.key; final file = entry.value; - return _FilePreviewItem( + return FilePreviewItem( file: file, onRemove: () => removeFile(index), ); @@ -790,10 +793,10 @@ class DesignRequestCreatePage extends HookConsumerWidget { child: ElevatedButton( onPressed: isSubmitting.value ? null : submitForm, style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: AppColors.white, - disabledBackgroundColor: AppColors.primaryBlue.withValues(alpha: 0.7), - disabledForegroundColor: AppColors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.surface, + disabledBackgroundColor: colorScheme.primary.withValues(alpha: 0.7), + disabledForegroundColor: colorScheme.surface, padding: const EdgeInsets.symmetric(vertical: 14), elevation: 0, shape: RoundedRectangleBorder( @@ -804,12 +807,12 @@ class DesignRequestCreatePage extends HookConsumerWidget { ? Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox( + SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, - color: AppColors.white, + color: colorScheme.surface, ), ), const SizedBox(width: 12), @@ -850,167 +853,3 @@ class DesignRequestCreatePage extends HookConsumerWidget { } } -/// Progress Step Widget - -/// Form Field Widget -class _FormField extends StatelessWidget { - final String label; - final bool required; - final TextEditingController controller; - final String hint; - final TextInputType? keyboardType; - final String? Function(String?)? validator; - - const _FormField({ - required this.label, - this.required = false, - required this.controller, - required this.hint, - this.keyboardType, - this.validator, - }); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - text: TextSpan( - text: label, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: AppColors.grey900, - ), - children: required - ? const [ - TextSpan( - text: ' *', - style: TextStyle(color: AppColors.danger), - ), - ] - : null, - ), - ), - const SizedBox(height: 8), - TextFormField( - controller: controller, - keyboardType: keyboardType, - decoration: InputDecoration( - hintText: hint, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100, width: 2), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.grey100, width: 2), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide( - color: AppColors.primaryBlue, - width: 2, - ), - ), - errorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.danger, width: 2), - ), - focusedErrorBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - borderSide: const BorderSide(color: AppColors.danger, width: 2), - ), - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), - ), - validator: validator, - ), - ], - ); - } -} - -/// File Preview Item Widget -class _FilePreviewItem extends StatelessWidget { - final PlatformFile file; - final VoidCallback onRemove; - - const _FilePreviewItem({required this.file, required this.onRemove}); - - IconData _getFileIcon() { - final extension = file.extension?.toLowerCase(); - if (extension == 'pdf') return Icons.picture_as_pdf; - if (extension == 'jpg' || extension == 'jpeg' || extension == 'png') { - return Icons.image; - } - return Icons.insert_drive_file; - } - - String _formatFileSize(int bytes) { - if (bytes < 1024) return '$bytes B'; - if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; - return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; - } - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(bottom: 8), - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: AppColors.grey100, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - children: [ - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: AppColors.primaryBlue, - borderRadius: BorderRadius.circular(6), - ), - child: Icon(_getFileIcon(), color: AppColors.white, size: 20), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - file.name, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: AppColors.grey900, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 4), - Text( - _formatFileSize(file.size), - style: const TextStyle( - fontSize: 12, - color: AppColors.grey500, - ), - ), - ], - ), - ), - IconButton( - icon: const Icon(Icons.close, size: 20), - color: AppColors.danger, - onPressed: onRemove, - padding: EdgeInsets.zero, - constraints: const BoxConstraints(minWidth: 24, minHeight: 24), - ), - ], - ), - ); - } -} diff --git a/lib/features/showrooms/presentation/pages/design_request_detail_page.dart b/lib/features/showrooms/presentation/pages/design_request_detail_page.dart index 0155770..a6b0e4a 100644 --- a/lib/features/showrooms/presentation/pages/design_request_detail_page.dart +++ b/lib/features/showrooms/presentation/pages/design_request_detail_page.dart @@ -15,6 +15,9 @@ import 'package:worker/core/theme/colors.dart'; import 'package:worker/features/showrooms/domain/entities/design_request.dart'; import 'package:worker/features/showrooms/domain/entities/sample_project.dart'; import 'package:worker/features/showrooms/presentation/providers/design_request_provider.dart'; +import 'package:worker/features/showrooms/presentation/widgets/description_item.dart'; +import 'package:worker/features/showrooms/presentation/widgets/file_item.dart'; +import 'package:worker/features/showrooms/presentation/widgets/image_viewer_dialog.dart'; /// Design Request Detail Page /// @@ -161,7 +164,7 @@ class DesignRequestDetailPage extends ConsumerWidget { showDialog( context: context, barrierColor: Colors.black87, - builder: (context) => _ImageViewerDialog( + builder: (context) => ImageViewerDialog( images: images, initialIndex: initialIndex, ), @@ -171,21 +174,22 @@ class DesignRequestDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final detailAsync = ref.watch(designRequestDetailProvider(requestId)); + final colorScheme = Theme.of(context).colorScheme; return Scaffold( - backgroundColor: AppColors.grey50, + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), + icon: Icon(Icons.arrow_back, color: colorScheme.onSurface), onPressed: () => Navigator.of(context).pop(), ), centerTitle: false, - title: const Text( + title: Text( 'Chi tiết Yêu cầu', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 20, fontWeight: FontWeight.w600, ), @@ -193,7 +197,7 @@ class DesignRequestDetailPage extends ConsumerWidget { actions: [ detailAsync.maybeWhen( data: (request) => IconButton( - icon: const Icon(Icons.share, color: Colors.black), + icon: Icon(Icons.share, color: colorScheme.onSurface), onPressed: () => _shareRequest(context, request), ), orElse: () => const SizedBox.shrink(), @@ -226,10 +230,10 @@ class DesignRequestDetailPage extends ConsumerWidget { // Request ID Text( '#${request.id}', - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), const SizedBox(height: 8), @@ -238,9 +242,9 @@ class DesignRequestDetailPage extends ConsumerWidget { if (request.dateline != null) Text( 'Ngày gửi: ${request.dateline}', - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), @@ -269,7 +273,7 @@ class DesignRequestDetailPage extends ConsumerWidget { ), const SizedBox(height: 24), - const Divider(height: 1, color: AppColors.grey100), + Divider(height: 1, color: colorScheme.surfaceContainerHighest), const SizedBox(height: 24), // Design Information Section @@ -277,16 +281,16 @@ class DesignRequestDetailPage extends ConsumerWidget { children: [ Icon( Icons.info_outline, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 20, ), const SizedBox(width: 8), - const Text( + Text( 'Thông tin thiết kế', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -295,14 +299,14 @@ class DesignRequestDetailPage extends ConsumerWidget { const SizedBox(height: 16), // Description List - _DescriptionItem( + DescriptionItem( label: 'Tên công trình:', value: request.subject, ), if (request.plainDescription.isNotEmpty) ...[ const SizedBox(height: 12), - _DescriptionItem( + DescriptionItem( label: 'Mô tả chi tiết:', value: request.plainDescription, isMultiLine: true, @@ -439,16 +443,16 @@ class DesignRequestDetailPage extends ConsumerWidget { children: [ Icon( Icons.attach_file, - color: AppColors.primaryBlue, + color: colorScheme.primary, size: 20, ), const SizedBox(width: 8), - const Text( + Text( 'Tài liệu đính kèm', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -468,8 +472,8 @@ class DesignRequestDetailPage extends ConsumerWidget { child: ElevatedButton.icon( onPressed: () => _contactSupport(context), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryBlue, - foregroundColor: Colors.white, + backgroundColor: colorScheme.primary, + foregroundColor: colorScheme.onPrimary, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), @@ -505,9 +509,9 @@ class DesignRequestDetailPage extends ConsumerWidget { Text( 'Lỗi tải dữ liệu: ${error.toString().replaceAll('Exception: ', '')}', textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), @@ -524,6 +528,7 @@ class DesignRequestDetailPage extends ConsumerWidget { } Widget _buildFilesSection(BuildContext context, List files) { + final colorScheme = Theme.of(context).colorScheme; // Separate images and other files final images = files.where((f) => _isImageFile(f.fileUrl)).toList(); final otherFiles = files.where((f) => !_isImageFile(f.fileUrl)).toList(); @@ -553,13 +558,13 @@ class DesignRequestDetailPage extends ConsumerWidget { imageUrl: image.fileUrl, fit: BoxFit.cover, placeholder: (context, url) => Container( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), ), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Icon(Icons.error), ), ), @@ -575,7 +580,7 @@ class DesignRequestDetailPage extends ConsumerWidget { // Other Files as file items ...otherFiles.map( - (file) => _FileItem( + (file) => FileItem( fileUrl: file.fileUrl, icon: _getFileIcon(file.fileUrl), ), @@ -584,7 +589,7 @@ class DesignRequestDetailPage extends ConsumerWidget { // Show image files as file items too (for download/naming) if (images.isNotEmpty && otherFiles.isEmpty) ...images.map( - (file) => _FileItem( + (file) => FileItem( fileUrl: file.fileUrl, icon: Icons.image, ), @@ -593,246 +598,3 @@ class DesignRequestDetailPage extends ConsumerWidget { ); } } - -/// Description Item Widget -class _DescriptionItem extends StatelessWidget { - const _DescriptionItem({ - required this.label, - required this.value, - this.isMultiLine = false, - }); - - final String label; - final String value; - final bool isMultiLine; - - @override - Widget build(BuildContext context) { - if (isMultiLine) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: const TextStyle( - fontSize: 13, - color: AppColors.grey500, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 4), - Text( - value, - style: const TextStyle( - fontSize: 15, - color: AppColors.grey900, - fontWeight: FontWeight.w500, - height: 1.6, - ), - ), - ], - ); - } - - return Container( - padding: const EdgeInsets.only(bottom: 12), - decoration: const BoxDecoration( - border: Border(bottom: BorderSide(color: AppColors.grey100)), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 120, - child: Text( - label, - style: const TextStyle( - fontSize: 13, - color: AppColors.grey500, - fontWeight: FontWeight.w500, - ), - ), - ), - Expanded( - child: Text( - value, - style: const TextStyle( - fontSize: 15, - color: AppColors.grey900, - fontWeight: FontWeight.w500, - height: 1.6, - ), - ), - ), - ], - ), - ); - } -} - -/// File Item Widget -class _FileItem extends StatelessWidget { - const _FileItem({required this.fileUrl, required this.icon}); - - final String fileUrl; - final IconData icon; - - String get fileName { - final uri = Uri.parse(fileUrl); - return uri.pathSegments.isNotEmpty ? uri.pathSegments.last : fileUrl; - } - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(bottom: 8), - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: AppColors.grey100, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - children: [ - Container( - width: 32, - height: 32, - decoration: BoxDecoration( - color: AppColors.primaryBlue, - borderRadius: BorderRadius.circular(6), - ), - child: Icon(icon, color: Colors.white, size: 14), - ), - const SizedBox(width: 12), - Expanded( - child: Text( - fileName, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: AppColors.grey900, - ), - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ); - } -} - -/// Image Viewer Dialog with Swipe Navigation -class _ImageViewerDialog extends StatefulWidget { - const _ImageViewerDialog({ - required this.images, - required this.initialIndex, - }); - - final List images; - final int initialIndex; - - @override - State<_ImageViewerDialog> createState() => _ImageViewerDialogState(); -} - -class _ImageViewerDialogState extends State<_ImageViewerDialog> { - late PageController _pageController; - late int _currentIndex; - - @override - void initState() { - super.initState(); - _currentIndex = widget.initialIndex; - _pageController = PageController(initialPage: widget.initialIndex); - } - - @override - void dispose() { - _pageController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Dialog( - backgroundColor: Colors.transparent, - insetPadding: EdgeInsets.zero, - child: Container( - color: Colors.black, - child: Stack( - children: [ - // Main PageView - Center( - child: PageView.builder( - controller: _pageController, - onPageChanged: (index) { - setState(() { - _currentIndex = index; - }); - }, - itemCount: widget.images.length, - itemBuilder: (context, index) { - return Center( - child: CachedNetworkImage( - imageUrl: widget.images[index].fileUrl, - fit: BoxFit.contain, - placeholder: (context, url) => const Center( - child: CircularProgressIndicator(color: Colors.white), - ), - errorWidget: (context, url, error) => const Icon( - Icons.error, - color: Colors.white, - size: 48, - ), - ), - ); - }, - ), - ), - - // Top bar with counter and close button - Positioned( - top: 0, - left: 0, - right: 0, - child: SafeArea( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 12, - ), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.black.withValues(alpha: 0.7), - Colors.transparent, - ], - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${_currentIndex + 1} / ${widget.images.length}', - style: const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - IconButton( - icon: const Icon(Icons.close, color: Colors.white), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/features/showrooms/presentation/pages/model_house_detail_page.dart b/lib/features/showrooms/presentation/pages/model_house_detail_page.dart index f7be84a..343eb42 100644 --- a/lib/features/showrooms/presentation/pages/model_house_detail_page.dart +++ b/lib/features/showrooms/presentation/pages/model_house_detail_page.dart @@ -26,29 +26,30 @@ class ModelHouseDetailPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final detailAsync = ref.watch(sampleProjectDetailProvider(modelId)); return Scaffold( - backgroundColor: const Color(0xFFF4F6F8), + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( leading: IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.arrowLeft, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () => context.pop(), ), - title: const Text( + title: Text( 'Chi tiết Nhà mẫu', - style: TextStyle(color: Colors.black), + style: TextStyle(color: colorScheme.onSurface), ), actions: [ detailAsync.maybeWhen( data: (project) => IconButton( - icon: const FaIcon( + icon: FaIcon( FontAwesomeIcons.shareNodes, - color: Colors.black, + color: colorScheme.onSurface, size: 20, ), onPressed: () => _shareModel(context, project), @@ -58,7 +59,7 @@ class ModelHouseDetailPage extends ConsumerWidget { const SizedBox(width: AppSpacing.sm), ], elevation: AppBarSpecs.elevation, - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, centerTitle: false, ), body: detailAsync.when( @@ -72,7 +73,7 @@ class ModelHouseDetailPage extends ConsumerWidget { const SizedBox(height: 16), // Project Information - _buildProjectInfo(project), + _buildProjectInfo(context, project), const SizedBox(height: 16), @@ -102,9 +103,9 @@ class ModelHouseDetailPage extends ConsumerWidget { Text( 'Lỗi tải dữ liệu: ${error.toString().replaceAll('Exception: ', '')}', textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), @@ -299,12 +300,13 @@ class ModelHouseDetailPage extends ConsumerWidget { ); } - Widget _buildProjectInfo(SampleProject project) { + Widget _buildProjectInfo(BuildContext context, SampleProject project) { + final colorScheme = Theme.of(context).colorScheme; return Container( margin: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( @@ -320,10 +322,10 @@ class ModelHouseDetailPage extends ConsumerWidget { // Title Text( project.projectName, - style: const TextStyle( + style: TextStyle( fontSize: 24, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), if (project.plainDescription.isNotEmpty) ...[ @@ -331,9 +333,9 @@ class ModelHouseDetailPage extends ConsumerWidget { // Description Text( project.plainDescription, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Color(0xFF4b5563), + color: colorScheme.onSurfaceVariant, height: 1.6, ), ), @@ -344,13 +346,14 @@ class ModelHouseDetailPage extends ConsumerWidget { } Widget _buildImageGallery(BuildContext context, SampleProject project) { + final colorScheme = Theme.of(context).colorScheme; final images = project.filesList; return Container( margin: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.all(20), decoration: BoxDecoration( - color: AppColors.white, + color: colorScheme.surface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( @@ -366,18 +369,18 @@ class ModelHouseDetailPage extends ConsumerWidget { // Gallery Title Row( children: [ - const FaIcon( + FaIcon( FontAwesomeIcons.images, size: 18, - color: AppColors.grey900, + color: colorScheme.onSurface, ), const SizedBox(width: 8), Text( 'Thư viện Hình ảnh (${images.length})', - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), ], @@ -405,13 +408,13 @@ class ModelHouseDetailPage extends ConsumerWidget { imageUrl: image.fileUrl, fit: BoxFit.cover, placeholder: (context, url) => Container( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center( child: CircularProgressIndicator(strokeWidth: 2), ), ), errorWidget: (context, url, error) => Container( - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Icon(Icons.error), ), ), diff --git a/lib/features/showrooms/presentation/pages/model_houses_page.dart b/lib/features/showrooms/presentation/pages/model_houses_page.dart index 4536d1a..ea9a6d0 100644 --- a/lib/features/showrooms/presentation/pages/model_houses_page.dart +++ b/lib/features/showrooms/presentation/pages/model_houses_page.dart @@ -92,41 +92,43 @@ class _ModelHousesPageState extends ConsumerState @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Scaffold( - backgroundColor: AppColors.grey50, + backgroundColor: colorScheme.surfaceContainerLowest, appBar: AppBar( - backgroundColor: AppColors.white, + backgroundColor: colorScheme.surface, elevation: AppBarSpecs.elevation, leading: IconButton( - icon: const Icon(Icons.arrow_back, color: Colors.black), + icon: Icon(Icons.arrow_back, color: colorScheme.onSurface), onPressed: () => Navigator.of(context).pop(), ), centerTitle: false, - title: const Text( + title: Text( 'Nhà mẫu', style: TextStyle( - color: Colors.black, + color: colorScheme.onSurface, fontSize: 20, fontWeight: FontWeight.w600, ), ), actions: [ IconButton( - icon: const Icon(Icons.info_outline, color: Colors.black), + icon: Icon(Icons.info_outline, color: colorScheme.onSurface), onPressed: _showInfoDialog, ), const SizedBox(width: AppSpacing.sm), ], bottom: TabBar( controller: _tabController, - indicatorColor: AppColors.primaryBlue, + indicatorColor: colorScheme.primary, indicatorWeight: 3, - labelColor: AppColors.primaryBlue, + labelColor: colorScheme.primary, labelStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), - unselectedLabelColor: AppColors.grey500, + unselectedLabelColor: colorScheme.onSurfaceVariant, unselectedLabelStyle: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, @@ -148,11 +150,11 @@ class _ModelHousesPageState extends ConsumerState return _tabController.index == 1 ? FloatingActionButton( onPressed: _createNewRequest, - backgroundColor: AppColors.primaryBlue, + backgroundColor: colorScheme.primary, elevation: 4, - child: const Icon( + child: Icon( Icons.add, - color: AppColors.white, + color: colorScheme.onPrimary, size: 28, ), ) @@ -169,28 +171,29 @@ class _LibraryTab extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final sampleProjectsAsync = ref.watch(sampleProjectsListProvider); return sampleProjectsAsync.when( data: (projects) { if (projects.isEmpty) { - return const Center( + return Center( child: Padding( - padding: EdgeInsets.all(40), + padding: const EdgeInsets.all(40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.home_work_outlined, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: 16), + const SizedBox(height: 16), Text( 'Chưa có mẫu nhà nào', style: TextStyle( fontSize: 16, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -229,9 +232,9 @@ class _LibraryTab extends ConsumerWidget { Text( 'Lỗi tải dữ liệu: ${error.toString().replaceAll('Exception: ', '')}', textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), @@ -255,6 +258,8 @@ class _LibraryCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -282,26 +287,26 @@ class _LibraryCard extends StatelessWidget { fit: BoxFit.cover, placeholder: (context, url) => Container( height: 200, - color: AppColors.grey100, + color: colorScheme.surfaceContainerHighest, child: const Center(child: CircularProgressIndicator()), ), errorWidget: (context, url, error) => Container( height: 200, - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerHighest, + child: Icon( Icons.image_not_supported, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ) : Container( height: 200, - color: AppColors.grey100, - child: const Icon( + color: colorScheme.surfaceContainerHighest, + child: Icon( Icons.home_work, size: 48, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ), @@ -315,13 +320,13 @@ class _LibraryCard extends StatelessWidget { vertical: 6, ), decoration: BoxDecoration( - color: AppColors.primaryBlue.withValues(alpha: 0.9), + color: colorScheme.primary.withValues(alpha: 0.9), borderRadius: BorderRadius.circular(16), ), - child: const Text( + child: Text( 'Xem 360°', style: TextStyle( - color: AppColors.white, + color: colorScheme.onPrimary, fontSize: 12, fontWeight: FontWeight.w600, ), @@ -340,10 +345,10 @@ class _LibraryCard extends StatelessWidget { // Title Text( project.projectName, - style: const TextStyle( + style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), @@ -352,9 +357,9 @@ class _LibraryCard extends StatelessWidget { // Description Text( project.plainDescription, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, height: 1.5, ), maxLines: 3, @@ -377,28 +382,29 @@ class _DesignRequestsTab extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final colorScheme = Theme.of(context).colorScheme; final requestsAsync = ref.watch(designRequestsListProvider); return requestsAsync.when( data: (requests) { if (requests.isEmpty) { - return const Center( + return Center( child: Padding( - padding: EdgeInsets.all(40), + padding: const EdgeInsets.all(40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.design_services_outlined, size: 64, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), - SizedBox(height: 16), + const SizedBox(height: 16), Text( 'Chưa có yêu cầu thiết kế nào', style: TextStyle( fontSize: 16, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), ], @@ -437,9 +443,9 @@ class _DesignRequestsTab extends ConsumerWidget { Text( 'Lỗi tải dữ liệu: ${error.toString().replaceAll('Exception: ', '')}', textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: AppColors.grey500, + color: colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), @@ -489,6 +495,8 @@ class _RequestCard extends StatelessWidget { @override Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + return Card( elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), @@ -510,10 +518,10 @@ class _RequestCard extends StatelessWidget { Expanded( child: Text( 'Mã yêu cầu: #${request.id}', - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, - color: AppColors.grey900, + color: colorScheme.onSurface, ), overflow: TextOverflow.ellipsis, ), @@ -546,7 +554,7 @@ class _RequestCard extends StatelessWidget { if (request.dateline != null) Text( 'Deadline: ${request.dateline}', - style: const TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), ), const SizedBox(height: 8), @@ -554,10 +562,10 @@ class _RequestCard extends StatelessWidget { // Subject Text( request.subject, - style: const TextStyle( + style: TextStyle( fontSize: 15, fontWeight: FontWeight.w600, - color: AppColors.grey900, + color: colorScheme.onSurface, ), ), @@ -566,7 +574,7 @@ class _RequestCard extends StatelessWidget { // Description Text( request.plainDescription, - style: const TextStyle(fontSize: 14, color: AppColors.grey500), + style: TextStyle(fontSize: 14, color: colorScheme.onSurfaceVariant), maxLines: 2, overflow: TextOverflow.ellipsis, ), diff --git a/lib/features/showrooms/presentation/widgets/description_item.dart b/lib/features/showrooms/presentation/widgets/description_item.dart new file mode 100644 index 0000000..0022562 --- /dev/null +++ b/lib/features/showrooms/presentation/widgets/description_item.dart @@ -0,0 +1,92 @@ +/// Widget: Description Item +/// +/// Displays a label-value pair for design request details. +library; + +import 'package:flutter/material.dart'; + +/// Description Item Widget +/// +/// Shows a label and value pair with: +/// - Inline layout (label: value) for single line +/// - Stacked layout for multi-line values +/// - Theme-aware colors +class DescriptionItem extends StatelessWidget { + const DescriptionItem({ + super.key, + required this.label, + required this.value, + this.isMultiLine = false, + }); + + final String label; + final String value; + final bool isMultiLine; + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + if (isMultiLine) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label, + style: TextStyle( + fontSize: 13, + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 4), + Text( + value, + style: TextStyle( + fontSize: 15, + color: colorScheme.onSurface, + fontWeight: FontWeight.w500, + height: 1.6, + ), + ), + ], + ); + } + + return Container( + padding: const EdgeInsets.only(bottom: 12), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(color: colorScheme.surfaceContainerHighest), + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 120, + child: Text( + label, + style: TextStyle( + fontSize: 13, + color: colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), + ), + ), + Expanded( + child: Text( + value, + style: TextStyle( + fontSize: 15, + color: colorScheme.onSurface, + fontWeight: FontWeight.w500, + height: 1.6, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/showrooms/presentation/widgets/file_item.dart b/lib/features/showrooms/presentation/widgets/file_item.dart new file mode 100644 index 0000000..bd73f9a --- /dev/null +++ b/lib/features/showrooms/presentation/widgets/file_item.dart @@ -0,0 +1,67 @@ +/// Widget: File Item +/// +/// Displays a file attachment item with icon and name. +library; + +import 'package:flutter/material.dart'; + +/// File Item Widget +/// +/// Shows a file attachment with: +/// - File type icon +/// - File name extracted from URL +/// - Theme-aware styling +class FileItem extends StatelessWidget { + const FileItem({ + super.key, + required this.fileUrl, + required this.icon, + }); + + final String fileUrl; + final IconData icon; + + String get fileName { + final uri = Uri.parse(fileUrl); + return uri.pathSegments.isNotEmpty ? uri.pathSegments.last : fileUrl; + } + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Container( + width: 32, + height: 32, + decoration: BoxDecoration( + color: colorScheme.primary, + borderRadius: BorderRadius.circular(6), + ), + child: Icon(icon, color: colorScheme.onPrimary, size: 14), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + fileName, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/showrooms/presentation/widgets/file_preview_item.dart b/lib/features/showrooms/presentation/widgets/file_preview_item.dart new file mode 100644 index 0000000..60a31c6 --- /dev/null +++ b/lib/features/showrooms/presentation/widgets/file_preview_item.dart @@ -0,0 +1,101 @@ +/// Widget: File Preview Item +/// +/// Displays a file preview with icon, name, size, and remove button. +library; + +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:worker/core/theme/colors.dart'; + +/// File Preview Item Widget +/// +/// Shows uploaded file with: +/// - File type icon (PDF, image, etc.) +/// - File name +/// - File size +/// - Remove button +class FilePreviewItem extends StatelessWidget { + const FilePreviewItem({ + super.key, + required this.file, + required this.onRemove, + }); + + final PlatformFile file; + final VoidCallback onRemove; + + IconData _getFileIcon() { + final extension = file.extension?.toLowerCase(); + if (extension == 'pdf') return Icons.picture_as_pdf; + if (extension == 'jpg' || extension == 'jpeg' || extension == 'png') { + return Icons.image; + } + return Icons.insert_drive_file; + } + + String _formatFileSize(int bytes) { + if (bytes < 1024) return '$bytes B'; + if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; + return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; + } + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: colorScheme.primary, + borderRadius: BorderRadius.circular(6), + ), + child: Icon(_getFileIcon(), color: colorScheme.surface, size: 20), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + file.name, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Text( + _formatFileSize(file.size), + style: TextStyle( + fontSize: 12, + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + IconButton( + icon: const Icon(Icons.close, size: 20), + color: AppColors.danger, + onPressed: onRemove, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(minWidth: 24, minHeight: 24), + ), + ], + ), + ); + } +} diff --git a/lib/features/showrooms/presentation/widgets/form_field_widget.dart b/lib/features/showrooms/presentation/widgets/form_field_widget.dart new file mode 100644 index 0000000..4b17e2b --- /dev/null +++ b/lib/features/showrooms/presentation/widgets/form_field_widget.dart @@ -0,0 +1,104 @@ +/// Widget: Form Field +/// +/// Reusable form field with label, validation, and theming support. +library; + +import 'package:flutter/material.dart'; +import 'package:worker/core/theme/colors.dart'; + +/// Form Field Widget +/// +/// A styled text form field with: +/// - Label with optional required indicator +/// - Hint text +/// - Theme-aware borders +/// - Validation support +class FormFieldWidget extends StatelessWidget { + const FormFieldWidget({ + super.key, + required this.label, + this.required = false, + required this.controller, + required this.hint, + this.keyboardType, + this.validator, + }); + + final String label; + final bool required; + final TextEditingController controller; + final String hint; + final TextInputType? keyboardType; + final String? Function(String?)? validator; + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + text: TextSpan( + text: label, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: colorScheme.onSurface, + ), + children: required + ? const [ + TextSpan( + text: ' *', + style: TextStyle(color: AppColors.danger), + ), + ] + : null, + ), + ), + const SizedBox(height: 8), + TextFormField( + controller: controller, + keyboardType: keyboardType, + decoration: InputDecoration( + hintText: hint, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, + width: 2, + ), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.surfaceContainerHighest, + width: 2, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide( + color: colorScheme.primary, + width: 2, + ), + ), + errorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: AppColors.danger, width: 2), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: const BorderSide(color: AppColors.danger, width: 2), + ), + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + ), + validator: validator, + ), + ], + ); + } +} diff --git a/lib/features/showrooms/presentation/widgets/image_viewer_dialog.dart b/lib/features/showrooms/presentation/widgets/image_viewer_dialog.dart new file mode 100644 index 0000000..84a9566 --- /dev/null +++ b/lib/features/showrooms/presentation/widgets/image_viewer_dialog.dart @@ -0,0 +1,132 @@ +/// Widget: Image Viewer Dialog +/// +/// Full-screen image viewer with swipe navigation. +library; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:worker/features/showrooms/domain/entities/sample_project.dart'; + +/// Image Viewer Dialog +/// +/// Full-screen dialog for viewing images with: +/// - PageView for swipe navigation +/// - Image counter (1/5) +/// - Close button +/// - Gradient overlay for visibility +class ImageViewerDialog extends StatefulWidget { + const ImageViewerDialog({ + super.key, + required this.images, + required this.initialIndex, + }); + + final List images; + final int initialIndex; + + @override + State createState() => _ImageViewerDialogState(); +} + +class _ImageViewerDialogState extends State { + late PageController _pageController; + late int _currentIndex; + + @override + void initState() { + super.initState(); + _currentIndex = widget.initialIndex; + _pageController = PageController(initialPage: widget.initialIndex); + } + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.transparent, + insetPadding: EdgeInsets.zero, + child: Container( + color: Colors.black, + child: Stack( + children: [ + // Main PageView + Center( + child: PageView.builder( + controller: _pageController, + onPageChanged: (index) { + setState(() { + _currentIndex = index; + }); + }, + itemCount: widget.images.length, + itemBuilder: (context, index) { + return Center( + child: CachedNetworkImage( + imageUrl: widget.images[index].fileUrl, + fit: BoxFit.contain, + placeholder: (context, url) => const Center( + child: CircularProgressIndicator(color: Colors.white), + ), + errorWidget: (context, url, error) => const Icon( + Icons.error, + color: Colors.white, + size: 48, + ), + ), + ); + }, + ), + ), + + // Top bar with counter and close button + Positioned( + top: 0, + left: 0, + right: 0, + child: SafeArea( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withValues(alpha: 0.7), + Colors.transparent, + ], + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${_currentIndex + 1} / ${widget.images.length}', + style: const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () => Navigator.pop(context), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } +}