Flutter--可拖拽组件封装,支持拖动到列表边缘时图自动滚动!!!基于EdgeDraggingAutoScroller

已知问题:使用layout builder包裹获取子组件大小,会较大概率出现卡死,建议包裹父组件计算后传递进来;首尾滚动速度不一致;

核心组件,把这个套在你原有的listView或者GirdView等可滚动组件的child

class DragTargetView extends StatefulWidget {

  final T data;
  final Widget Function(BuildContext contex) itemBuilder;
  final Widget feedback;
  final Function(T moveData, T toData) exchangeItem;
  final DragTargetAccept? onAccept;
  final VoidCallback? onDragStarted;
  final DraggableCanceledCallback? onDraggableCanceled;

  const DragTargetView({
    super.key,
    required this.data,
    required this.itemBuilder,
    required this.feedback,
    required this.exchangeItem,
    this.onDragStarted,
    this.onDraggableCanceled,
    this.onAccept
  });

  @override
  State createState() => _DragTargetViewState();

}

class _DragTargetViewState extends State> {

  EdgeDraggingAutoScroller? autoScroller;

  late ScrollableState scrollable;
  Axis get scrollDirection  => axisDirectionToAxis(scrollable.axisDirection);
  bool get reverse          => scrollable.axisDirection == AxisDirection.up || scrollable.axisDirection == AxisDirection.left;

  Offset  dragPosition  = Offset.zero;
  Offset  dragOffset    = Offset.zero;

  @override
  Widget build(BuildContext context) {
    double maxHeight = 109;
    double maxWidth  = 109;

    scrollable = Scrollable.of(context)!;
    autoScroller = EdgeDraggingAutoScroller(
      scrollable,
      onScrollViewScrolled: () {
        autoScroller?.startAutoScrollIfNecessary(dragTargetRect(maxWidth, maxHeight));
      }
    );

    return LongPressDraggable(
      data: widget.data,
      feedback: widget.feedback,
      childWhenDragging: null,
      onDragStarted: widget.onDragStarted,
      onDragUpdate: (DragUpdateDetails details) {
        dragPosition = details.localPosition;
        dragOffset = details.delta;
        autoScroller?.startAutoScrollIfNecessary(dragTargetRect(maxWidth, maxHeight));
      },
      onDraggableCanceled: widget.onDraggableCanceled,
      onDragEnd: (details) {
        autoScroller?.startAutoScrollIfNecessary(dragTargetRect(maxWidth, maxHeight));
      },
      child: DragTarget(
        builder: (BuildContext context, List candidateData, List rejectedData) {
          bool isContains = candidateData.contains(widget.data);
          if (isContains) {
            return Container();
          }
          return widget.itemBuilder(context);
        },
        onWillAccept: (moveData) {
          var accept = moveData != null;
          if (accept) {
            widget.exchangeItem(moveData, widget.data);
          }
          return accept;
        },
        onAccept: widget.onAccept,
      ),
    );

  }


  Rect dragTargetRect(double width, double height) {
    Offset origin = dragPosition - dragOffset;

    if (scrollDirection == Axis.horizontal) {
      origin = Offset(dragPosition.dx < width ? -1 : origin.dx, origin.dy);
    } else if (scrollDirection == Axis.vertical) {
      origin = Offset(origin.dx, dragPosition.dy < height ? -1 : origin.dy);
    }

    print("dragTargetRect: dragPosition= $dragPosition, dragOffset= $dragOffset, origin= $origin");
    return Rect.fromLTWH(origin.dx, origin.dy, width, height);
  }
}

使用示范

这里构建了一个可拖拽滚动列表

class SingleDragGridView extends StatefulWidget {

  final List dataList;
  final Widget Function(BuildContext contex, int index, T data) itemBuilder;
  final SliverGridDelegate gridDelegate;
  final Function(List newList) onChanged;

  final ScrollPhysics? physics;
  final bool? shrinkWrap;
  final Axis? scrollDirection;
  final EdgeInsetsGeometry? padding;

  const SingleDragGridView({
    super.key,
    required this.gridDelegate,
    required this.itemBuilder,
    required this.dataList,
    required this.onChanged,
    this.physics,
    this.shrinkWrap,
    this.scrollDirection,
    this.padding
  });

  @override
  State createState() => _SingleDragGridViewState();
}

class _SingleDragGridViewState extends State> {

  List dataList = [];

  @override
  void initState() {
    super.initState();
    dataList.addAll(widget.dataList);
  }

  @override
  void didUpdateWidget(covariant SingleDragGridView oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (dataList != widget.dataList) dataList = [...widget.dataList];
  }

  @override
  Widget build(BuildContext context) {

    return GridView.builder(
      padding: widget.padding,
      gridDelegate: widget.gridDelegate,
      physics: widget.physics,
      shrinkWrap: widget.shrinkWrap ?? true,
      scrollDirection: widget.scrollDirection ?? Axis.vertical,
      itemBuilder: (context, index) {
        return draggableItem(context, index);
      },
      itemCount: dataList.length,
    );
  }


  Widget draggableItem(BuildContext context, int index) {
    T data = dataList[index];
    return DragTargetView(
        data: data,
        itemBuilder: (context) => widget.itemBuilder(context, index, data),
        feedback: Transform(
          transform: Matrix4.identity()..scale(1.1, 1.1),
          alignment: Alignment.center,
          child: widget.itemBuilder(context, index, data),
        ),
        exchangeItem: exchangeItem,
    );
  }


  // 重新排序
  void exchangeItem(T moveData, T toData) {
    setState(() {
      int toIndex = dataList.indexOf(toData);
      dataList.remove(moveData);
      dataList.insert(toIndex, moveData);
    });
    widget.onChanged(dataList);
  }

}

最终

  @override
  Widget build(BuildContext context) {
    List list = [];

    for (int i = 0;i<20;i++) {
      list.add(i);
    }

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SingleDragGridView(
        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            mainAxisSpacing: 5.0,
            crossAxisSpacing: 5.0,
            childAspectRatio: 1),
        itemBuilder: (context, index, data) {
          return Material(
            child: Container(
              width: 100,
              height: 100,
              color: Colors.green,
              child: Text("$data"),
            ),
          );
        },
        dataList: list,
        onChanged: (newList) {

        }
      )
    );

  }

你可能感兴趣的:(flutter开发,flutter,android,1024程序员节)