328 lines
8.0 KiB
Dart
Raw Normal View History

2025-08-13 16:11:04 +07:00
import 'dart:math' as math;
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:line_icons/line_icons.dart';
import '../../../common/theme/theme.dart';
import '../../components/button/button.dart';
import 'widgets/appbar.dart';
import 'widgets/category_delegate.dart';
import 'widgets/product_tile.dart';
@RoutePage()
class ProductPage extends StatefulWidget {
const ProductPage({super.key});
@override
State<ProductPage> createState() => _ProductPageState();
}
class _ProductPageState extends State<ProductPage>
with TickerProviderStateMixin {
String selectedCategory = 'Semua';
List<String> categories = ['Semua', 'Makanan', 'Minuman', 'Snack', 'Dessert'];
// Animation
late AnimationController _rotationController;
late Animation<double> _rotationAnimation;
// Sample product data
List<Product> products = [
Product(
id: '1',
name: 'Nasi Goreng Special',
price: 25000,
category: 'Makanan',
stock: 50,
imageUrl: 'assets/images/nasi_goreng.jpg',
isActive: true,
),
Product(
id: '8',
name: 'Nasi Goreng',
price: 15000,
category: 'Makanan',
stock: 50,
imageUrl: 'assets/images/nasi_goreng.jpg',
isActive: true,
),
Product(
id: '9',
name: 'Nasi Goreng Telor',
price: 18000,
category: 'Makanan',
stock: 50,
imageUrl: 'assets/images/nasi_goreng.jpg',
isActive: true,
),
Product(
id: '10',
name: 'Mie Goreng ',
price: 18000,
category: 'Makanan',
stock: 50,
imageUrl: 'assets/images/nasi_goreng.jpg',
isActive: true,
),
Product(
id: '2',
name: 'Es Teh Manis',
price: 8000,
category: 'Minuman',
stock: 100,
imageUrl: 'assets/images/es_teh.jpg',
isActive: true,
),
Product(
id: '6',
name: 'Es Jeruk',
price: 10000,
category: 'Minuman',
stock: 100,
imageUrl: 'assets/images/es_teh.jpg',
isActive: true,
),
Product(
id: '7',
name: 'Es Kelapa',
price: 12000,
category: 'Minuman',
stock: 100,
imageUrl: 'assets/images/es_teh.jpg',
isActive: true,
),
Product(
id: '3',
name: 'Keripik Singkong',
price: 15000,
category: 'Snack',
stock: 25,
imageUrl: 'assets/images/keripik.jpg',
isActive: true,
),
Product(
id: '4',
name: 'Es Krim Vanilla',
price: 12000,
category: 'Dessert',
stock: 30,
imageUrl: 'assets/images/ice_cream.jpg',
isActive: false,
),
Product(
id: '5',
name: 'Ayam Bakar',
price: 35000,
category: 'Makanan',
stock: 20,
imageUrl: 'assets/images/ayam_bakar.jpg',
isActive: true,
),
];
List<Product> get filteredProducts {
return products.where((product) {
bool matchesCategory =
selectedCategory == 'Semua' || product.category == selectedCategory;
return matchesCategory;
}).toList();
}
@override
initState() {
super.initState();
_rotationController = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
)..repeat();
_rotationAnimation = Tween<double>(
begin: 0,
end: 2 * math.pi,
).animate(_rotationController);
}
@override
void dispose() {
_rotationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColor.background,
body: CustomScrollView(
slivers: [
_buildSliverAppBar(),
_buildCategoryFilter(),
_buildProductGrid(),
_buildEmptyState(),
],
),
);
}
Widget _buildSliverAppBar() {
return SliverAppBar(
expandedHeight: 120.0,
floating: false,
pinned: true,
elevation: 0,
flexibleSpace: ProductAppbar(rotationAnimation: _rotationAnimation),
actions: [
ActionIconButton(onTap: () {}, icon: LineIcons.search),
ActionIconButton(onTap: _showAddProductDialog, icon: LineIcons.plus),
ActionIconButton(onTap: _showOptionsMenu, icon: LineIcons.filter),
],
);
}
Widget _buildCategoryFilter() {
return SliverPersistentHeader(
pinned: true,
delegate: ProductCategoryHeaderDelegate(
categories: categories,
selectedCategory: selectedCategory,
onCategoryChanged: (category) {
setState(() {
selectedCategory = category;
});
},
),
);
}
Widget _buildProductGrid() {
if (filteredProducts.isEmpty) {
return const SliverToBoxAdapter(child: SizedBox.shrink());
}
return SliverPadding(
padding: const EdgeInsets.all(16.0),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 0.85,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
),
delegate: SliverChildBuilderDelegate((context, index) {
final product = filteredProducts[index];
return ProductTile(product: product, onTap: () {});
}, childCount: filteredProducts.length),
),
);
}
Widget _buildEmptyState() {
if (filteredProducts.isNotEmpty) {
return const SliverToBoxAdapter(child: SizedBox.shrink());
}
return SliverToBoxAdapter(
child: Container(
height: 300,
margin: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.inventory_2_outlined,
size: 64,
color: AppColor.textLight,
),
const SizedBox(height: 16),
Text(
'Tidak ada produk ditemukan',
style: TextStyle(
color: AppColor.textSecondary,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
Text(
'Coba ubah filter atau tambah produk baru',
style: TextStyle(color: AppColor.textLight, fontSize: 14),
textAlign: TextAlign.center,
),
],
),
),
);
}
void _showAddProductDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Tambah Produk'),
content: const Text(
'Dialog tambah produk akan diimplementasikan di sini',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Batal'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('Simpan'),
),
],
),
);
}
void _showOptionsMenu() {
showModalBottomSheet(
context: context,
builder: (context) => Container(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.sort),
title: const Text('Urutkan'),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: const Icon(Icons.filter_list),
title: const Text('Filter Lanjutan'),
onTap: () => Navigator.pop(context),
),
ListTile(
leading: const Icon(Icons.download),
title: const Text('Export Data'),
onTap: () => Navigator.pop(context),
),
],
),
),
);
}
}
// Product Model
class Product {
final String id;
final String name;
final int price;
final String category;
final int stock;
final String imageUrl;
bool isActive;
Product({
required this.id,
required this.name,
required this.price,
required this.category,
required this.stock,
required this.imageUrl,
required this.isActive,
});
}