flutter可吸边且隐藏一半的拖拽悬浮按钮

dart 复制代码
import 'package:flutter/material.dart';

class DraggableFloatingButton extends StatefulWidget {
  final Widget child;
  final double size; // 按钮大小
  final double hiddenRatio; // 隐藏比例(比如0.5表示隐藏一半)
  final GestureTapCallback? onTap;

  const DraggableFloatingButton({
    super.key,
    required this.child,
    this.size = 60,
    this.hiddenRatio = 0.5,
    this.onTap
  });

  @override
  State<DraggableFloatingButton> createState() => _DraggableFloatingButtonState();
}

class _DraggableFloatingButtonState extends State<DraggableFloatingButton> {
  late Offset _position; // 按钮当前位置
  late double _screenWidth; // 屏幕宽度
  late double _screenHeight; // 屏幕高度
  bool _isDragging = false; // 是否正在拖拽

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 初始化位置(默认右下角)
    _screenWidth = MediaQuery.of(context).size.width;
    _screenHeight = MediaQuery.of(context).size.height;
    _position = Offset(
      _screenWidth - widget.size * (1 - widget.hiddenRatio),
      _screenHeight - widget.size * 6,
    );
  }

  // 处理拖拽结束后的吸边逻辑
  void _onDragEnd() {
    setState(() {
      // 判断靠左边还是右边吸边
      if (_position.dx < _screenWidth / 2) {
        // 靠左吸边,隐藏一半
        _position = Offset(-widget.size * widget.hiddenRatio, _position.dy);
      } else {
        // 靠右吸边,隐藏一半
        _position = Offset(
          _screenWidth - widget.size * (1 - widget.hiddenRatio),
          _position.dy,
        );
      }
      _isDragging = false;
    });
  }

  // 限制拖拽范围(避免超出屏幕)
  Offset _limitPosition(Offset newPosition) {
    double dx = newPosition.dx;
    double dy = newPosition.dy;

    // 水平范围:-隐藏部分 ~ 屏幕宽-显示部分
    dx = dx.clamp(
      -widget.size * widget.hiddenRatio,
      _screenWidth - widget.size * (1 - widget.hiddenRatio),
    );
    // 垂直范围:顶部安全区 ~ 底部安全区
    dy = dy.clamp(
      MediaQuery.of(context).padding.top + 10,
      _screenHeight - MediaQuery.of(context).padding.bottom - widget.size - 10,
    );

    return Offset(dx, dy);
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: _position.dx,
      top: _position.dy,
      child: GestureDetector(
        onTap: widget.onTap,
        // 拖拽开始
        onPanStart: (details) {
          setState(() => _isDragging = true);
        },
        // 拖拽中
        onPanUpdate: (details) {
          setState(() {
            _position = _limitPosition(
              _position + details.delta,
            );
          });
        },
        // 拖拽结束
        onPanEnd: (details) => _onDragEnd(),
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeOut,
          width: widget.size,
          height: widget.size,
          // 拖拽时放大,未拖拽时复原
          transform: _isDragging
              ? Matrix4.diagonal3Values(1.2, 1.2, 1)//放大1.2倍
              : Matrix4.identity(),
          child: widget.child,
        ),
      ),
    );
  }

}

使用

dart 复制代码
@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          PageView.builder(
            controller: _pageController,
            padEnds: false,
            allowImplicitScrolling: true,
            scrollDirection: Axis.vertical,
            physics: CustomPageScrollPhysics(onLeftBoundaryReached: _leftHandleBoundaryReached,onRightBoundaryReached: _rightHandleBoundaryReached),
            itemCount: _goodsList.length,
            onPageChanged: (index) {
              setState(() {
                _currentPageIndex = index;
              });
              // 页面切换时更新视口占比
              //  _updateViewportFraction(index);
            },
            itemBuilder: (context, index) {
              return ProductSelectionVerticalPageItemWidget(index: index,
                goodsBean: _goodsList[index],
                marginBottom: index == _goodsList.length - 1 ? Utils().bottomStatusHeight() + 30.w : 0,);
            },
          ),
          // 可吸边悬浮按钮
          DraggableFloatingButton(
            size: 60,
            hiddenRatio: 0.5, // 隐藏一半
            onTap: (){},
            child: Container(
              width: 60,
              height: 60,
              decoration: BoxDecoration(
                color: Colors.white70,
                shape: BoxShape.circle,
                border: Border.all(color: JadeColors.grey_5)
              ),
              child: Icon(Icons.shopping_cart,color: JadeColors.grey_3,),
            ),
          ),
        ],
      )

    );
  }
相关推荐
Zeaon22 分钟前
别再手写筛选栏了:一个可组合的 Flutter 选择组件(5 入口 × 7 委托)
android·flutter
事圆则缓36 分钟前
Flutter 快速上手
flutter
「、皓子~5 小时前
海狸IM 2.1 私有化部署从零到一:中间件、微服务与 Nginx 全流程
nginx·flutter·微服务·中间件·开源软件·im·海狸im
嘴贱欠吻!6 小时前
Flutter 触觉反馈插件 Gaimon三方库鸿蒙端教程
flutter·华为·harmonyos
getapi7 小时前
1 在 macOS 电脑上更新 Flutter iOS 应用到 App Store
flutter·macos·ios
恋猫de小郭7 小时前
Flutter 状态管理基准测评,一个很有趣的观点
android·前端·flutter
●VON7 小时前
Flutter 鸿蒙插件适配实战:给 screen_security 补上截图与录屏防护
flutter·华为·harmonyos
tbit1 天前
Android Google Map PlatformView 图层异常排查与修复
flutter
哈__1 天前
Flutter 3.44.9 + OpenHarmony7:home_widget 三方库桌面服务卡片(FormKit)的应用
flutter·华为·harmonyos
哈__1 天前
Flutter 3.44.9 + OpenHarmony7:image_cropper三方库 图片裁剪的应用
flutter·openharmony