如何在 flutter 中添加另一个可重新排序的列表视图

how to add another reorderable list view in flutter

我正在学习 Flutter,是否可以创建一个可重新排序的列表,其中的项目本身就是可重新排序的列表(下图)。如果有人可以建议我或帮助我,我将不胜感激。谢谢

这是我的代码

class _ReorderItemsState extends State<ReorderItems> {

  List<String> topTen = [
    "EPL",
    "MLS",
    "LLG",
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ReorderableListView(
        onReorder: onReorder,
        children: getListItem(),
      ),
    );
  }

  List<ExpansionTile> getListItem() => topTen.asMap()
  .map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
  .values.toList();

  ExpansionTile buildTenableListTile(String item, int index) => ExpansionTile(
    key: ValueKey(item),
    title: Text(item),
    leading: Icon(Icons.list),
    backgroundColor: Colors.blue,
  );

  void onReorder(int oldIndex, int newIndex){
    if(newIndex > oldIndex){
      newIndex -=1;
    }
    setState(() {
      String game = topTen[oldIndex];
      topTen.removeAt(oldIndex);
      topTen.insert(newIndex, game);
    });
  }
}

您可以通过设置 ExpansionTile 小部件的 children 属性来实现。

方法如下

  1. 您需要一个通用的 数据处理程序 或某种状态管理来将 parent 和 children 的状态保持在小部件外部以避免当 child 列表更改时重建。为简洁起见,我使用单例来保存公共数据。在实际情况下,这应该是一些基于 ChangeNotifierBLoc 的方法。但是,如果您改变 parent 或 child 列表,则不需要完全重建,因为 Flutter,小部件是不可变的。
/// Holding the common data as a singleton to avoid excessive rebuilds.
/// Usually this should be replaced with a manager or bloc or changenotifier class
class DataHolder {
  List<String> parentKeys;

  Map<String, List<String>> childMap;

  DataHolder._privateConstructor();

  static final DataHolder _dataHolder = DataHolder._privateConstructor();

  static DataHolder get instance => _dataHolder;

  factory DataHolder.initialize({@required parentKeys}) {
    _dataHolder.parentKeys = parentKeys;
    _dataHolder.childMap = {};
    for (String key in parentKeys) {
      _dataHolder.childMap.putIfAbsent(
          key, () => ['${key}child_1', '${key}child_2', '${key}child_3']);
    }
    return _dataHolder;
  }
}
  1. 创建一个小部件,其中 return 是 child ReorderableListView,每个小部件都具有唯一 ScrollController。例如ReorderList 小部件。除了我 return 一个 ListTile 而不是 ExpansionTile 并设置 scrollController 属性外,它几乎与您写的完全相同。当前的稳定版本没有这个属性。因此,在此解决方案中,它用 PrimaryScrollController 小部件包装以避免重复使用 scrollController。

class ReorderList extends StatefulWidget {
  final String parentMapKey;
  ReorderList({this.parentMapKey});
  @override
  _ReorderListState createState() => _ReorderListState();
}

class _ReorderListState extends State<ReorderList> {
  @override
  Widget build(BuildContext context) {
    return PrimaryScrollController(
      controller: ScrollController(),
      child: ReorderableListView(
        // scrollController: ScrollController(),
        onReorder: onReorder,
        children: DataHolder.instance.childMap[widget.parentMapKey]
            .map(
              (String child) => ListTile(
                key: ValueKey(child),
                leading: Icon(Icons.done_all),
                title: Text(child),
              ),
            )
            .toList(),
      ),
    );
  }

  void onReorder(int oldIndex, int newIndex) {
    if (newIndex > oldIndex) {
      newIndex -= 1;
    }
    List<String> children = DataHolder.instance.childMap[widget.parentMapKey];
    String game = children[oldIndex];
    children.removeAt(oldIndex);
    children.insert(newIndex, game);
    DataHolder.instance.childMap[widget.parentMapKey] = children;
    // Need to set state to rebuild the children.
    setState(() {});
  }
}


  1. 在 Parent ExpansionTile 小部件中将此新小部件设置为 children 之一。这个 children 和 parent 都是从 DataHolder 单例 class 的值构建的。

    Note I am setting a constant height to avoid conflicts of layout. You have to play with this for dynamic sizes.

class ReorderItems extends StatefulWidget {
  final List<String> topTen;
  ReorderItems({this.topTen});
  @override
  _ReorderItemsState createState() => _ReorderItemsState();
}

class _ReorderItemsState extends State<ReorderItems> {
  @override
  void initState() {
    super.initState();
    // initialize the children for the Expansion tile
    // This initialization can be replaced with any logic like network fetch or something else.
    DataHolder.initialize(parentKeys: widget.topTen);
  }

  @override
  Widget build(BuildContext context) {
    return PrimaryScrollController(
      key: ValueKey(widget.topTen.toString()),
      controller: ScrollController(),
      child: ReorderableListView(
        onReorder: onReorder,
        children: getListItem(),
      ),
    );
  }

  List<ExpansionTile> getListItem() => DataHolder.instance.parentKeys
      .asMap()
      .map((index, item) => MapEntry(index, buildTenableListTile(item, index)))
      .values
      .toList();

  ExpansionTile buildTenableListTile(String mapKey, int index) => ExpansionTile(
        key: ValueKey(mapKey),
        title: Text(mapKey),
        leading: Icon(Icons.list),
        backgroundColor: Colors.blue,
        children: [
          Container(
            key: ValueKey('$mapKey$index'),
            height: 400,
            child: Container(
              padding: EdgeInsets.only(left: 20.0),
              child: ReorderList(
                parentMapKey: mapKey,
              ),
            ),
          ),
        ],
      );

  void onReorder(int oldIndex, int newIndex) {
    if (newIndex > oldIndex) {
      newIndex -= 1;
    }
    setState(() {
      String game = DataHolder.instance.parentKeys[oldIndex];
      DataHolder.instance.parentKeys.removeAt(oldIndex);
      DataHolder.instance.parentKeys.insert(newIndex, game);
    });
  }
}

codepen 中提供了一个完整的解决方案。 我更改了代码以从 parent 小部件动态接受项目列表。您将不得不尝试如何维护数据和减少重建。但一般来说,只要维护列表,child ReorderableListView 就可以工作。

希望这对您有所帮助。