You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何控制slidingUpPanel仅通过拖拽手柄触发拖拽操作

实现仅通过顶部手柄控制开合的SlidingUpPanel(仿YouTube评论区)

核心改动说明

要实现仅手柄触发面板开合、内部列表滚动不影响面板状态的效果,关键在于禁用SlidingUpPanel的全局拖拽,转而给手柄添加自定义手势控制:

  • 禁用面板全局拖拽,阻止非手柄区域触发面板移动
  • 给手柄添加点击切换、垂直拖拽的手势逻辑,手动控制面板位置
  • 分离内部列表的滚动事件,确保其不会干扰面板状态

修改后的完整代码

PanelController panelController = PanelController();
double _minHeight = 0;
double _maxHeight = 0;

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  // 提前计算最小/最大高度,避免build方法中重复计算
  _minHeight = MediaQuery.of(context).size.height * .7;
  _maxHeight = MediaQuery.of(context).size.height * .9;
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: SafeArea(
      child: SlidingUpPanel(
        controller: panelController,
        maxHeight: _maxHeight,
        minHeight: _minHeight,
        borderRadius: const BorderRadius.vertical(top: Radius.circular(40.0)),
        allowDragging: false, // 禁用面板全局拖拽
        panelSnapping: true, // 保留开合时的吸附效果
        panelBuilder: (scrollController) => Stack(
          alignment: Alignment.topCenter,
          children: [
            // 拖拽手柄:处理点击切换与垂直拖拽
            GestureDetector(
              onTap: () {
                panelController.isPanelOpen 
                    ? panelController.close() 
                    : panelController.open();
              },
              onVerticalDragUpdate: (details) {
                // 根据拖拽偏移计算面板新高度
                double currentHeight = panelController.panelPosition * (_maxHeight - _minHeight) + _minHeight;
                double newHeight = currentHeight - details.delta.dy;
                // 限制高度在最小/最大值区间内
                newHeight = newHeight.clamp(_minHeight, _maxHeight);
                // 转换为0-1的位置比例,更新面板状态
                double newPosition = (newHeight - _minHeight) / (_maxHeight - _minHeight);
                panelController.setPanelPosition(newPosition);
              },
              onVerticalDragEnd: (details) {
                // 拖拽结束自动吸附到最近的展开/收起状态
                if (panelController.panelPosition > 0.5) {
                  panelController.open();
                } else {
                  panelController.close();
                }
              },
              child: Container(
                color: Colors.transparent,
                height: 36.0,
                width: MediaQuery.of(context).size.width,
                child: const Center(
                  child: SizedBox(
                    width: 46.77,
                    height: 4.0,
                    child: DecoratedBox(
                      decoration: BoxDecoration(color: Colors.grey),
                    ),
                  ),
                ),
              ),
            ),
            // 内部可滚动列表
            Padding(
              padding: const EdgeInsets.only(top: 40.0),
              child: ListView.builder(
                physics: const AlwaysScrollableScrollPhysics(),
                controller: scrollController,
                padding: EdgeInsets.zero,
                itemCount: 20,
                itemBuilder: (context, index) {
                  return const Padding(
                    padding: EdgeInsets.all(8.0),
                    child: SizedBox(
                      height: 70.0,
                      child: DecoratedBox(
                        decoration: BoxDecoration(color: Colors.red),
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

效果验证

  • 滚动内部ListView时,面板保持当前展开/收起状态,不会随滚动触发开合
  • 点击顶部手柄可快速切换面板的展开/收起状态
  • 拖拽手柄可平滑调整面板高度,松开后自动吸附到最近的展开/收起位置

内容的提问来源于stack exchange,提问作者ILii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 23:54:06