Flutter 全局可拖拽悬浮控件

2023-09-17  本文已影响0人  旺仔_100
背景:项目中需要做一个可拖拽悬浮控件,松手后悬停到对应的两边。
效果:似乎不能放视频,我放两张图片。红色的是可以拖动的控件。
image.png image.png
主要思路:
代码实现
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

///统一管理唯一Overlay
mixin OverlayManagerMixin<T extends StatefulWidget> on State<T> {
  bool _overlayEntryInserted = false;
  OverlayState? _overlayState;
  OverlayEntry _overlayEntry = OverlayEntry(
    builder: (_) => const SizedBox.shrink(),
  );

  @override
  void initState() {
    super.initState();
    _overlayState = Overlay.of(context);
  }

  void injectOverlay(Widget widget, double width, double height) {
    if (_overlayEntryInserted) {
      debugPrint("overlay 没有销毁,不允许重复插入");
      return;
    }
    _overlayEntry = OverlayEntry(builder: (_) {
      return Material(
        type: MaterialType.transparency,
        child: DragHoverBothSidesWidget(widget, width, height),
      );
    });
    _overlayState?.insert(_overlayEntry);
    _overlayEntryInserted = true;
  }

  void removeOverlay() {
    // Call `remove` only when the entry has been inserted.
    if (_overlayEntryInserted) {
      _overlayEntry.remove();
      _overlayEntryInserted = false;
    }
  }
}

///创建一个可以拖拽悬停两边控件
class DragHoverBothSidesWidget extends StatefulWidget {
  Widget child;

  ///可拖拽控件的宽高
  double height;
  double width;
  double bottomDistance;

  DragHoverBothSidesWidget(this.child, this.width, this.height, {Key? key, this.bottomDistance = 68}) : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return DragHoverBothSidesState();
  }
}

final double ratio = WidgetsBinding.instance.window.devicePixelRatio;

class DragHoverBothSidesState extends State<DragHoverBothSidesWidget> {
  double _dx = 0;
  double _dy = 0;
  final Size _windowSize = WidgetsBinding.instance.window.physicalSize / ratio;

  @override
  void initState() {
    super.initState();
    _dx = _windowSize.width - widget.width;
    _dy = _windowSize.height - widget.height - widget.bottomDistance;
  }

  void dragEnd(DragEndDetails details) {
    if (_dx + widget.width / 2 < _windowSize.width / 2) {
      _dx = 0;
    } else {
      _dx = _windowSize.width - widget.width;
    }
    if (_dy + widget.height > _windowSize.height) {
      _dy = _windowSize.height - widget.height - widget.bottomDistance;
    } else if (_dy < 0) {
      _dy = 0;
    }
    setState(() {});
  }

  void dragEvent(DragUpdateDetails details) {
    _dx = details.globalPosition.dx - widget.width / 2;
    _dy = details.globalPosition.dy - widget.height / 2;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: _windowSize.width,
      height: _windowSize.height,
      // color: Colors.green,
      child: Stack(
        alignment: Alignment.center,
        children: [Positioned(left: _dx, top: _dy, child: GestureDetector(onVerticalDragEnd: dragEnd, onHorizontalDragEnd: dragEnd, onHorizontalDragUpdate: dragEvent, onVerticalDragUpdate: dragEvent, child: widget.child))],
      ),
    );
  }
}

上一篇下一篇

猜你喜欢

热点阅读