當你在使用ListView創建列表時,是否曾經想過要添加排序功能?
ReorderableListView是Flutter中的一個小部件,允許用戶通過拖放列表項目來重新排序。
這次我們來介紹ReorderableListView及其可設置的屬性。
- 基本的ReorderableListView使用方法
- ReorderableListView的屬性
- itemBuilder(必需)
- itemCount(必需)
- onReorder(必需)
- itemExtent(可選)
- prototypeItem(可選)
- proxyDecorator(可選)
- buildDefaultDragHandles(可選)
- padding(可選)
- header(可選)
- footer(可選)
- scrollDirection(可選)
- reverse(可選)
- scrollController(可選)
- primary(可選)
- physics(可選)
- shrinkWrap(可選)
- anchor(可選)
- cacheExtent(可選)
- dragStartBehavior(可選)
- keyboardDismissBehavior(可選)
- restorationId(可選)
- clipBehavior(可選)
- 總結
基本的ReorderableListView使用方法
以下是基本的ReorderableListView實現代碼。
在下面的示例中,可以通過拖動列表中的項目來更改其順序。
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'ReorderableListView Example',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<String> _items = List.generate(20, (index) => "Item $index");
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('ReorderableListView Example'),
),
body: ReorderableListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(_items[index]),
title: Text(_items[index]),
trailing: const Icon(Icons.drag_handle),
);
},
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final item = _items.removeAt(oldIndex);
_items.insert(newIndex, item);
});
},
),
);
}
}
使用ReorderableListView.builder顯示20個列表項目。
ReorderableListView的屬性
ReorderableListView有許多屬性可以用來自定義列表。
可用的屬性類似於ListView,但略有不同。
itemBuilder(必需)
itemBuilder是生成每個項目的屬性。
它逐一處理接收到的列表項目並返回顯示的小部件。
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(_items[index]),
title: Text(_items[index]),
trailing: const Icon(Icons.drag_handle),
);
},
itemCount(必需)
itemCount是指定列表項目數量的屬性。
在ReorderableListView中,itemCount屬性是必需的。
itemCount: _items.length,
onReorder(必需)
當列表項目被重新排列時調用的回調函數。
onReorder在用戶放置項目時執行。
以下是基本的onReorder處理,為了便於說明,改變了代碼順序。
onReorder: (oldIndex, newIndex) {
setState(() {
// 從列表中刪除並獲取oldIndex的項目
final item = _items.removeAt(oldIndex);
// 由於刪除後索引會發生變化,如果newIndex大於oldIndex,需要減1
if (newIndex > oldIndex) {
newIndex -= 1;
}
// 將剛剛刪除的項目插入到新的索引位置
_items.insert(newIndex, item);
});
},
itemExtent(可選)
可以指定每個項目的固定高度。
設置後,每個項目的高度將統一,從而提高滾動性能。默認值為null。
itemExtent: 30.0,
prototypeItem(可選)
列表的佈局基於提供的小部件進行計算。
這可以減少佈局計算的開銷。
適用於固定大小(內容)的列表。
prototypeItem: ListTile(
title: Text('Sample Item'),
),
proxyDecorator(可選)
可以更改拖動中項目的外觀。
以下示例將拖動中的項目設置為半透明。
proxyDecorator: (child, index, animation) {
return Material(
elevation: 6.0,
color: Colors.transparent,
shadowColor: Colors.black,
child: child,
);
},
buildDefaultDragHandles(可選)
可以指定是否使用默認的拖動手柄。
以下示例不使用默認手柄,而是使用ReorderableDragStartListener進行處理。
buildDefaultDragHandles: false,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(_items[index]),
title: Text(_items[index]),
trailing: ReorderableDragStartListener(
index: index,
child: Icon(Icons.drag_handle),
),
);
},
padding(可選)
這是許多小部件都有的熟悉屬性。為整個列表添加內邊距。
可以如下設置。
padding: const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
header(可選)
顯示在列表開頭的小部件。可用於設置列表標題等。
由於是列表的一個元素,滾動時會消失。
如果想要始終顯示,需要在列表前添加小部件或使用SliverList。
header: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'列表的頭部',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
footer(可選)
顯示在列表末尾的小部件。用法與header相同。
footer: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'列表的尾部',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
scrollDirection(可選)
scrollDirection指定列表的滾動方向。
可以設置為垂直方向(Axis.vertical)或水平方向(Axis.horizontal)。
默認為垂直方向(Axis.vertical)。
scrollDirection: Axis.horizontal,
reverse(可選)
reverse可以將列表的排列順序反轉。
設置為true時,列表的顯示順序將反轉。默認為false。
reverse:true,
scrollController(可選)
可以從程序中細微控制列表視圖的滾動。
scrollController: _scrollController,
以下是按下Top按鈕後移動到頂部的代碼。
class _MainState extends State<HomePage> {
final List<String> _items = List.generate(20, (index) => "Item $index");
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
_scrollController.addListener(() {
print("Scroll position: ${_scrollController.position.pixels}");
});
}
void _scrollToTop() {
_scrollController.animateTo(
0.0,
duration: Duration(seconds: 1),
curve: Curves.easeInOut,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('回到頂部'),
actions: [
IconButton(
icon: Icon(Icons.arrow_upward),
onPressed: _scrollToTop,
),
],
),
body: ReorderableListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(_items[index]),
title: Text(_items[index]),
trailing: ReorderableDragStartListener(
index: index,
child: Icon(Icons.drag_handle),
),
);
},
scrollController: _scrollController,
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
final item = _items.removeAt(oldIndex);
_items.insert(newIndex, item);
});
},
),
);
}
}
primary(可選)
例如在嵌套滾動視圖的情況下,為子列表設置「primary: false」,可以優先滾動子列表。
如果不設置,會優先滾動父列表,導致子列表無法順暢滾動。默認為true。
primary: false,
physics(可選)
可以設置滾動的行為。
標準的滾動類如下。默認為ClampingScrollPhysics。
- BouncingScrollPhysics:
到達列表邊緣時,滾動反彈 - ClampingScrollPhysics:
到達列表邊緣時,滾動固定 - FixedExtentScrollPhysics:
滾動按固定高度定位 - NeverScrollableScrollPhysics:
無法滾動 - PageScrollPhysics:
類似於頁面視圖的滾動,與橫向滾動結合使用效果更佳
physics: BouncingScrollPhysics(),
shrinkWrap(可選)
shrinkWrap指定列表是否只取需要的大小。
設置為true時,列表將根據其內容決定自身的高度。默認為false。
shrinkWrap: true,
anchor(可選)
可以設置列表的顯示位置。
設置後,第一個項目上方會有空間。範圍為0~1。
(不太清楚什麼時候會用到這個屬性…)
anchor: 0.1,
cacheExtent(可選)
可以控制滾動視圖在顯示範圍外緩存多少小部件。
默認情況下,緩存量很少,因此可能需要使用該屬性來改善滾動性能。
但是,默認情況下也很快,所以對於少量且小尺寸的列表可能不太需要。
dragStartBehavior(可選)
可以控制拖動操作的觸摸事件開始時的行為。
- DragStartBehavior.start:
拖動操作從用戶開始移動手指的瞬間開始
動畫較為流暢。默認為此設置。 - DragStartBehavior.down:
從檢測到向下事件的位置開始
反應較為靈敏
dragStartBehavior: DragStartBehavior.down,
不過實際感覺兩者差不多。根據需求設置即可。
keyboardDismissBehavior(可選)
可以設置當用戶滾動時是否關閉鍵盤。
- ScrollViewKeyboardDismissBehavior.manual:
用戶滾動時,鍵盤不會自動關閉
默認設置為此選項 - ScrollViewKeyboardDismissBehavior.onDrag:
用戶滾動時,鍵盤會自動關閉
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
restorationId(可選)
可以設置恢復列表狀態的ID。
保存滾動位置和輸入內容等,用戶會話中的狀態,
在應用重新啟動或重新生成時恢復該狀態。默認未設置。
詳情請參考Flutter官方(鏈接)。
restorationId: 'reorderable_list', // 使用該ID來實現恢復等處理
clipBehavior(可選)
用於控制超出小部件邊界的部分如何被剪切。
默認設置為Clip.hardEdge。
- Clip.none:
允許繪製超出父邊界的部分 - Clip.hardEdge:
超出父邊界的部分不會顯示 - Clip.antiAlias:
對超出父邊界的部分進行抗鋸齒處理(使重疊部分更好看)
邊緣更平滑,但可能影響性能 - Clip.antiAliasWithSaveLayer:
對超出父邊界的部分進行抗鋸齒處理
提供高質量的渲染,但性能開銷較高
clipBehavior: Clip.antiAlias,
Clip.antiAlias和Clip.antiAliasWithSaveLayer對文本列表可能效果不明顯,
但對包含圖片或圖表的列表可能有效。
總結
介紹了可重新排序的ReorderableListView.builder。
通過設置屬性,可以大大自定義其行為。
了解有哪些屬性可以幫助你擴展實現的範圍。