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

如何在DraggableScrollableSheet中实现自定义滚动动画?

解决DraggableScrollableSheet自定义滚动物理效果的问题

方案1:通过ScrollConfiguration注入自定义物理效果

DraggableScrollableSheet内部的拖动逻辑基于Scrollable组件,因此可以用ScrollConfiguration覆盖其默认的滚动物理,同时实现clamping和bouncing的混合效果:

ScrollConfiguration(
  behavior: ScrollConfiguration.of(context).copyWith(
    physics: const BouncingScrollPhysics(parent: ClampingScrollPhysics()),
  ),
  child: DraggableScrollableSheet(
    initialChildSize: 0.5,
    minChildSize: 0.2,
    maxChildSize: 0.8,
    builder: (context, scrollController) {
      return ListView.builder(
        controller: scrollController,
        itemCount: 20,
        itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
      );
    },
  ),
)

这里BouncingScrollPhysics作为父物理,会在面板拖动到边界时触发回弹效果,而ClampingScrollPhysics作为子物理,保证正常拖动时的阻尼感,刚好匹配你需要的混合效果。

方案2:自定义控制器实现边界回弹动画

如果需要更精细的动画控制,可以通过DraggableScrollableController监听面板尺寸变化,手动触发回弹动画:

final _sheetController = DraggableScrollableController();

@override
void initState() {
  super.initState();
  _sheetController.addListener(_handleSheetSizeChange);
}

void _handleSheetSizeChange() {
  // 到达最大高度时触发回弹
  if (_sheetController.size >= _sheetController.maxSize) {
    _sheetController.animateTo(
      _sheetController.maxSize - 0.05,
      duration: const Duration(milliseconds: 120),
      curve: Curves.bounceOut,
    ).then((_) {
      _sheetController.animateTo(
        _sheetController.maxSize,
        duration: const Duration(milliseconds: 120),
        curve: Curves.bounceIn,
      );
    });
  }
  // 到达最小高度时同理可添加回弹逻辑
  else if (_sheetController.size <= _sheetController.minSize) {
    _sheetController.animateTo(
      _sheetController.minSize + 0.05,
      duration: const Duration(milliseconds: 120),
      curve: Curves.bounceOut,
    ).then((_) {
      _sheetController.animateTo(
        _sheetController.minSize,
        duration: const Duration(milliseconds: 120),
        curve: Curves.bounceIn,
      );
    });
  }
}

@override
void dispose() {
  _sheetController.removeListener(_handleSheetSizeChange);
  _sheetController.dispose();
  super.dispose();
}

@override
Widget build(BuildContext context) {
  return DraggableScrollableSheet(
    controller: _sheetController,
    initialChildSize: 0.5,
    minChildSize: 0.2,
    maxChildSize: 0.8,
    builder: (context, scrollController) {
      return ListView.builder(
        controller: scrollController,
        itemCount: 20,
        itemBuilder: (context, index) => ListTile(title: Text('Item $index')),
      );
    },
  );
}

这种方式完全自定义边界处的动画细节,适合需要特定回弹幅度、速度的场景。

注意事项

  • 方案1是最轻量化的原生解决方案,优先推荐;
  • 如果是面板内部滚动元素需要物理效果,直接给内部的ScrollView(如ListView、GridView)设置physics参数即可,无需修改DraggableScrollableSheet本身。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 12:15:25