
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,),
),
),
],
)
);
}