fixed list view

This commit is contained in:
hannathkadher
2025-01-16 02:14:04 +04:00
parent 60028cdf78
commit 8a95f93556
4 changed files with 344 additions and 115 deletions

View File

@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class DialogDropdown extends StatefulWidget {
final List<String> items;
final ValueChanged<String> onSelected;
final String? selectedValue;
const DialogDropdown({
Key? key,
required this.items,
required this.onSelected,
this.selectedValue,
}) : super(key: key);
@override
_DialogDropdownState createState() => _DialogDropdownState();
}
class _DialogDropdownState extends State<DialogDropdown> {
bool _isOpen = false;
late OverlayEntry _overlayEntry;
@override
void initState() {
super.initState();
}
void _toggleDropdown() {
if (_isOpen) {
_closeDropdown();
} else {
_openDropdown();
}
}
void _openDropdown() {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry);
_isOpen = true;
}
void _closeDropdown() {
_overlayEntry.remove();
_isOpen = false;
}
OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) {
return GestureDetector(
onTap: () {
_closeDropdown();
},
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: offset.dy + size.height,
width: size.width,
child: Material(
elevation: 4.0,
child: Container(
color: ColorsManager.whiteColors,
constraints: const BoxConstraints(
maxHeight: 200.0, // Set max height for dropdown
),
child: ListView.builder(
shrinkWrap: true,
itemCount: widget.items.length,
itemBuilder: (context, index) {
final item = widget.items[index];
return Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ColorsManager.lightGrayBorderColor,
width: 1.0,
),
),
),
child: ListTile(
title: Text(
item,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager.textPrimaryColor,
),
),
onTap: () {
widget.onSelected(item);
_closeDropdown();
},
),
);
},
),
),
),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _toggleDropdown,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration(
border: Border.all(color: ColorsManager.transparentColor),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
widget.selectedValue ?? 'Select an item',
style: Theme.of(context).textTheme.bodyMedium,
),
const Icon(Icons.arrow_drop_down),
],
),
),
);
}
}

View File

@ -0,0 +1,160 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class DialogTextfieldDropdown extends StatefulWidget {
final List<String> items;
final ValueChanged<String> onSelected;
final String? initialValue;
const DialogTextfieldDropdown({
Key? key,
required this.items,
required this.onSelected,
this.initialValue,
}) : super(key: key);
@override
_DialogTextfieldDropdownState createState() =>
_DialogTextfieldDropdownState();
}
class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
bool _isOpen = false;
late OverlayEntry _overlayEntry;
final TextEditingController _controller = TextEditingController();
late List<String> _filteredItems; // Filtered items list
@override
void initState() {
super.initState();
_controller.text = widget.initialValue ?? 'Select Tag';
_filteredItems = List.from(widget.items); // Initialize filtered items
}
void _toggleDropdown() {
if (_isOpen) {
_closeDropdown();
} else {
_openDropdown();
}
}
void _openDropdown() {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry);
_isOpen = true;
}
void _closeDropdown() {
_overlayEntry.remove();
_isOpen = false;
}
OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) {
return GestureDetector(
onTap: () {
_closeDropdown();
},
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: offset.dy + size.height,
width: size.width,
child: Material(
elevation: 4.0,
child: Container(
color: ColorsManager.whiteColors,
constraints: const BoxConstraints(
maxHeight: 200.0,
),
child: ListView.builder(
shrinkWrap: true,
itemCount: _filteredItems.length,
itemBuilder: (context, index) {
final item = _filteredItems[index];
return Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ColorsManager.lightGrayBorderColor,
width: 1.0,
),
),
),
child: ListTile(
title: Text(item,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager.textPrimaryColor)),
onTap: () {
_controller.text = item;
widget.onSelected(item);
setState(() {
_filteredItems
.remove(item); // Remove selected item
});
_closeDropdown();
},
),
);
},
),
),
),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _toggleDropdown,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration(
border: Border.all(color: ColorsManager.transparentColor),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextFormField(
controller: _controller,
onChanged: (value) {
setState(() {
_filteredItems = widget.items
.where((item) =>
item.toLowerCase().contains(value.toLowerCase()))
.toList(); // Filter items dynamically
});
widget.onSelected(value);
},
style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration(
hintText: 'Enter or Select tag',
border: InputBorder.none,
),
),
),
const Icon(Icons.arrow_drop_down),
],
),
),
);
}
}

