Files
worker/lib/features/account/presentation/pages/addresses_page.dart
Phuoc Nguyen 0dda402246 update address
2025-11-18 17:04:00 +07:00

362 lines
12 KiB
Dart

/// Addresses Page
///
/// Displays list of saved addresses with management options.
/// Features:
/// - List of saved addresses
/// - Default address indicator
/// - Edit/delete actions
/// - Set as default functionality
/// - Add new address
library;
import 'package:flutter/material.dart';
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/router/app_router.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/account/presentation/widgets/address_card.dart';
/// Addresses Page
///
/// Page for managing saved delivery addresses.
class AddressesPage extends ConsumerWidget {
const AddressesPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// Watch addresses from API
final addressesAsync = ref.watch(addressesProvider);
return Scaffold(
backgroundColor: const Color(0xFFF4F6F8),
appBar: AppBar(
backgroundColor: AppColors.white,
elevation: AppBarSpecs.elevation,
leading: IconButton(
icon: const FaIcon(FontAwesomeIcons.arrowLeft, color: Colors.black, size: 20),
onPressed: () => context.pop(),
),
title: const Text(
'Địa chỉ của bạn',
style: TextStyle(color: Colors.black),
),
foregroundColor: AppColors.grey900,
centerTitle: false,
actions: [
IconButton(
icon: const FaIcon(FontAwesomeIcons.circleInfo, color: Colors.black, size: 20),
onPressed: () {
_showInfoDialog(context);
},
),
const SizedBox(width: AppSpacing.sm),
],
),
body: addressesAsync.when(
data: (addresses) => Column(
children: [
// Address List
Expanded(
child: RefreshIndicator(
onRefresh: () async {
await ref.read(addressesProvider.notifier).refresh();
},
child: addresses.isEmpty
? _buildEmptyState(context)
: ListView.separated(
padding: const EdgeInsets.all(AppSpacing.md),
itemCount: addresses.length,
separatorBuilder: (context, index) =>
const SizedBox(height: AppSpacing.md),
itemBuilder: (context, index) {
final address = addresses[index];
return AddressCard(
name: address.addressTitle,
phone: address.phone,
address: address.fullAddress,
isDefault: address.isDefault,
onEdit: () {
context.push(
RouteNames.addressForm,
extra: address,
);
},
onDelete: () {
_showDeleteConfirmation(context, ref, address);
},
onSetDefault: () {
_setDefaultAddress(context, ref, address);
},
);
},
),
),
),
// Add New Address Button
Padding(
padding: const EdgeInsets.all(AppSpacing.md),
child: SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () {
context.push(RouteNames.addressForm);
},
icon: const FaIcon(FontAwesomeIcons.plus, size: 18),
label: const Text(
'Thêm địa chỉ mới',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.button),
),
),
),
),
),
],
),
loading: () => const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const FaIcon(
FontAwesomeIcons.triangleExclamation,
size: 64,
color: AppColors.danger,
),
const SizedBox(height: 16),
const Text(
'Không thể tải danh sách địa chỉ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.grey900,
),
),
const SizedBox(height: 8),
Text(
error.toString(),
style: const TextStyle(fontSize: 14, color: AppColors.grey500),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
ref.read(addressesProvider.notifier).refresh();
},
icon: const FaIcon(FontAwesomeIcons.arrowsRotate, size: 18),
label: const Text('Thử lại'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
),
),
],
),
),
),
);
}
/// Build empty state
Widget _buildEmptyState(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FaIcon(
FontAwesomeIcons.locationDot,
size: 64,
color: AppColors.grey500.withValues(alpha: 0.4),
),
const SizedBox(height: 16),
const Text(
'Chưa có địa chỉ nào',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.grey900,
),
),
const SizedBox(height: 8),
Text(
'Thêm địa chỉ để nhận hàng nhanh hơn',
style: TextStyle(
fontSize: 14,
color: AppColors.grey500.withValues(alpha: 0.8),
),
),
const SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
context.push(RouteNames.addressForm);
},
icon: const FaIcon(FontAwesomeIcons.plus, size: 18),
label: const Text(
'Thêm địa chỉ mới',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryBlue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(AppRadius.button),
),
),
),
],
),
);
}
/// Set address as default
void _setDefaultAddress(BuildContext context, WidgetRef ref, Address address) {
ref.read(addressesProvider.notifier).setDefaultAddress(address.name);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const FaIcon(
FontAwesomeIcons.circleCheck,
color: Colors.white,
size: 18,
),
const SizedBox(width: 12),
const Text('Đã đặt làm địa chỉ mặc định'),
],
),
backgroundColor: const Color(0xFF10B981),
duration: const Duration(seconds: 2),
),
);
}
/// Show info dialog
void _showInfoDialog(BuildContext context) {
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text(
'Hướng dẫn sử dụng',
style: TextStyle(fontWeight: FontWeight.bold),
),
content: const SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Quản lý địa chỉ giao hàng của bạn:'),
SizedBox(height: 12),
Text('• Thêm địa chỉ mới để dễ dàng đặt hàng'),
Text('• Đặt địa chỉ mặc định cho đơn hàng'),
Text('• Chỉnh sửa hoặc xóa địa chỉ bất kỳ'),
Text('• Lưu nhiều địa chỉ cho các mục đích khác nhau'),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Đóng'),
),
],
),
);
}
/// Show delete confirmation dialog
void _showDeleteConfirmation(
BuildContext context,
WidgetRef ref,
Address address,
) {
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Xóa địa chỉ'),
content: const Text('Bạn có chắc chắn muốn xóa địa chỉ này?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Hủy'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
_deleteAddress(context, ref, address);
},
style: TextButton.styleFrom(foregroundColor: AppColors.danger),
child: const Text('Xóa'),
),
],
),
);
}
/// Delete address
void _deleteAddress(
BuildContext context,
WidgetRef ref,
Address address,
) async {
try {
await ref.read(addressesProvider.notifier).deleteAddress(address.name);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const FaIcon(
FontAwesomeIcons.circleCheck,
color: Colors.white,
size: 18,
),
const SizedBox(width: 12),
const Text('Đã xóa địa chỉ'),
],
),
backgroundColor: const Color(0xFF10B981),
duration: const Duration(seconds: 2),
),
);
}
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Row(
children: [
const FaIcon(
FontAwesomeIcons.circleExclamation,
color: Colors.white,
size: 18,
),
const SizedBox(width: 12),
Text('Lỗi: ${e.toString()}'),
],
),
backgroundColor: AppColors.danger,
duration: const Duration(seconds: 3),
),
);
}
}
}
}