This commit is contained in:
Phuoc Nguyen
2025-10-23 17:03:58 +07:00
parent 30c245b401
commit 9189b65ebf
22 changed files with 589 additions and 195 deletions

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../domain/entities/product.dart';
import '../../data/models/product_model.dart';
import '../providers/products_provider.dart';
@@ -139,7 +140,7 @@ class _BatchUpdatePageState extends ConsumerState<BatchUpdatePage> {
children: [
Expanded(
child: OutlinedButton(
onPressed: _isLoading ? null : () => Navigator.pop(context),
onPressed: _isLoading ? null : () => context.pop(),
child: const Text('Cancel'),
),
),
@@ -327,7 +328,7 @@ class _BatchUpdatePageState extends ConsumerState<BatchUpdatePage> {
ref.invalidate(productsProvider);
if (mounted) {
Navigator.pop(context);
context.pop();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(

View File

@@ -3,33 +3,66 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:intl/intl.dart';
import '../../domain/entities/product.dart';
import '../providers/products_provider.dart';
import '../../../categories/presentation/providers/categories_provider.dart';
import '../../../../shared/widgets/price_display.dart';
/// Product detail page showing full product information
class ProductDetailPage extends ConsumerWidget {
final Product product;
final String productId;
const ProductDetailPage({
super.key,
required this.product,
required this.productId,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final productsAsync = ref.watch(productsProvider);
final categoriesAsync = ref.watch(categoriesProvider);
// Find category name
final categoryName = categoriesAsync.whenOrNull(
data: (categories) {
final category = categories.firstWhere(
(cat) => cat.id == product.categoryId,
orElse: () => categories.first,
);
return category.name;
},
);
return productsAsync.when(
data: (products) {
// Find the product by ID
Product? product;
try {
product = products.firstWhere((p) => p.id == productId);
} catch (e) {
// Product not found
return Scaffold(
appBar: AppBar(title: const Text('Product Not Found')),
body: const Center(child: Text('Product not found')),
);
}
// Find category name
final categoryName = categoriesAsync.whenOrNull(
data: (categories) {
try {
final category = categories.firstWhere(
(cat) => cat.id == product!.categoryId,
);
return category.name;
} catch (e) {
return null;
}
},
);
return _buildProductDetail(context, product, categoryName);
},
loading: () => Scaffold(
appBar: AppBar(title: const Text('Product Details')),
body: const Center(child: CircularProgressIndicator()),
),
error: (error, stack) => Scaffold(
appBar: AppBar(title: const Text('Error')),
body: Center(child: Text('Error: $error')),
),
);
}
Widget _buildProductDetail(BuildContext context, Product product, String? categoryName) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Details'),
@@ -48,7 +81,7 @@ class ProductDetailPage extends ConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Product Image
_buildProductImage(context),
_buildProductImage(context, product),
// Product Info Section
Padding(
@@ -97,19 +130,19 @@ class ProductDetailPage extends ConsumerWidget {
const SizedBox(height: 24),
// Stock Information
_buildStockSection(context),
_buildStockSection(context, product),
const SizedBox(height: 24),
// Description Section
_buildDescriptionSection(context),
_buildDescriptionSection(context, product),
const SizedBox(height: 24),
// Additional Information
_buildAdditionalInfo(context),
_buildAdditionalInfo(context, product),
const SizedBox(height: 24),
// Action Buttons
_buildActionButtons(context),
_buildActionButtons(context, product),
],
),
),
@@ -120,7 +153,7 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Build product image section
Widget _buildProductImage(BuildContext context) {
Widget _buildProductImage(BuildContext context, Product product) {
return Hero(
tag: 'product-${product.id}',
child: Container(
@@ -154,9 +187,9 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Build stock information section
Widget _buildStockSection(BuildContext context) {
final stockColor = _getStockColor(context);
final stockStatus = _getStockStatus();
Widget _buildStockSection(BuildContext context, Product product) {
final stockColor = _getStockColor(context, product);
final stockStatus = _getStockStatus(product);
return Card(
child: Padding(
@@ -245,7 +278,7 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Build description section
Widget _buildDescriptionSection(BuildContext context) {
Widget _buildDescriptionSection(BuildContext context, Product product) {
if (product.description == null || product.description!.isEmpty) {
return const SizedBox.shrink();
}
@@ -269,7 +302,7 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Build additional information section
Widget _buildAdditionalInfo(BuildContext context) {
Widget _buildAdditionalInfo(BuildContext context, Product product) {
final dateFormat = DateFormat('MMM dd, yyyy');
return Card(
@@ -355,7 +388,7 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Build action buttons
Widget _buildActionButtons(BuildContext context) {
Widget _buildActionButtons(BuildContext context, Product product) {
return Column(
children: [
// Add to Cart Button
@@ -400,7 +433,7 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Get stock color based on quantity
Color _getStockColor(BuildContext context) {
Color _getStockColor(BuildContext context, Product product) {
if (product.stockQuantity == 0) {
return Colors.red;
} else if (product.stockQuantity < 5) {
@@ -411,7 +444,7 @@ class ProductDetailPage extends ConsumerWidget {
}
/// Get stock status text
String _getStockStatus() {
String _getStockStatus(Product product) {
if (product.stockQuantity == 0) {
return 'Out of Stock';
} else if (product.stockQuantity < 5) {

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../widgets/product_grid.dart';
import '../widgets/product_search_bar.dart';
import '../widgets/product_list_item.dart';
@@ -99,13 +100,9 @@ class _ProductsPageState extends ConsumerState<ProductsPage> {
final selectedProducts = filteredProducts
.where((p) => _selectedProductIds.contains(p.id))
.toList();
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BatchUpdatePage(
selectedProducts: selectedProducts,
),
),
context.push(
'/products/batch-update',
extra: selectedProducts,
).then((_) {
setState(() {
_isSelectionMode = false;