View File

@ -127,7 +127,8 @@ class AssignTagModelBloc
}
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
return uniqueTags.length == tags.length && !hasEmptyTag;
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
return isValid;
}
String? _getValidationError(List<TagModel> tags) {

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/common/dialog_dropdown.dart';
import 'package:syncrow_web/common/dialog_textfield_dropdown.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
@ -79,6 +81,8 @@ class AssignTagModelsDialog extends StatelessWidget {
style:
Theme.of(context).textTheme.bodyMedium)),
DataColumn(
numeric: false,
headingRowAlignment: MainAxisAlignment.start,
label: Text('Tag',
style:
Theme.of(context).textTheme.bodyMedium)),
@ -109,6 +113,8 @@ class AssignTagModelsDialog extends StatelessWidget {
: List.generate(state.tags.length, (index) {
final tag = state.tags[index];
final controller = controllers[index];
final availableTags = getAvailableTags(
allTags ?? [], state.tags, tag);
return DataRow(
cells: [
@ -159,130 +165,43 @@ class AssignTagModelsDialog extends StatelessWidget {
),
),
DataCell(
Row(
children: [
Expanded(
child: TextFormField(
controller: controller,
onChanged: (value) {
context
.read<AssignTagModelBloc>()
.add(UpdateTag(
index: index,
tag: value.trim(),
));
},
decoration: const InputDecoration(
border: InputBorder.none,
),
style: const TextStyle(
fontSize: 14,
color: ColorsManager.blackColor,
),
),
Container(
alignment: Alignment
.centerLeft, // Align cell content to the left
child: SizedBox(
width: double
.infinity, // Ensure full width for dropdown
child: DialogTextfieldDropdown(
items: availableTags ?? [],
onSelected: (value) {
controller.text = value;
context
.read<AssignTagModelBloc>()
.add(UpdateTag(
index: index,
tag: value,
));
},
),
SizedBox(
width: MediaQuery.of(context)
.size
.width *
0.15,
child: PopupMenuButton<String>(
color: ColorsManager.whiteColors,
icon: const Icon(
Icons.arrow_drop_down,
color:
ColorsManager.blackColor),
onSelected: (value) {
controller.text = value;
context
.read<AssignTagModelBloc>()
.add(UpdateTag(
index: index,
tag: value,
));
},
itemBuilder: (context) {
return (allTags ?? [])
.where((tagValue) => !state
.tags
.map((e) => e.tag)
.contains(tagValue))
.map((tagValue) {
return PopupMenuItem<String>(
textStyle: const TextStyle(
color: ColorsManager
.textPrimaryColor),
value: tagValue,
child: ConstrainedBox(
constraints:
BoxConstraints(
minWidth: MediaQuery.of(
context)
.size
.width *
0.15,
maxWidth: MediaQuery.of(
context)
.size
.width *
0.15,
),
child: Text(
tagValue,
overflow: TextOverflow
.ellipsis,
),
));
}).toList();
},
),
),
],
),
),
),
DataCell(
DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: tag.location ?? 'None',
dropdownColor: ColorsManager
.whiteColors, // Dropdown background
style: const TextStyle(
color: Colors
.black), // Style for selected text
items: [
const DropdownMenuItem<String>(
value: 'None',
child: Text(
'None',
style: TextStyle(
color: ColorsManager
.textPrimaryColor),
),
),
...locations.map((location) {
return DropdownMenuItem<String>(
value: location,
child: Text(
location,
style: const TextStyle(
color: ColorsManager
.textPrimaryColor),
),
);
}).toList(),
],
onChanged: (value) {
if (value != null) {
SizedBox(
width: double.infinity,
child: DialogDropdown(
items: locations,
selectedValue:
tag.location ?? 'None',
onSelected: (value) {
context
.read<AssignTagModelBloc>()
.add(UpdateLocation(
index: index,
location: value,
));
}
},
),
),
},
)),
),
],
);
@ -380,4 +299,15 @@ class AssignTagModelsDialog extends StatelessWidget {
),
);
}
List<String> getAvailableTags(
List<String> allTags, List<TagModel> currentTags, TagModel currentTag) {
print("happening");
return allTags
.where((tagValue) => !currentTags
.where((e) => e != currentTag) // Exclude the current row
.map((e) => e.tag)
.contains(tagValue))
.toList();
}
}