The principle and use of Flutter Key (5) An example that requires a key: a Listview that can be dragged to change the order

16051795:

The principle and use of Flutter Key (1) What happens without Key

The principle and use of Flutter Key (2) Correspondence between Widget and Element

The principle and use of Flutter Key (3) Three types of LocalKey

Principle and use of Flutter Key (4) Usage of GlobalKey

The principle and use of Flutter Key (5) An example that requires a key: a Listview that can be dragged to change the order

Before, we introduced several keys in flutter, their corresponding principles and usage methods. This time, let’s review and see an example that needs to use keys.

ReorderableListView

You've probably used ListViewthe component, which can resize, display and scroll a list of items. But one thing it can't do is move items in the list. Luckily, there is ReorderableListView.

ReorderableListViewIt is not an uncommonly used ListView component. It is one 用户可以通过拖动来重新排序其项目的列表组件. With it, we can move it in the scrolling direction of the ListView by long-pressing the item, and put it in a new position.

It has one requirement: all list items must have a Key
and must implement onReorderthe method, which is the callback method called by the user when reordering the list. Take a look at the effect of not implementing this callback:

ReorderableListView(
  children: [
    Box(Colors.blue, key: ValueKey(1)),
    Box(Colors.green, key: ValueKey(2)),
    Box(Colors.red, key: _globalKey),
  ],
  onReorder: (oldIndex, newIndex) {
    print('从位置$oldIndex移动到$newIndex');
  },
)

a.gif

It can be seen that although we have exchanged the red and green boxes, the UI has not changed after letting go, because it has not been realized onReorder.

onReorder: (oldIndex, newIndex), there are two parameters, oldIndex and newIndex, which represent the index of the dragged widget before and after dragging. Take a look at the print results:

move from position 2 to 1

The explanation is that it has moved from the second position to the first position (at the beginning it is the 0th position).
Whenever such a change occurs, we need to modify the list component. Modify
our code and remove the old position widget, insert it at the new location:

final boxList = [
  Box(Colors.blue, key: ValueKey(1)),
  Box(Colors.green, key: ValueKey(2)),
  Box(Colors.red, key: ValueKey(3)),
];

Widget listWidget() {
  return ReorderableListView(
    children: boxList,
    onReorder: (oldIndex, newIndex) {
      print('从位置$oldIndex移动到$newIndex');
      final box = boxList.removeAt(oldIndex);
      boxList.insert(newIndex, box);
    },
  );
}

asd.gif

This achieves our goal of moving the widget.
But in fact, when moving the widget from top to bottom, there is a problem. Let me move it again and let’s take a look:

asdasd.gif

Corresponding printing: Move from position 0 to 2.
Just look at the reason:

index widget
0 box1
1 box2
2 box2

The order we moved just now is to move box1 to the back of box2, which is reflected here from 0 to behind box2, that is to say, to the position of 2, because the position of box2 is 1. I don’t know if you understand.
Of course, if you move from the starting position to the end, an array out-of-bounds error will occur:

════════ Exception caught by animation library ═════════════════════════════════════════════════════
The following RangeError was thrown while notifying status listeners for AnimationController:
Invalid value: Not in inclusive range 0..2: 3

When the exception was thrown, this was the stack: 
#0      List.insert (dart:core-patch/growable_array.dart:11:7)
#1      _MyHomePageState.listWidget.<anonymous closure> (package:flutter_key/home_page.dart:53:17)
#2      SliverReorderableListState._dropCompleted (package:flutter/src/widgets/reorderable_list.dart:646:24)
#3      _DragInfo._dropCompleted (package:flutter/src/widgets/reorderable_list.dart:1163:22)
#4      _DragInfo.startDrag.<anonymous closure> (package:flutter/src/widgets/reorderable_list.dart:1134:9)
...
The AnimationController notifying status listeners was: AnimationController#aca1a(⏮ 0.000; paused; DISPOSED)
════════════════════════════════════════════════════════════════════════════════════════════════════
 

So when we move down, we have to do extra processing, the new index needs to be decremented by 1, and then delete and insert:

if(newIndex>oldIndex){
  newIndex --;
}

Horizontal list, that is, the operation to the right needs to be processed

Disadvantages of ReorderableListView

After dragging a few times, I found several shortcomings:

  • Long press to trigger dragging, easy to touch by mistake.
  • ReorderableListView is still a Listview, which means it can scroll. When the list is long and can be scrolled, there will be many misoperations.
  • For one-dimensional ListView, we all know that listview can only slide in the scrolling direction, and the same is true for ReorderableListView, which cannot be dragged back and forth up, down, left, and right.

In fact, we can implement such a component by ourselves.
If we drag, we can Draggableimplement it through it. It is a widget that supports drag and drop.
In order to avoid scrolling, we can use Columnand Row.

When there is time later, a list component that supports dragging will be implemented to better achieve this effect.

Guess you like

Origin blog.csdn.net/u011272795/article/details/120156732