asjqkkkk / circle_list

A new Flutter package for circle list.
Other
36 stars 13 forks source link

Determine child being dragged? #12

Open jonjon1123 opened 3 years ago

jonjon1123 commented 3 years ago

Is it possible to determine the index of children that is being dragged?

asjqkkkk commented 3 years ago

According to the current implementation, this function is not yet supported

jonjon1123 commented 3 years ago

Any hints on how to go about determining this? I am happy to try and implement this, but I will prob need some guidance.

asjqkkkk commented 3 years ago

Hi, now GestureDetector is parent widget for Stack, and the children of CircleList is actually Stack's children; If you want to know which child is dragged, maybe you need to wrap GestureDetector for every child

jonjon1123 commented 3 years ago

Yeah, I tried wrapping GestureDetector around each child, but there is a weird delay in when it fires and sets the state for which child is being dragged. It seems it would be better if the CircleList reported back which child/index is being dragged. The challenge I see with that is it doesn't look like CircleList really knows that information easily. It seems some kind of math/geometry would have to be done to determine where the user touched the screen and if that point falls in the bounds of one of the children. With that said, I think that math/geometry is beyond my ability. :)

jonjon1123 commented 3 years ago

I think I may have a workable solution to this.

First, I changed the following so we can get access to DragStartDetails:

typedef RadialDragStart = Function(PolarCoord startCoord, DragStartDetails details);

I also added this and have it returned from onDragStart in CircleList:

typedef RadialDragStartWithIndex = Function(PolarCoord coord, DragStartDetails details, int? index);

Then within the _CircleListState I create a list of keys for the children:

List<GlobalKey> keys = [];

...

for(int i = 0; i < widget.children.length; i++) {
  keys.add(new GlobalKey(debugLabel: i.toString()));
}

When rendering each child in the List.generate, I assign the key from the list we created above:

new Container(
  key: keys[index],
  width: childrenDiameter,
  height: childrenDiameter,
  alignment: Alignment.center,
  child: widget.children[index]
)

Lastly, I added a new function called getChildIndex that uses the globalPosition of the onRadialDragStart tap to determine if the start tap is within the bounds of the child. If so, it returns the index of that child.

int? getChildIndex(Offset globalPosition) {
  for(int i = 0; i < keys.length; i++) {
    // Get the render box for each key
    RenderBox? renderBox = keys[i].currentContext?.findRenderObject() as RenderBox?;
    // Convert the globalPosition of the tap that has been passed in to a local position for this key's render box
    Offset? localOffset = renderBox?.globalToLocal(globalPosition);
    // Check if the render box's boundaries contain the position of the tap that has been converted to this render boxes local position.
    // If it does, then the user is tapping within this child's render box and therefore we can return this child's index
    if (renderBox != null && localOffset != null && renderBox.paintBounds.contains(localOffset)) {
      return i;
    }
  }
}

I call getChildIndex like so within onRadialDragStart in CircleList:

int? index = getChildIndex(details.globalPosition);
asjqkkkk commented 3 years ago

I think I may have a workable solution to this.

First, I changed the following so we can get access to DragStartDetails:

typedef RadialDragStart = Function(PolarCoord startCoord, DragStartDetails details);

I also added this and have it returned from onDragStart in CircleList:

typedef RadialDragStartWithIndex = Function(PolarCoord coord, DragStartDetails details, int? index);

Then within the _CircleListState I create a list of keys for the children:

List<GlobalKey> keys = [];

...

for(int i = 0; i < widget.children.length; i++) {
  keys.add(new GlobalKey(debugLabel: i.toString()));
}

When rendering each child in the List.generate, I assign the key from the list we created above:

new Container(
  key: keys[index],
  width: childrenDiameter,
  height: childrenDiameter,
  alignment: Alignment.center,
  child: widget.children[index]
)

Lastly, I added a new function called getChildIndex that uses the globalPosition of the onRadialDragStart tap to determine if the start tap is within the bounds of the child. If so, it returns the index of that child.

int? getChildIndex(Offset globalPosition) {
  for(int i = 0; i < keys.length; i++) {
    // Get the render box for each key
    RenderBox? renderBox = keys[i].currentContext?.findRenderObject() as RenderBox?;
    // Convert the globalPosition of the tap that has been passed in to a local position for this key's render box
    Offset? localOffset = renderBox?.globalToLocal(globalPosition);
    // Check if the render box's boundaries contain the position of the tap that has been converted to this render boxes local position.
    // If it does, then the user is tapping within this child's render box and therefore we can return this child's index
    if (renderBox != null && localOffset != null && renderBox.paintBounds.contains(localOffset)) {
      return i;
    }
  }
}

I call getChildIndex like so within onRadialDragStart in CircleList:

int? index = getChildIndex(details.globalPosition);

Hi, if I update CircleList's children, maybe the GlobalKey list need be updated too

jonjon1123 commented 3 years ago

Yeah, I ended up passing in the list of keys to CircleList. This does assume that the order of the list of keys is the same as the list of children. I also added a check to ensure the list of keys is the same length as the list of children.