【Flutter】如何实现一个拖拽关闭/展开的下拉列表
题记
如题,这是我们需要实现的效果图,我们需要flutter 中将其封装成一个小组件。
效果需求是,可以通过拖动区域拖动。
- 实现向上拖动变成列表模式
- 向下拖动变成地图模式
- 列表保持原有的下拉刷新,上拉加载更多
- 需要有中间过渡动画
分析
- 首先像这种组件一般是用在层叠布局
Stack
中,所以我们可以用Position
来做组件的容器。 - 既然用
Position
做了容器,那就可以通过控制Position.top
来控制列表的高度。 - 然后只需要将拖动区域绑定有一个手势检测来实现拖动效果就可以了。
- 最后的最后,将
Position
换成AnimationPosition
来实现动画效果。
一、极少的代码完整实现
import 'package:flutter/material.dart';
class DraggablePositionPanel extends StatefulWidget {
const DraggablePositionPanel({Key? key, required this.child, this.top = 0})
: super(key: key);
final double top;
final Widget child;
@override
State<DraggablePositionPanel> createState() => _DraggablePositionPanelState();
}
class _DraggablePositionPanelState extends State<DraggablePositionPanel> {
double? dy;
bool showAnimation = false;
@override
Widget build(BuildContext context) {
const barHeight = 4.0;
const barPadding = 8.0;
return AnimatedPositioned(
duration:
showAnimation ? const Duration(milliseconds: 200) : Duration.zero,
top: dy ?? widget.top,
left: 0,
right: 0,
bottom: 0,
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(4), topRight: Radius.circular(4)),
),
child: Column(
children: [
//滚动区域
GestureDetector(
behavior: HitTestBehavior.opaque,
onVerticalDragUpdate: (detail) {
setState(() {
showAnimation = false;
dy = detail.globalPosition.dy <= widget.top
? widget.top
: detail.globalPosition.dy;
});
},
onVerticalDragEnd: (detail) {
final size = MediaQuery.of(context).size;
final threshold = (size.height - widget.top) / 2;
setState(() {
dy = dy != null && dy! <= threshold
? widget.top
: size.height - barHeight - (barPadding * 2);
showAnimation = true;
});
},
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: barPadding),
child: Container(
width: 48,
height: barHeight,
decoration: BoxDecoration(
color: const Color(0xffc2c2c2),
borderRadius: BorderRadius.circular(barHeight),
),
),
),
),
),
Expanded(child: widget.child)
],
),
));
}
}
细节解读
1、手势检测通过设置 GestureDetector
的behavior: HitTestBehavior.opaque
,来扩大拖动生效区域。
GestureDetector(
behavior: HitTestBehavior.opaque,
...
)
2、通过设置showAnimation
状态值来控制AnimatedPositioned.duration
来实现拖动过程实时拖动,松手后直接吸附到顶部或者底部。
如何使用
DraggablePositionPanel(
top: UIScreen.appBarHeight,//自行控制初始上边距
child:YOUR_WIDGET(), //传入列表组件
)
Bye~
转载自:https://juejin.cn/post/7256393626682277925