This commit is contained in:
Phuoc Nguyen
2025-10-10 16:38:07 +07:00
parent e5b247d622
commit b94c158004
177 changed files with 25080 additions and 152 deletions

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import '../../domain/entities/category.dart';
/// Category card widget
class CategoryCard extends StatelessWidget {
final Category category;
const CategoryCard({
super.key,
required this.category,
});
@override
Widget build(BuildContext context) {
final color = category.color != null
? Color(int.parse(category.color!.substring(1), radix: 16) + 0xFF000000)
: Theme.of(context).colorScheme.primaryContainer;
return Card(
color: color,
child: InkWell(
onTap: () {
// TODO: Filter products by category
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.category,
size: 48,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
const SizedBox(height: 8),
Text(
category.name,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
'${category.productCount} products',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/categories_provider.dart';
import '../../domain/entities/category.dart';
import 'category_card.dart';
import '../../../../core/widgets/loading_indicator.dart';
import '../../../../core/widgets/error_widget.dart';
import '../../../../core/widgets/empty_state.dart';
/// Category grid widget
class CategoryGrid extends ConsumerWidget {
final void Function(Category)? onCategoryTap;
const CategoryGrid({
super.key,
this.onCategoryTap,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final categoriesAsync = ref.watch(categoriesProvider);
return categoriesAsync.when(
loading: () => const LoadingIndicator(message: 'Loading categories...'),
error: (error, stack) => ErrorDisplay(
message: error.toString(),
onRetry: () => ref.refresh(categoriesProvider),
),
data: (categories) {
if (categories.isEmpty) {
return const EmptyState(
message: 'No categories found',
subMessage: 'Categories will appear here once added',
icon: Icons.category_outlined,
);
}
return LayoutBuilder(
builder: (context, constraints) {
// Determine grid columns based on width
int crossAxisCount = 2;
if (constraints.maxWidth > 1200) {
crossAxisCount = 4;
} else if (constraints.maxWidth > 800) {
crossAxisCount = 3;
}
return GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
childAspectRatio: 1.2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: categories.length,
itemBuilder: (context, index) {
final category = categories[index];
return RepaintBoundary(
child: GestureDetector(
onTap: () => onCategoryTap?.call(category),
child: CategoryCard(category: category),
),
);
},
);
},
);
},
);
}
}

View File

@@ -0,0 +1,5 @@
// Category Feature Widgets
export 'category_card.dart';
export 'category_grid.dart';
// This file provides a central export point for all category widgets