如何在Flutter中实现带底层可见效果的堆叠式Bottom Sheet?
实现堆叠式Bottom Sheet的解决方案
不用showModalBottomSheet(它会全屏覆盖底层内容),改用Scaffold内置的showBottomSheet方法,它支持在Scaffold的overlay层叠加多个sheet,且能轻松控制每个sheet的高度,实现底层sheet部分可见的效果。
完整代码示例
import 'package:flutter/material.dart'; void main() => runApp(const StackedBottomSheetApp()); class StackedBottomSheetApp extends StatelessWidget { const StackedBottomSheetApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: const Text('堆叠式Bottom Sheet示例')), body: Center( child: ElevatedButton( onPressed: () => _showFirstBottomSheet(context), child: const Text('打开第一个Bottom Sheet'), ), ), ), ); } void _showFirstBottomSheet(BuildContext context) { final sheetController = Scaffold.of(context).showBottomSheet( (context) => Container( height: MediaQuery.of(context).size.height * 0.7, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), child: Column( children: [ const Padding( padding: EdgeInsets.all(16.0), child: Text( '第一个Bottom Sheet', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), Expanded( child: ListView.builder( itemCount: 5, itemBuilder: (context, index) => ListTile( title: Text('条目 ${index + 1}'), leading: const Icon(Icons.list), ), ), ), Padding( padding: const EdgeInsets.all(16.0), child: ElevatedButton( onPressed: () => _showSecondBottomSheet(context), child: const Text('打开第二个Bottom Sheet'), ), ), ], ), ), ); // 可选:监听第一个sheet的关闭事件 sheetController.closed.then((_) => print('第一个sheet已关闭')); } void _showSecondBottomSheet(BuildContext context) { final sheetController = Scaffold.of(context).showBottomSheet( (context) => Container( height: MediaQuery.of(context).size.height * 0.6, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), child: Column( children: [ const Padding( padding: EdgeInsets.all(16.0), child: Text( '第二个Bottom Sheet', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), Expanded( child: Center( child: ElevatedButton( onPressed: () => Navigator.of(context).pop(), child: const Text('关闭当前Sheet'), ), ), ), ], ), ), ); sheetController.closed.then((_) => print('第二个sheet已关闭')); } }
关键说明
- 叠加原理:
showBottomSheet会将sheet添加到Scaffold的Overlay层,后续弹出的sheet会位于之前的sheet上方。 - 可见控制:通过设置第二个sheet的高度(0.6倍屏幕高度)小于第一个sheet的高度(0.7倍屏幕高度),让第一个sheet的底部区域保持可见。
- 交互优化:每个sheet都可以通过
Navigator.of(context).pop()单独关闭,也可以通过返回的PersistentBottomSheetController监听关闭事件或主动关闭。
进阶方案(如需拖拽调整)
如果需要支持拖拽调整sheet高度,可以结合DraggableScrollableSheet和Stack组件,手动管理多个sheet的堆叠层级和高度,代码示例如下:
// 简化的拖拽式堆叠sheet示例 void _showDraggableStackedSheets(BuildContext context) { showModalBottomSheet( context: context, isScrollControlled: true, builder: (context) => Stack( children: [ // 第一个拖拽sheet DraggableScrollableSheet( initialChildSize: 0.7, minChildSize: 0.3, maxChildSize: 0.9, builder: (context, scrollController) => Container( color: Colors.grey[200], child: ListView.builder( controller: scrollController, itemCount: 10, itemBuilder: (context, index) => ListTile(title: Text('条目 $index')), ), ), ), // 第二个拖拽sheet(默认覆盖在上方) Positioned( top: MediaQuery.of(context).size.height * 0.1, left: 0, right: 0, child: DraggableScrollableSheet( initialChildSize: 0.6, minChildSize: 0.4, maxChildSize: 0.8, builder: (context, scrollController) => Container( color: Colors.white, child: Center(child: ElevatedButton(onPressed: () {}, child: const Text('操作'))), ), ), ), ], ), ); }
内容的提问来源于stack exchange,提问作者Ubaidillah Faris Al-Faruq
相关产品推荐
相关产品推荐

