как добавить еще один переупорядочиваемый вид списка во флаттере - PullRequest
0 голосов
/ 30 апреля 2020

Я изучаю 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);
    });
  }
}

1 Ответ

1 голос
/ 30 апреля 2020

Этого можно добиться, установив атрибут children виджета ExpansionTile.

Подход заключается в следующем.

  1. Вам необходим общий обработчик данных или какое-либо управление состоянием, чтобы сохранить состояние родителя и потомков за пределами виджета, чтобы избежать перестроений при изменении дочернего списка. Для краткости я использую синглтон для хранения общих данных. В реальном случае это должен быть подход, основанный на ChangeNotifier или BLoc. Однако, если вы изменяете родительский или дочерний список, вам нужна полная перестройка, потому что виджеты 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;
  }
}
Создайте виджет, который возвращает дочерний элемент ReorderableListView с уникальным ScrollController для каждого из этих виджетов. Например, ReorderList виджет. Это почти идентично тому, что вы написали, за исключением того, что я возвращаю 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(() {});
  }
}


В родительском виджете ExpansionTile установите для этого нового виджета один из children. Этот дочерний элемент и родительский элемент созданы из значения одноэлементного класса DataHolder.

Примечание. Я устанавливаю постоянную высоту, чтобы избежать конфликтов макета. Вы должны поиграть с этим для динамических c размеров.

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 доступно полностью рабочее решение. Я изменил код, чтобы динамически принимать список элементов из родительского виджета. Вам придется поиграть с тем, как сохранить данные и сократить перестроения. Но в целом ребенок ReorderableListView работает, пока поддерживается список.

Надеюсь, это поможет.

...