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
相关产品推荐
相关产品推荐

