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

@@ -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),
],
)),
),
),
);
}
}