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

Flutter中无法监听Bottom Sheet背景区域的滚动事件问题

问题解决:背景区域拖动触发DraggableScrollableSheet滚动监听

问题核心是背景的BackdropFilter组件未关联DraggableScrollableSheet的滚动逻辑,导致拖动背景时无法触发滚动通知。以下两种方案可解决该问题:

方案一:给背景添加手势监听,同步控制滚动控制器

直接为背景组件添加GestureDetector,监听垂直拖动事件,通过DraggableScrollableController调整弹窗尺寸,同时触发监听逻辑。

修改后的完整代码:

import 'dart:ui';

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 Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final DraggableScrollableController draggableScrollableController = DraggableScrollableController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SizedBox(),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showBs(context),
        tooltip: 'Show bs',
        child: const Icon(Icons.play_arrow),
      ),
    );
  }

  Widget backgroundWidget(BuildContext context) {
    final screenHeight = MediaQuery.of(context).size.height;
    final maxDragDistance = screenHeight * 0.5;

    return GestureDetector(
      onVerticalDragUpdate: (details) {
        final newSize = draggableScrollableController.size - (details.delta.dy / screenHeight);
        draggableScrollableController.size = newSize.clamp(0.0, 0.5);
        print('Scroll from background works now');
      },
      onVerticalDragEnd: (details) {
        if (draggableScrollableController.size < 0.2) {
          Navigator.pop(context);
        } else {
          draggableScrollableController.animateTo(0.5, duration: Duration(milliseconds: 300), curve: Curves.easeOut);
        }
      },
      child: BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
        child: Container(
          height: double.maxFinite,
          width: double.maxFinite,
          color: Colors.blue.withOpacity(0.2),
        ),
      ),
    );
  }

  void _showBs(BuildContext context) {
    showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      clipBehavior: Clip.none,
      backgroundColor: Colors.transparent,
      builder: (context) {
        return NotificationListener(
          onNotification: (notification) {
            if (notification is ScrollUpdateNotification) {
              print('Working when scroll from body');
            }
            return true;
          },
          child: Stack(
            alignment: Alignment.bottomCenter,
            clipBehavior: Clip.none,
            children: [
              backgroundWidget(context),
              draggableWidget(),
            ],
          ),
        );
      },
    );
  }

  Widget draggableWidget() {
    return DraggableScrollableSheet(
      expand: false,
      maxChildSize: 0.5,
      minChildSize: 0.0,
      initialChildSize: 0.5,
      controller: draggableScrollableController,
      builder: (BuildContext context, ScrollController scrollController) {
        return SingleChildScrollView(
          controller: scrollController,
          child: Container(
            color: Colors.red,
            width: double.maxFinite,
            height: 500,
          ),
        );
      },
    );
  }
}

方案二:将背景作为DraggableScrollableSheet的子组件

把背景组件嵌套到DraggableScrollableSheet内部,让背景和主体共享同一滚动手势区域,利用组件本身的手势处理逻辑实现全局响应。

修改后的核心代码片段:

Widget draggableWidget(BuildContext context) {
  return DraggableScrollableSheet(
    expand: false,
    maxChildSize: 0.5,
    minChildSize: 0.0,
    initialChildSize: 0.5,
    controller: draggableScrollableController,
    builder: (BuildContext context, ScrollController scrollController) {
      return Stack(
        children: [
          Positioned.fill(child: backgroundWidget()),
          SingleChildScrollView(
            controller: scrollController,
            child: Container(
              color: Colors.red,
              width: double.maxFinite,
              height: 500,
            ),
          ),
        ],
      );
    },
  );
}

void _showBs(BuildContext context) {
  showModalBottomSheet(
    context: context,
    isScrollControlled: true,
    clipBehavior: Clip.none,
    backgroundColor: Colors.transparent,
    builder: (context) {
      return NotificationListener(
        onNotification: (notification) {
          print('Working from both body and background');
          return true;
        },
        child: draggableWidget(context),
      );
    },
  );
}

方案说明

  • 方案一适合需要单独定制背景拖动逻辑的场景,可灵活控制背景与主体的交互差异;
  • 方案二更简洁,复用DraggableScrollableSheet原生手势处理,无需额外编写手势监听代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:58:11