fix : list items

This commit is contained in:
2025-07-08 07:14:56 +03:30
parent 3b5d1665d6
commit 639a8dd585
3 changed files with 256 additions and 63 deletions

View File

@@ -34,7 +34,7 @@ class SalesOutOfProvinceBuyersPage
child: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.timerSvg.path,
labelIcon: Assets.vec.userRaduisSvg.path,
);
}, controller.isExpandedList);
},
@@ -121,6 +121,8 @@ class SalesOutOfProvinceBuyersPage
Widget submitButtonWidget(bool isOnEdit) {
return ObxValue((data) {
return RElevated(
isFullWidth: true,
backgroundColor: AppColor.greenNormal,
text: isOnEdit ? 'ویرایش' : 'ثبت',
onPressed: data.value
? () async {
@@ -137,7 +139,7 @@ class SalesOutOfProvinceBuyersPage
Widget _provinceWidget() {
return Obx(() {
return OverlayDropdownWidget<IranProvinceCityModel>(
return OverlayDropdownWidget2<IranProvinceCityModel>(
items: controller.rootLogic.provinces,
onChanged: (value) {
controller.selectedProvince.value = value;
@@ -223,27 +225,8 @@ class SalesOutOfProvinceBuyersPage
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
controller.setEditDataBuyer(item);
Get.bottomSheet(
addOrEditBuyerBottomSheet(true),
isScrollControlled: true,
).whenComplete(() {
controller.resetSubmitForm();
});
},
child: Assets.vec.editSvg.svg(
width: 20,
height: 20,
colorFilter: ColorFilter.mode(
AppColor.blueNormal,
BlendMode.srcIn,
),
),
),
Text(
'${item.province}-${item.city}',
@@ -265,6 +248,29 @@ class SalesOutOfProvinceBuyersPage
title: 'وزن',
value: '${item.requestsInfo?.totalWeight.separatedByComma}',
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 16.w,
children: [
RElevated(
text: 'ویرایش',
width: 150.w,
height: 40.h,
onPressed: () {
controller.setEditDataBuyer(item);
Get.bottomSheet(
addOrEditBuyerBottomSheet(true),
isScrollControlled: true,
).whenComplete(() {
controller.resetSubmitForm();
});
},
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
backgroundColor: AppColor.greenNormal,
),
],
),
],
),
);

View File

@@ -34,7 +34,7 @@ class SalesOutOfProvinceSalesListPage
onTap: () => controller.isExpandedList.toggle(index),
index: index,
child: itemListWidget(item),
secondChild: itemListExpandedWidget(item),
secondChild: itemListExpandedWidget(item, index),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.timerSvg.path,
);
@@ -70,6 +70,7 @@ class SalesOutOfProvinceSalesListPage
Expanded(
flex: 5,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
item.buyer?.fullname ?? 'N/A',
@@ -90,6 +91,7 @@ class SalesOutOfProvinceSalesListPage
Expanded(
flex: 4,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Text(
@@ -117,7 +119,7 @@ class SalesOutOfProvinceSalesListPage
);
}
itemListExpandedWidget(StewardFreeSaleBar item) {
itemListExpandedWidget(StewardFreeSaleBar item, int index) {
return Container(
padding: EdgeInsets.fromLTRB(8, 12, 14, 12),
@@ -129,51 +131,13 @@ class SalesOutOfProvinceSalesListPage
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
controller.setEditDataSales(item);
Get.bottomSheet(
addOrEditSaleBottomSheet(true),
isScrollControlled: true,
).whenComplete(() {
controller.resetSubmitForm();
});
},
child: Assets.vec.editSvg.svg(
width: 20,
height: 20,
colorFilter: ColorFilter.mode(
AppColor.blueNormal,
BlendMode.srcIn,
),
),
),
Text(
'${item.province}-${item.city}',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
GestureDetector(
onTap: () {
buildDeleteDialog(
onConfirm: () => controller
.deleteStewardPurchaseOutOfProvince(item.key!),
onRefresh: () => controller.getOutProvinceSales(),
);
},
child: Assets.vec.trashSvg.svg(
width: 20,
height: 20,
colorFilter: ColorFilter.mode(
AppColor.error,
BlendMode.srcIn,
),
),
),
],
),
Container(
@@ -201,6 +165,45 @@ class SalesOutOfProvinceSalesListPage
title: 'وزن لاشه',
value: '${item.weightOfCarcasses?.separatedByComma}',
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 16.w,
children: [
RElevated(
text: 'ویرایش',
width: 150.w,
height: 40.h,
onPressed: () {
controller.setEditDataSales(item);
Get.bottomSheet(
addOrEditSaleBottomSheet(true),
isScrollControlled: true,
).whenComplete(() {
controller.resetSubmitForm();
});
},
textStyle: AppFonts.yekan20.copyWith(color: Colors.white),
backgroundColor: AppColor.greenNormal,
),
ROutlinedElevated(
text: 'حذف',
textStyle: AppFonts.yekan20.copyWith(color: AppColor.redNormal),
width: 150.w,
height: 40.h,
onPressed: () {
buildDeleteDialog(
onConfirm: () async {
controller.isExpandedList.remove(index);
controller.deleteStewardPurchaseOutOfProvince(item.key!);
},
onRefresh: () => controller.getOutProvinceSales(),
);
},
borderColor: AppColor.redNormal,
),
],
),
],
),
);
@@ -277,6 +280,8 @@ class SalesOutOfProvinceSalesListPage
Widget submitButtonWidget(bool isOnEdit) {
return ObxValue((data) {
return RElevated(
isFullWidth: true,
backgroundColor: AppColor.greenNormal,
text: isOnEdit ? 'ویرایش' : 'ثبت',
onPressed: data.value
? () async {

View File

@@ -139,3 +139,185 @@ class _OverlayDropdownState<T> extends State<OverlayDropdownWidget<T>> {
);
}
}
class OverlayDropdownWidget2<T> extends StatefulWidget {
final List<T> items;
final T? selectedItem;
final T? initialValue;
final Widget Function(T item) itemBuilder;
final Widget Function(T? selected) labelBuilder;
final void Function(T selected)? onChanged;
final EdgeInsets? contentPadding;
final String Function(T item)? itemToString;
const OverlayDropdownWidget2({
super.key,
required this.items,
required this.itemBuilder,
required this.labelBuilder,
this.initialValue,
this.onChanged,
this.selectedItem,
this.contentPadding,
this.itemToString,
});
@override
State<OverlayDropdownWidget2<T>> createState() => _OverlayDropdownState2<T>();
}
class _OverlayDropdownState2<T> extends State<OverlayDropdownWidget2<T>> {
final GlobalKey _key = GlobalKey();
OverlayEntry? _overlayEntry;
final RxBool _isOpen = false.obs;
T? selectedItem;
late TextEditingController _searchController;
late RxList<T> _filteredItems;
@override
void initState() {
super.initState();
selectedItem = widget.selectedItem ?? widget.initialValue;
_searchController = TextEditingController();
_filteredItems = RxList<T>(widget.items);
}
void _showOverlay() {
final renderBox = _key.currentContext!.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
final screenHeight = MediaQuery.of(context).size.height;
final bool openUp = offset.dy + size.height + 300 > screenHeight;
_searchController.clear();
_filteredItems.value = widget.items;
_overlayEntry = OverlayEntry(
builder: (_) => GestureDetector(
onTap: _removeOverlay,
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: openUp ? offset.dy - 300 - 4 : offset.dy + size.height + 4,
width: size.width,
child: Material(
elevation: 4,
borderRadius: BorderRadius.circular(8),
child: Obx(() => Container(
decoration: BoxDecoration(
color: AppColor.bgLight,
border: Border.all(color: AppColor.darkGreyLight),
borderRadius: BorderRadius.circular(8),
),
constraints: BoxConstraints(maxHeight: 300),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8),
child: TextField(
controller: _searchController,
decoration: const InputDecoration(
hintText: 'جستجو...',
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
border: OutlineInputBorder(),
),
onChanged: (query) {
_filteredItems.value = widget.items
.where((item) =>
widget.itemToString?.call(item).toLowerCase().contains(query.toLowerCase()) ??
false)
.toList();
},
),
),
if (_filteredItems.isEmpty)
const Padding(
padding: EdgeInsets.all(16.0),
child: Text("نتیجه‌ای یافت نشد."),
),
if (_filteredItems.isNotEmpty)
Expanded(
child: ListView(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
children: _filteredItems.map((item) {
return InkWell(
onTap: () {
widget.onChanged?.call(item);
setState(() {
selectedItem = item;
});
_removeOverlay();
},
child: Padding(
padding: widget.contentPadding ??
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: widget.itemBuilder(item),
),
);
}).toList(),
),
),
],
),
)),
),
),
],
),
),
);
Overlay.of(context).insert(_overlayEntry!);
_isOpen.value = true;
}
void _removeOverlay() {
_overlayEntry?.remove();
_overlayEntry = null;
_isOpen.value = false;
}
@override
void dispose() {
_removeOverlay();
_searchController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return IgnorePointer(
ignoring: widget.items.isEmpty,
child: GestureDetector(
key: _key,
onTap: () {
_isOpen.value ? _removeOverlay() : _showOverlay();
},
child: Container(
height: 40,
width: Get.width,
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: widget.items.isEmpty ? Colors.grey.shade200 : AppColor.bgLight,
border: Border.all(color: AppColor.darkGreyLight),
borderRadius: BorderRadius.circular(8),
),
child: Obx(() => Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
widget.labelBuilder(selectedItem),
Icon(_isOpen.value ? CupertinoIcons.chevron_up : CupertinoIcons.chevron_down, size: 14),
],
)),
),
),
);
}
}