Flutter可选中拖拽列表插件selectable_draggable_listbox的使用
Flutter可选中拖拽列表插件selectable_draggable_listbox的使用
插件简介
selectable_draggable_listbox
是一个功能强大的Flutter插件,支持多选、拖拽和排序功能。它允许你将数据绑定到自定义的小部件列表中,并且可以轻松实现多个列表之间的拖拽操作。
主要特性
- 绑定数据到列表视图:
- 简单的文本项小部件
- 可自定义的模板化列表项小部件
- 多选功能:
- 使用Shift点击选择连续项
- 使用Ctrl/Cmd点击选择单个项
- 支持单选模式
- 可排序:
- 点击并拖动“三条线”图标重新排序项
- 拖拽和放置:
- 在不同列表之间拖拽项
- 每个列表可以独立设置为可拖拽、可放置或两者兼有
开始使用
1. 添加依赖
你可以通过以下两种方式添加 selectable_draggable_listbox
到你的项目中:
-
运行命令:
flutter pub add selectable_draggable_listbox
-
或者编辑
pubspec.yaml
文件,然后运行flutter pub get
:dependencies: selectable_draggable_listbox: ^latest_version
2. 导入包
在你的Dart文件中导入插件:
import 'package:selectable_draggable_listbox/selectable_draggable_listbox.dart';
使用示例
下面是一个完整的示例代码,展示了如何使用 selectable_draggable_listbox
创建一个包含购物清单和最近购买清单的界面。这个示例包括多选、排序和拖拽功能。
示例代码
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:selectable_draggable_listbox/selectable_draggable_listbox.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Selectable Draggable Listbox Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Selectable Draggable Listbox Demo'),
);
}
}
class GroceryItem {
final String name;
GroceryItem({
required this.name,
});
}
class RecentGroceryItem extends GroceryItem {
DateTime lastBought;
String get lastBoughtShortDtTm =>
DateFormat('MM/dd/yyyy kk:mm').format(lastBought);
RecentGroceryItem({
required super.name,
required this.lastBought,
});
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: const Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
// 购物清单:支持多选、排序和从该列表拖拽
GroceryListWidget(),
// 最近购买清单:支持单选、自定义模板和拖拽到该列表
RecentListWidget(),
],
),
),
);
}
}
class GroceryListWidget extends StatefulWidget {
const GroceryListWidget({
super.key,
});
[@override](/user/override)
State<GroceryListWidget> createState() => _GroceryListWidgetState();
}
class _GroceryListWidgetState extends State<GroceryListWidget> {
final _groceryList = [
GroceryItem(name: 'Apples'),
GroceryItem(name: 'Bananas'),
GroceryItem(name: 'Milk'),
GroceryItem(name: 'Cheese'),
GroceryItem(name: 'Bread'),
].forListbox().toList();
[@override](/user/override)
Widget build(BuildContext context) {
/// 创建简单的列表项模板
/// 展示带有编号前缀的项
/// 当拖拽时,移除编号前缀
SimpleListboxItem<GroceryItem> makeItemTemplate(
int index,
ListboxEventManager eventManager,
ListItem<GroceryItem> item,
void Function(ListItem<GroceryItem>)? onSelect,
bool isDragging,
) {
return SimpleListboxItem(
key: Key('$index'),
eventManager: eventManager,
item: item,
label: isDragging ? item.data.name : '${index + 1}. ${item.data.name}',
onSelect: onSelect,
isDragging: isDragging,
);
}
void onSelect(Iterable<ListItem<GroceryItem>> itemsSelected) {
debugPrint(
'Selected: ${itemsSelected.map((e) => e.data.name).join(',')}');
setState(() {
for (var item in _groceryList) {
item.isSelected = itemsSelected.contains(item);
}
});
}
void onReorder(int oldIndex, int newIndex) {
debugPrint('Moving item from $oldIndex to $newIndex');
setState(() {
// 使用扩展方法移动项
_groceryList.move(oldIndex, newIndex);
});
}
return Flexible(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Grocery List',
style: Theme.of(context).textTheme.displaySmall,
),
const Text('Features: Multi-select, Reorder, Drag From'),
Expanded(
child: Builder(
builder: (context) {
return Listbox(
// 仅在调试时需要键来标识交互的列表
key: const Key('GroceryList'),
items: _groceryList,
onSelect: onSelect,
onReorder: onReorder,
itemTemplate: (context, eventManager, index, item, onSelect) =>
makeItemTemplate(index, eventManager, item, onSelect, false),
dragTemplate: (context, eventManager, index, item) =>
makeItemTemplate(index, eventManager, item, null, true),
// 启用调试信息
enableDebug: true,
);
},
),
),
],
),
),
);
}
}
class RecentListWidget extends StatefulWidget {
const RecentListWidget({
super.key,
});
[@override](/user/override)
State<RecentListWidget> createState() => _RecentListWidgetState();
}
class _RecentListWidgetState extends State<RecentListWidget> {
final _recentList = [
RecentGroceryItem(
name: 'Apples',
lastBought: DateTime(2024, 3, 3, 13, 22, 33),
),
RecentGroceryItem(
name: 'Bread',
lastBought: DateTime(2024, 3, 4, 10, 14, 12),
)
].forListbox().toList();
[@override](/user/override)
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
/// 创建更复杂的列表项模板
/// 展示带有日期标记的行
TemplatedListboxItem<RecentGroceryItem> makeItemTemplate(
int index,
ListboxEventManager eventManager,
ListItem<RecentGroceryItem> item,
String label,
void Function(ListItem<RecentGroceryItem>)? onSelect,
bool isDragPlaceholder,
) {
return TemplatedListboxItem(
key: Key('$index'),
item: item,
childTemplate: (context, item) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label),
if (!isDragPlaceholder)
Badge(
label: Text(item.data.lastBoughtShortDtTm),
),
],
),
);
},
eventManager: eventManager,
onSelect: onSelect,
customDecoration: isDragPlaceholder
? BoxDecoration(
color: colorScheme.primaryContainer,
borderRadius: const BorderRadius.all(
Radius.circular(5),
),
)
: null,
);
}
void onSelect(Iterable<ListItem<RecentGroceryItem>> itemsSelected) {
debugPrint(
'Selected: ${itemsSelected.map((e) => e.data.name).join(',')}');
setState(() {
for (var item in _recentList) {
item.isSelected = itemsSelected.contains(item);
}
});
}
void onDrop(Iterable<ListItem<RecentGroceryItem>> itemsDropped, int index) {
debugPrint(
'Dropped items ${itemsDropped.map((e) => e.data.name).join(',')} into index $index');
// 避免添加重复项
final existingNames = _recentList.map((i) => i.data.name);
// 将拖拽的项插入到列表中
final itemsToInsert = itemsDropped
.where((i) => !existingNames.contains(i.data.name))
.toList();
_recentList.insertAll(index, itemsToInsert);
}
return Flexible(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Recently Bought',
style: Theme.of(context).textTheme.displaySmall,
),
const Text('Features: Single-select, Customized Template, Drag To'),
Expanded(
child: Builder(
builder: (context) {
return Listbox(
// 仅在调试时需要键来标识交互的列表
key: const Key('RecentList'),
items: _recentList,
onSelect: onSelect,
onDrop: onDrop,
dragDropTransform: (input) {
if (input is GroceryItem || input is RecentGroceryItem) {
return RecentGroceryItem(
name: input.name, lastBought: DateTime.now());
} else {
throw Exception(
'Cannot accept items of type ${input.runtimeType}');
}
},
itemTemplate: (context, eventManager, index, item, onSelect) =>
makeItemTemplate(index, eventManager, item, item.data.name, onSelect, false),
dropPlaceholderTemplate: (context, eventManager, index, item, itemsToBeDropped) {
var itemsLength = itemsToBeDropped.length;
// 根据拖拽的项数量调整占位符标签
var label = itemsLength > 1
? '$itemsLength new items...'
: itemsLength > 0
? itemsToBeDropped.first.data.name
: '';
return makeItemTemplate(
index, eventManager, item, label, null, true);
},
// 关闭多选功能
disableMultiSelect: true,
// 启用调试信息
enableDebug: true,
);
},
),
),
],
),
),
);
}
}
更多关于Flutter可选中拖拽列表插件selectable_draggable_listbox的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter可选中拖拽列表插件selectable_draggable_listbox的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,selectable_draggable_listbox
是一个在 Flutter 中实现可选中拖拽列表的插件。下面是一个简单的代码示例,展示如何使用这个插件来创建一个可选中且可拖拽的列表。
首先,确保你已经在 pubspec.yaml
文件中添加了 selectable_draggable_listbox
依赖:
dependencies:
flutter:
sdk: flutter
selectable_draggable_listbox: ^最新版本号 # 请替换为实际的最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来,是一个示例代码,展示如何使用 SelectableDraggableListBox
:
import 'package:flutter/material.dart';
import 'package:selectable_draggable_listbox/selectable_draggable_listbox.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Selectable Draggable ListBox Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> items = List<String>.generate(10, (i) => "Item $i");
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Selectable Draggable ListBox Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: SelectableDraggableListBox<String>(
items: items,
selectedColor: Colors.lightBlueAccent,
unselectedColor: Colors.white,
selectedTextStyle: TextStyle(color: Colors.white),
unselectedTextStyle: TextStyle(color: Colors.black),
onSelectedItemsChanged: (selectedItems) {
setState(() {
// 这里可以处理选中的项目,比如保存到状态或者进行其他逻辑处理
print("Selected items: $selectedItems");
});
},
onDragEnded: (fromIndex, toIndex, item) {
setState(() {
// 更新列表中的项目顺序
if (fromIndex < toIndex) {
items.insert(toIndex, items.removeAt(fromIndex));
} else if (fromIndex > toIndex) {
items.insert(toIndex, items.removeAt(fromIndex));
}
// 这里可以添加其他逻辑处理,比如更新数据源
});
},
),
),
);
}
}
代码解释
- 依赖导入:确保导入了
selectable_draggable_listbox
插件。 - 主应用结构:定义了一个简单的 Flutter 应用,包含一个
MaterialApp
和一个MyHomePage
页面。 - 状态管理:
MyHomePage
是一个有状态组件,用于管理列表项的状态。 - 列表数据:
items
列表包含了初始的10个项目。 - UI构建:
- 使用
SelectableDraggableListBox
组件来构建可选中且可拖拽的列表。 selectedColor
和unselectedColor
用于设置选中项和未选中项的背景颜色。selectedTextStyle
和unselectedTextStyle
用于设置选中项和未选中项的文本样式。onSelectedItemsChanged
回调用于处理选中项的变化。onDragEnded
回调用于处理拖拽结束后的逻辑,包括更新列表的顺序。
- 使用
这样,你就可以创建一个可选中且可拖拽的列表,并处理用户交互事件。根据实际需求,你可以进一步扩展和定制这个示例。