Flutter реализует произвольное перетаскивание элементов управления


предисловие

Использование флаттер-разработки требует элементов управления, которые можно перетаскивать, таких как элементы на чертежной доске или панели инструментов и поля поиска.Осуществить перетаскивание для каждого в отдельности довольно проблематично.Функция перетаскивания инкапсулирована в элемент управления, который можно использовать напрямую когда это необходимо.Гораздо удобнее перетаскивать элемент управления в качестве родительского элемента управления.


1. Как этого добиться?

1. Используйте GestureDetector для реагирования на события перетаскивания

//总位移
var _unlimtedOffset = Offset.zero;
//当前位移(有活动区域限制时,鼠标超过边界后当前位移不等于总位移,此时总位移可以确保回到边界内鼠标与控件的相对位置不变)
final _offset = ValueNotifier<Offset>(Offset.zero);
GestureDetector(
  child: this.widget.child,
   onPanUpdate: (detail) {
    
    
       //累加拖动距离
      _unlimtedOffset += detail.delta;
 }
)

2. Используйте Transform, чтобы преобразовать положение элемента управления.

Используйте перевод, чтобы изменить положение

//ValueListenableBuilder监听_offset 改变,此处略
Transform.translate(
    offset: offset,
    child:GestureDetector()//上一步的child:GestureDetector
)

3. Рассчитайте площадь перетаскивания

Этот шаг необязателен, но необходим, если вам нужно ограничить диапазон контрольных действий.
Получите размер элемента управления через GlobalKey в событии onPanUpdate GestureDetector:

onPanUpdate: (detail) {
    
    
   //拖动区域为父控件,去掉则不受限制,但拖出父控件会被遮挡无法点击。
   //获取父控件大小
   RenderBox ? parentRenderBox = _mykey.currentContext
   ? .findAncestorRenderObjectOfType<RenderObject>() as RenderBox ? ;
   final screenSize = parentRenderBox ? .size;
   //获取控件大小
   final mySize = _mykey.currentContext ? .size;
   final renderBox =
   _mykey.currentContext ? .findRenderObject() as RenderBox ? ;
   //获取控件当前位置    
   var originOffset = renderBox ? .localToGlobal(Offset.zero);
   if (originOffset != null) {
    
    
   	originOffset = parentRenderBox ? .globalToLocal(originOffset);
   }
   if (screenSize == null || mySize == null || originOffset == null) {
    
    
   	return;
   }
   //计算不超出父控件区域
   if (off.dx < -originOffset.dx) {
    
    
   	off = Offset(-originOffset.dx, off.dy);
   }
   else if (off.dx >
   	screenSize.width - mySize.width - originOffset.dx) {
    
    
   	off = Offset(
   		screenSize.width - mySize.width - originOffset.dx,
   		off.dy,
   		);
   }
   if (off.dy < -originOffset.dy) {
    
    
   	off = Offset(off.dx, -originOffset.dy);
   }
   else if (off.dy >
   	screenSize.height - mySize.height - originOffset.dy) {
    
    
   	off = Offset(
   		off.dx,
   		screenSize.height - mySize.height - originOffset.dy,
   		);
   }
   //现在活动区域为父控件 --end
}

2. Полный код

drag_move_box.dart

import 'package:flutter/material.dart';

/// 可拖动容器
/// 拖动范围是父控件
class DragMoveBox extends StatefulWidget {
    
    
  final Widget child;
  const DragMoveBox({
    
    
    super.key,
    required this.child,
  });
  
  State<DragMoveBox> createState() => _DragMoveBoxState();
}

class _DragMoveBoxState extends State<DragMoveBox> {
    
    
  final GlobalKey _mykey = GlobalKey();
  //当前位移(有活动区域限制时,鼠标超过边界后当前位移不等于总位移,此时总位移可以确保回到边界内鼠标与控件的相对位置不变)
  final _offset = ValueNotifier<Offset>(Offset.zero);
  //总位移
  var _unlimtedOffset = Offset.zero;
  
  Widget build(BuildContext context) {
    
    
    return ValueListenableBuilder(
      valueListenable: _offset,
      builder:
          //采用transform变换实现拖动
          (context, offset, widget) => Transform.translate(
        key: _mykey,
        offset: offset,
        child: GestureDetector(
          child: this.widget.child,
          onPanUpdate: (detail) {
    
    
            var off = _unlimtedOffset = _unlimtedOffset + detail.delta;
            //拖动区域为父控件,去掉则不受限制,但拖出父控件会被遮挡无法点击。
            //获取父控件大小
            RenderBox? parentRenderBox = _mykey.currentContext
                ?.findAncestorRenderObjectOfType<RenderObject>() as RenderBox?;
            final screenSize = parentRenderBox?.size;
            //获取控件大小
            final mySize = _mykey.currentContext?.size;
            final renderBox =
                _mykey.currentContext?.findRenderObject() as RenderBox?;
            //获取控件当前位置    
            var originOffset = renderBox?.localToGlobal(Offset.zero);
            if (originOffset != null) {
    
    
              originOffset = parentRenderBox?.globalToLocal(originOffset);
            }
            if (screenSize == null || mySize == null || originOffset == null) {
    
    
              return;
            }
            //计算不超出父控件区域
            if (off.dx < -originOffset.dx) {
    
    
              off = Offset(-originOffset.dx, off.dy);
            } else if (off.dx >
                screenSize.width - mySize.width - originOffset.dx) {
    
    
              off = Offset(
                screenSize.width - mySize.width - originOffset.dx,
                off.dy,
              );
            }
            if (off.dy < -originOffset.dy) {
    
    
              off = Offset(off.dx, -originOffset.dy);
            } else if (off.dy >
                screenSize.height - mySize.height - originOffset.dy) {
    
    
              off = Offset(
                off.dx,
                screenSize.height - mySize.height - originOffset.dy,
              );
            }
            //现在活动区域为父控件 --end
            _offset.value = off;
          },
        ),
      ),
    );
  }
}

3. Пример использования

1. Основное использование

DragMoveBox(
child:Text("You have pushed the button this many times:") //需要拖动的控件
)

Предварительный просмотр эффекта
вставьте сюда описание изображения


Подведем итог

Это то, о чем я хочу поговорить сегодня. В этой статье представлена ​​простая реализация управления перетаскиванием, особенно после того, как она упакована в контейнер, она становится очень простой в использовании. Основная причина в том, что вы можете придумать преобразование перевода для изменения После понимания размера элемента управления с помощью GlobalKey и метода получения размера элемента управления можно легко реализовать функцию перетаскивания.

Supongo que te gusta

Origin blog.csdn.net/u013113678/article/details/131489967
Recomendado
Clasificación