美文网首页
Flutter可拖动组件

Flutter可拖动组件

作者: 倪大头 | 来源:发表于2022-03-19 15:39 被阅读0次

    先上效果:


    9mwns-e5tn0.gif

    布局是Stack和Position,可拖动的子组件使用GestureDetector的onPanUpdate更新offset,代码里使用了getx组件做状态监听,也可以不用,在onPanUpdate里调用setState刷新组件即可。

    组件代码:
    参数有四个,child是可拖动的组件,stackKey是外层Stack的globalkey,offsetX和offsetY是可拖动组件的初始坐标

    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    
    class MoveAbleWidget extends StatefulWidget {
      final Widget child;
      final GlobalKey stackKey; // stack组件的key
      final double offsetX; // 组件初始位置X坐标
      final double offsetY; // 组件初始位置Y坐标
    
      const MoveAbleWidget(
          {Key? key,
          required this.child,
          required this.stackKey,
          this.offsetX = 0,
          this.offsetY = 0})
          : super(key: key);
    
      @override
      State<MoveAbleWidget> createState() => _MoveAbleWidgetState();
    }
    
    class _MoveAbleWidgetState extends State<MoveAbleWidget> {
      // stack组件宽度
      late double stackWidth;
      // stack组件高度
      late double stackHeight;
    
      // child组件key
      final GlobalKey _childKey = GlobalKey();
      // child组件宽度
      late double childWidth;
      // child组件高度
      late double childHeight;
    
      // child组件位置
      late Offset initialOffset;
      var xPosition = 0.0.obs;
      var yPosition = 0.0.obs;
    
      @override
      void initState() {
        super.initState();
    
        // 绘制完毕监听
        WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
          // 获取stack宽高
          RenderBox? stackRenderBox =
              widget.stackKey.currentContext?.findRenderObject() as RenderBox?;
          stackWidth = stackRenderBox?.size.width ?? 0;
          stackHeight = stackRenderBox?.size.height ?? 0;
    
          // 获取child宽高
          RenderBox? childRenderBox =
              _childKey.currentContext?.findRenderObject() as RenderBox?;
          childWidth = childRenderBox?.size.width ?? 0;
          childHeight = childRenderBox?.size.height ?? 0;
    
          // 可拖动组件的初始位置
          Offset newOffset = configOffset(widget.offsetX, widget.offsetY);
          xPosition.value = newOffset.dx;
          yPosition.value = newOffset.dy;
        });
      }
    
      // 设置可拖动组件位置不超出父组件范围
      Offset configOffset(double childX, double childY) {
        double newX = childX;
        double newY = childY;
        // 左边界
        if (newX < 0) {
          newX = 0;
        }
        // 右边界
        if (newX > (stackWidth - childWidth)) {
          newX = stackWidth - childWidth;
        }
        // 上边界
        if (newY < 0) {
          newY = 0;
        }
        // 下边界
        if (newY > (stackHeight - childHeight)) {
          newY = stackHeight - childHeight;
        }
    
        return Offset(newX, newY);
      }
    
      @override
      Widget build(BuildContext context) {
        return Obx(() {
          return Positioned(
            top: yPosition.value,
            left: xPosition.value,
            child: GestureDetector(
              child: Container(
                key: _childKey,
                child: widget.child,
              ),
              onPanUpdate: (DragUpdateDetails details) {
                // 拖动不允许超过父组件范围
                Offset newOffset = configOffset(
                  xPosition.value + details.delta.dx,
                  yPosition.value + details.delta.dy,
                );
                xPosition.value = newOffset.dx;
                yPosition.value = newOffset.dy;
                // print('x: ${xPosition.value}  y: ${yPosition.value}');
              },
            ),
          );
        });
      }
    }
    

    使用:

    return Container(
      margin: EdgeInsets.all(50),
      color: Colors.lightBlueAccent,
      child: Stack(
        key: stackKey,
        children: [
          MoveAbleWidget(
            offsetX: 100,
            offsetY: 100,
            stackKey: stackKey,
            child: Container(
              width: 40,
              height: 40,
              color: Colors.amber,
            ),
          ),
        ],
      ),
    );
    

    需要在可拖动组件外面套一层Stack组件,并且给Stack组件设置一个globalkey,再传给MoveAbleWidget。

    相关文章

      网友评论

          本文标题:Flutter可拖动组件

          本文链接:https://www.haomeiwen.com/subject/iyagdrtx.html