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

Flutter Web桌面端DraggableScrollableSheet滚动异常问题求解

问题原因

这是Flutter Web桌面端的已知框架行为,并非你遗漏了配置项。鼠标滚轮事件的默认处理逻辑和触控滑动事件不同:触控滑动事件会在滚动到Scrollable边界时冒泡到父级可滚动组件,而鼠标滚轮事件默认仅由当前命中的Scrollable消费,滚动到边界后不会传递给外层的DraggableScrollableSheet,因此无法触发面板的高度调整。

解决方案

你可以通过手动监听滚轮事件、同步偏移到DraggableScrollableSheet控制器的方式修复该问题,完整修改后的核心代码如下:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hello World',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;
  const MyHomePage({super.key, this.title = "Home Page"});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // 声明DraggableScrollable控制器
  final DraggableScrollableController _sheetController = DraggableScrollableController();

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: const Center(
        child: Text(
          'Hello, World!',
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => showModalBottomSheet(
          context: context,
          isScrollControlled: true,
          builder: (_) => DraggableScrollableSheet(
            controller: _sheetController,
            expand: false,
            builder: (_, ScrollController scrollController) {
              // 用Listener监听滚轮事件
              return Listener(
                onPointerSignal: (signal) {
                  if (signal is PointerScrollEvent) {
                    final currentSize = _sheetController.size;
                    final scrollDelta = signal.scrollDelta.dy / MediaQuery.of(context).size.height;
                    // 计算新的尺寸,限制在0~1区间
                    final newSize = (currentSize - scrollDelta).clamp(0.0, 1.0);
                    
                    // 列表未到边界时不处理滚轮,交给列表自身滚动
                    if (scrollController.position.atEdge) {
                      final isAtTop = scrollController.position.pixels == 0;
                      // 列表在顶部时,向上滚动滚轮(scrollDelta为负)需要扩大面板
                      if (isAtTop && scrollDelta.isNegative) {
                        _sheetController.jumpTo(newSize);
                      }
                      // 列表在底部时,向下滚动滚轮(scrollDelta为正)需要缩小面板
                      else if (!isAtTop && scrollDelta.isPositive) {
                        _sheetController.jumpTo(newSize);
                      }
                    }
                    // 面板已经到最大/最小尺寸时,直接处理滚轮滚动列表
                    else if (currentSize <= 0.5 && scrollDelta.isPositive) {
                      return;
                    } else if (currentSize >= 1 && scrollDelta.isNegative) {
                      return;
                    }
                  }
                },
                child: ListView(
                  controller: scrollController,
                  physics: const ClampingScrollPhysics(),
                  children: [for (var i = 0; i < 250; i++) Text("Item: $i")],
                ),
              );
            },
          ),
          shape: const RoundedRectangleBorder(
              borderRadius: BorderRadius.vertical(top: Radius.circular(20))),
        ),
        tooltip: "Open Sheet!",
        child: const Icon(Icons.add),
      ),
    );
  }
}

修改后即可实现桌面端和移动端一致的联动滚动效果:列表滚动到顶部后继续向上滚动滚轮即可展开面板到全屏,滚动到底部后继续向下滚动滚轮即可收起面板。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 20:36:02