Sortable#
드래그 앤 드롭으로 항목의 순서를 변경할 수 있는 정렬 가능한 리스트 컴포넌트입니다.
Live Preview#
Web
Item 1
Item 2
Item 3
Flutter
Loading Flutter...
class SortableDefaultExample extends StatefulComponent {
const SortableDefaultExample({super.key});
@override
State<SortableDefaultExample> createState() => _SortableDefaultExampleState();
}
class _SortableDefaultExampleState extends State<SortableDefaultExample> {
List<String> _items = const ['Item 1', 'Item 2', 'Item 3'];
void handleReorder(int oldIndex, int newIndex) {
setState(() {
final newItems = [..._items];
final item = newItems.removeAt(oldIndex);
newItems.insert(newIndex, item);
_items = newItems;
});
}
@override
Component build(BuildContext context) {
return CoSortable(
itemCount: _items.length,
onReorder: handleReorder,
itemBuilder: (index, dragHandle, removeButton) => div(
[
dragHandle,
span([Component.text(_items[index])]),
],
classes: 'flex items-center flex-1',
),
);
}
}
class SortableDefaultExample extends StatefulWidget {
const SortableDefaultExample({super.key});
@override
State<SortableDefaultExample> createState() => _SortableDefaultExampleState();
}
class _SortableDefaultExampleState extends State<SortableDefaultExample> {
List<String> _items = const ['Item 1', 'Item 2', 'Item 3'];
void handleReorder(int oldIndex, int newIndex) {
setState(() {
final newItems = [..._items];
final item = newItems.removeAt(oldIndex);
newItems.insert(newIndex, item);
_items = newItems;
});
}
@override
Widget build(BuildContext context) {
return CoSortable(
itemCount: _items.length,
onReorder: handleReorder,
itemBuilder: (context, index, dragHandle, removeButton) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
dragHandle,
const SizedBox(width: 8),
Text(_items[index]),
],
);
},
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 사용자가 항목의 우선순위나 순서를 직접 지정해야 하는 경우 (할 일 목록, 메뉴 순서 등)
- 대시보드 위젯, 사이드바 메뉴 순서를 사용자가 커스터마이즈할 수 있어야 하는 경우
- 카테고리나 태그의 노출 순서를 관리자가 직접 설정해야 하는 경우
대신 다른 컴포넌트를 사용하세요:
Table: 정렬이 필요 없는 단순 데이터 표시 테이블Tree: 항목 간 계층 구조(부모-자식)가 있는 경우
기본 사용법 (Basic Usage)#
CoSortable(
itemCount: items.length,
onReorder: (oldIndex, newIndex) {
setState(() {
final newItems = [...items];
final item = newItems.removeAt(oldIndex);
newItems.insert(newIndex, item);
items = newItems;
});
},
itemBuilder: (context, index, dragHandle, removeButton) {
return Row(
children: [
dragHandle,
Expanded(child: Text(items[index])),
if (removeButton != null) removeButton,
],
);
},
)
CoSortable(
itemCount: items.length,
onReorder: (oldIndex, newIndex) => reorderItems(oldIndex, newIndex),
itemBuilder: (index, dragHandle, removeButton) => div(
[
dragHandle,
span([text(items[index])]),
if (removeButton != null) removeButton,
],
classes: 'flex items-center flex-1',
),
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
itemCount | int | 필수 | 정렬할 항목 수 |
itemBuilder |
Function(context, index, dragHandle, removeButton) |
필수 | 항목 빌더 (드래그 핸들과 제거 버튼을 파라미터로 받음) |
onReorder |
void Function(int oldIndex, int newIndex)? |
null |
순서 변경 콜백 |
onRemove |
void Function(int index)? |
null |
항목 제거 콜백 (removable true 시 필수) |
enabled |
bool |
true |
드래그 앤 드롭 활성화 여부 |
removable |
bool |
false |
제거 버튼 표시 여부 (true 시 onRemove 필요) |
제거 가능한 항목 (Removable Items)#
CoSortable(
itemCount: items.length,
onReorder: handleReorder,
onRemove: (index) {
setState(() {
items = [
...items.sublist(0, index),
...items.sublist(index + 1),
];
});
},
removable: true,
itemBuilder: (context, index, dragHandle, removeButton) {
return Row(
children: [
dragHandle,
Expanded(child: Text(items[index])),
if (removeButton != null) removeButton,
],
);
},
)
테마 커스터마이징 (Theme Customization)#
CoreComponentTheme.sortable을 통해 프로젝트 레벨에서 스타일을 오버라이드할 수 있습니다.
CoreComponentTheme(
sortable: CoreSortableTheme(
itemBackgroundColor: CoreColor(0xFFF5F5F5),
itemBorderRadius: 8.0,
handleColor: CoreColor(0xFF888888),
gap: 12.0,
),
)
동작 스펙 (Behavior)#
인터랙션#
-
Flutter: 항목 롱프레스 후 드래그하여 순서 변경 (
LongPressDraggable/DragTarget) -
Web: HTML5 Drag and Drop API (
draggable="true",dragover,drop) - 드래그 중 항목은 반투명 처리되며, 드롭 대상에는 강조 테두리 표시
접근성#
- 루트 컨테이너에
role="list"자동 적용 - 각 항목에
role="listitem"자동 적용 - 드래그 핸들은
cursor: grab/cursor: grabbing커서 표시
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 드래그 구현 | LongPressDraggable + DragTarget |
HTML5 Drag and Drop API |
| 드래그 핸들 아이콘 | Icons.drag_indicator | Unicode ⠁⠁⠁ |
| 제거 버튼 아이콘 | Icons.close | Unicode ✕ |