【簡單】可重新排序的列表ReorderableListView及其屬性

title:【簡單】可重新排序的列表ReorderableListView及其屬性

當你在使用ListView創建列表時,是否曾經想過要添加排序功能?
ReorderableListView是Flutter中的一個小部件,允許用戶通過拖放列表項目來重新排序。

這次我們來介紹ReorderableListView及其可設置的屬性。

基本的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.antiAliasClip.antiAliasWithSaveLayer對文本列表可能效果不明顯,
但對包含圖片或圖表的列表可能有效。

總結

介紹了可重新排序的ReorderableListView.builder。
通過設置屬性,可以大大自定義其行為。
了解有哪些屬性可以幫助你擴展實現的範圍。

标题和URL已复制