second commit

This commit is contained in:
Anbarasu
2026-05-27 10:35:09 +05:30
parent c53794c04c
commit 1435ac47b0
501 changed files with 52818 additions and 0 deletions

View File

@@ -0,0 +1,330 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../Controller/products/product_controller.dart';
import '../../Globalwidgets/textwidget.dart';
import '../../Helper/Constants/AssetConstants.dart';
import '../../Helper/Constants/Colorconstants.dart';
import '../../Helper/Logger.dart';
import '../../Model/Response/products/product_response.dart';
import '../Dashboard/Dashboardview.dart';
class ProductView extends StatelessWidget {
ProductView({super.key});
final ProductController controller = Get.put(ProductController());
final FocusNode searchFocusNode = FocusNode();
@override
Widget build(BuildContext context) {
return GetBuilder<ProductController>(
initState: (_) {
controller.getProducts();
},
builder: (controller) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (Widget child, Animation<double> animation) {
return SizeTransition(
sizeFactor: animation,
axis: Axis.horizontal,
child: FadeTransition(opacity: animation, child: child),
);
},
child: controller.isSearchModeEnable.value
? TextField(
key: const ValueKey('searchField'),
focusNode: searchFocusNode,
controller: controller.productSearchController,
cursorColor: ColorConstants.primaryColor,
decoration: const InputDecoration(
hintText: 'Search Products',
border: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Colors.white, width: 2),
),
isDense: false,
contentPadding: EdgeInsets.zero,
),
style: const TextStyle(color: Colors.black),
// 🔥 FIXED HERE — USE LOCAL SEARCH
onChanged: (value) {
controller.applySearch(value);
},
autofocus: true,
)
: TextWidget(
key: const ValueKey('titleText'),
text: 'Products',
fontWeight: FontWeight.w700,
fontSize: 20,
),
),
actions: [
Padding(
padding: const EdgeInsets.only(right: 12),
child: InkWell(
onTap: () {
if (controller.isSearchModeEnable.value) {
controller.productSearchController.clear();
searchFocusNode.unfocus();
// 🔥 FIXED HERE — RESTORE LIST
controller.applySearch('');
} else {
Future.delayed(const Duration(milliseconds: 100), () {
searchFocusNode.requestFocus();
});
}
controller.isSearchModeEnable.value =
!controller.isSearchModeEnable.value;
controller.update();
},
child: Icon(
controller.isSearchModeEnable.value
? Icons.cancel
: Icons.search,
color: ColorConstants.primaryColor,
),
),
),
],
),
body: Obx(() {
if (controller.isProductLoading.value) {
return const Center(
child: Padding(
padding: EdgeInsets.only(top: 10),
child: ShimmerListView(height: 100),
),
);
}
if (controller.product.isEmpty) {
return emptyProductsWidget();
}
return ListView.builder(
cacheExtent: 1000,
itemCount: controller.product.length,
itemBuilder: (context, index) {
final product = controller.product[index];
return ProductCard(
key: ValueKey(product.productid),
product: product,
index: index,
controller: controller,
isLoading: controller.loadingIndices.contains(index),
);
},
);
}),
);
},
);
}
}
/// Product Card Widget
class ProductCard extends StatelessWidget {
final ProductData product;
final int index;
final ProductController controller;
final bool isLoading;
const ProductCard({
super.key,
required this.product,
required this.index,
required this.controller,
required this.isLoading,
});
@override
Widget build(BuildContext context) {
final isAvailable = product.status == 'Active';
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
child: Padding(
padding: const EdgeInsets.only(top: 5),
child: Card(
elevation: 0,
shadowColor: Colors.grey.shade100,
color: Colors.white,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.only(top: 12, left: 10, right: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: CachedNetworkImage(
imageUrl: product.productimage ?? '',
width: 80,
height: 80,
fit: BoxFit.cover,
memCacheHeight: 160,
memCacheWidth: 160,
maxHeightDiskCache: 160,
maxWidthDiskCache: 160,
placeholder: (context, url) => Container(
width: 80,
height: 80,
color: Colors.grey.shade100,
child: Icon(Icons.image,
color: Colors.grey.shade400, size: 40),
),
errorWidget: (context, url, error) => Container(
width: 80,
height: 80,
color: Colors.grey.shade100,
child: Icon(Icons.broken_image_outlined,
color: Colors.grey.shade400, size: 40),
),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextWidget(
text: product.productname ?? 'Unknown',
maxLines: 1,
fontSize: 15,
fontWeight: FontWeight.w700,
color: isAvailable
? Colors.black87
: Colors.grey.shade600,
),
const SizedBox(height: 8),
Row(
children: [
Icon(
Icons.inventory_2_outlined,
size: 16,
color: Colors.grey.shade500,
),
const SizedBox(width: 4),
Text(
"Qty: ${product.productstock ?? 0}",
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
fontWeight: FontWeight.w500,
),
),
const Spacer(),
Text(
product.productcost != null
? '${product.productcost!.toStringAsFixed(2)}'
: 'N/A',
style: TextStyle(
fontSize: 14,
color: Colors.grey.shade700,
fontWeight: FontWeight.w500,
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isAvailable
? Colors.green.shade500
: Colors.red.shade400,
borderRadius: BorderRadius.circular(10),
),
child: Text(
isAvailable ? "Active" : "Out of Stock",
style: const TextStyle(
fontSize: 11,
color: Colors.white,
fontWeight: FontWeight.w600,
),
),
),
const Spacer(),
AnimatedOpacity(
opacity: isLoading ? 0.6 : 1.0,
duration: const Duration(milliseconds: 300),
child: Switch.adaptive(
value: isAvailable,
activeColor: Colors.green.shade500,
inactiveThumbColor: Colors.grey.shade400,
inactiveTrackColor: Colors.grey.shade200,
onChanged: isLoading
? null
: (_) => controller.toggleAvailability(index),
),
),
],
),
],
),
),
],
),
),
),
),
);
}
}
Widget emptyProductsWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 10),
Image.asset(
AssetConstants.noProductsFound,
height: 200,
width: 200,
fit: BoxFit.fill,
),
TextWidget(
text: 'No Products Found!',
color: ColorConstants.blackColor,
fontSize: 18,
fontWeight: FontWeight.w700,
maxLines: 2,
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
TextWidget(
text: 'You havent added any products yet.',
color: ColorConstants.blackColor,
fontSize: 14,
fontWeight: FontWeight.normal,
maxLines: 2,
textAlign: TextAlign.center,
),
],
),
);
}