Drag Selectable ListView
A Flutter ListView that supports drag-to-select multiple items with customizable selection UI and smooth interaction.
Features
✨ Drag Selection: Select multiple items by dragging across them
🎨 Customizable UI: Custom checkbox builders and item layouts
📱 Lazy Loading: Uses ListView.builder for efficient rendering
🎯 Flexible Selection: Support for both tap and drag selection modes
🔧 Easy Integration: Simple API that works with existing ListView patterns
Getting Started
Installation
Add this to your package's pubspec.yaml file:
dependencies:
drag_selectable_listview: ^0.0.1
Then run:
flutter pub get
Basic Usage
import 'package:drag_selectable_listview/drag_selectable_listview.dart';
class MyListWidget extends StatefulWidget {
@override
_MyListWidgetState createState() => _MyListWidgetState();
}
class _MyListWidgetState extends State<MyListWidget> {
Set<int> selectedItems = {};
@override
Widget build(BuildContext context) {
return DragSelectableList(
itemCount: 50,
selected: selectedItems,
itemHeight: 56.0,
checkboxWidth: 48.0,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
subtitle: Text('Description for item $index'),
);
},
checkboxBuilder: ({
required bool value,
required ValueChanged<bool?> onChanged,
}) {
return Checkbox(
value: value,
onChanged: onChanged,
);
},
onSelectionChanged: (Set<int> newSelection) {
setState(() {
selectedItems = newSelection;
});
},
);
}
}
Advanced Usage
Custom Checkbox Design
checkboxBuilder: ({
required bool value,
required ValueChanged<bool?> onChanged,
}) {
return Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: value ? Colors.blue : Colors.transparent,
border: Border.all(color: Colors.blue),
borderRadius: BorderRadius.circular(4),
),
child: value
? Icon(Icons.check, size: 16, color: Colors.white)
: null,
);
}
Custom Item Height
DragSelectableList(
itemHeight: 80.0, // Custom height for each item
checkboxWidth: 60.0, // Custom checkbox area width
// ... other parameters
)
Initial Selection
Set<int> initialSelected = {0, 2, 5}; // Pre-select items
DragSelectableList(
selected: initialSelected,
// ... other parameters
)
API Reference
Properties
- itemCount (int): Total number of items in the list
- itemBuilder (IndexedWidgetBuilder): Builder function for list items
- selected (Set
- onSelectionChanged (ValueChanged<Set
- checkboxBuilder (Function): Builder for custom checkbox widgets
- itemHeight (double): Height of each list item
- checkboxWidth (double): Width allocated for checkbox area
Selection Behavior
- Tap Selection: Tap individual checkboxes to toggle selection
- Drag Selection: Press and drag across items to select/deselect ranges
- Selection Toggle: Drag selection toggles items (selected ↔ deselected)
- Visual Feedback: Selection changes are immediately reflected in the UI
Examples
Contact List Example
class ContactList extends StatefulWidget {
@override
_ContactListState createState() => _ContactListState();
}
class _ContactListState extends State<ContactList> {
Set<int> selectedContacts = {};
List<Contact> contacts = [/* your contacts */];
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Selected: ${selectedContacts.length} contacts'),
Expanded(
child: DragSelectableList(
itemCount: contacts.length,
selected: selectedContacts,
itemHeight: 72.0,
checkboxWidth: 56.0,
itemBuilder: (context, index) {
final contact = contacts[index];
return ListTile(
leading: CircleAvatar(
child: Text(contact.name[0]),
),
title: Text(contact.name),
subtitle: Text(contact.email),
);
},
checkboxBuilder: ({
required bool value,
required ValueChanged<bool?> onChanged,
}) {
return Checkbox(
value: value,
onChanged: onChanged,
);
},
onSelectionChanged: (newSelection) {
setState(() {
selectedContacts = newSelection;
});
},
),
),
],
);
}
}
Performance
Current Optimizations
✅ Lazy Loading: Uses ListView.builder for efficient memory usage
✅ Efficient Scrolling: Proper scroll controller management
✅ Minimal Rebuilds: Targeted widget updates
Performance Considerations
For optimal performance with large lists:
// Good: Use const constructors for static content
itemBuilder: (context, index) {
return const MyListItemWidget(); // If possible
}
// Good: Provide explicit item heights
itemHeight: 56.0, // Avoids layout calculations
// Consider: Use RepaintBoundary for complex items
itemBuilder: (context, index) {
return RepaintBoundary(
child: MyComplexItemWidget(),
);
}
Future Optimizations
Potential improvements for future versions:
- RepaintBoundary integration for complex items
- AutomaticKeepAlive for state preservation
- Selection change batching for smoother drag operations
- Item caching mechanisms
Testing
This package includes comprehensive tests covering:
- Widget rendering and layout
- Selection state management
- Drag gesture handling
- Custom UI components
- Edge cases and boundary conditions
Run tests with:
flutter test
See TESTING.md for detailed test documentation.
Contributing
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Bug Reports
Please file bug reports with:
- Clear description of the issue
- Steps to reproduce
- Expected vs actual behavior
- Flutter version and device information
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
0.0.1
- Initial release
- Basic drag selection functionality
- Custom checkbox support
- Configurable dimensions
Support
If you have any questions or need help, please open an issue on GitHub.
Made with ❤️ by the Flutter community