如何根据内容调整Widget大小?DraggableScrollableSheet适配问题
实现DraggableScrollableSheet自动适配内容高度
要让DraggableScrollableSheet根据内容(包括Visibility组件的显隐)自动调整尺寸,核心是让Sheet能感知内容的实际高度,并动态调整自身的尺寸参数,同时确保不可见的Visibility组件不占用空间。
关键要点
- 确保
Visibility组件的maintainSize设为false(默认值,不可见时完全不占空间) - 使用
GlobalKey获取内容容器的实际高度,动态更新Sheet的尺寸参数 - 结合
SingleChildScrollView处理内容超出视口的情况(可选,当内容高度超过屏幕时允许滚动)
完整代码示例
import 'package:flutter/material.dart'; class AutoSizeSheetDemo extends StatefulWidget { const AutoSizeSheetDemo({super.key}); @override State<AutoSizeSheetDemo> createState() => _AutoSizeSheetDemoState(); } class _AutoSizeSheetDemoState extends State<AutoSizeSheetDemo> { // 用于获取内容容器的实际高度 final GlobalKey _contentKey = GlobalKey(); bool _showExtraWidgets = false; double _sheetMinSize = 0.2; double _sheetMaxSize = 0.5; @override void initState() { super.initState(); // 初始获取一次内容高度 WidgetsBinding.instance.addPostFrameCallback((_) => _updateSheetSize()); } void _updateSheetSize() { final RenderBox? renderBox = _contentKey.currentContext?.findRenderObject() as RenderBox?; if (renderBox == null) return; final screenHeight = MediaQuery.of(context).size.height; final contentHeight = renderBox.size.height; final newSize = contentHeight / screenHeight; setState(() { // 确保尺寸在0.1到1.0之间(DraggableScrollableSheet的有效范围) _sheetMinSize = newSize.clamp(0.1, 1.0); _sheetMaxSize = newSize.clamp(0.1, 1.0); // 如果需要允许拖动调整,可将maxSize设为1.0,minSize设为计算出的最小高度 // _sheetMaxSize = 1.0; }); } void _toggleWidgets() { setState(() { _showExtraWidgets = !_showExtraWidgets; }); // 内容变化后延迟更新尺寸,确保布局完成 WidgetsBinding.instance.addPostFrameCallback((_) => _updateSheetSize()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("自动适配Sheet")), body: Center( child: ElevatedButton( onPressed: () { showModalBottomSheet( context: context, isScrollControlled: true, builder: (context) { return DraggableScrollableSheet( initialChildSize: _sheetMinSize, minChildSize: _sheetMinSize, maxChildSize: _sheetMaxSize, builder: (context, scrollController) { return SingleChildScrollView( controller: scrollController, child: Container( key: _contentKey, padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 固定显示的Widget 1 const Text( "固定内容1", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), // 固定显示的Widget 2 Container( height: 80, color: Colors.blue[100], child: const Center(child: Text("固定容器")), ), const SizedBox(height: 16), // 可切换显示的Widget 3 Visibility( visible: _showExtraWidgets, // 确保不可见时不占空间 maintainSize: false, maintainAnimation: false, maintainState: false, child: Column( children: [ const Text("可显示/隐藏内容1"), const SizedBox(height: 16), Container( height: 120, color: Colors.green[100], child: const Center(child: Text("动态容器1")), ), ], ), ), const SizedBox(height: 16), // 可切换显示的Widget 4 Visibility( visible: _showExtraWidgets, maintainSize: false, child: Column( children: [ const Text("可显示/隐藏内容2"), const SizedBox(height: 16), Container( height: 100, color: Colors.orange[100], child: const Center(child: Text("动态容器2")), ), ], ), ), const SizedBox(height: 16), // 切换按钮 ElevatedButton( onPressed: _toggleWidgets, child: Text(_showExtraWidgets ? "隐藏额外内容" : "显示额外内容"), ), ], ), ), ); }, ); }, ); }, child: const Text("打开Sheet"), ), ), ); } }
代码说明
- GlobalKey获取内容高度:通过
_contentKey获取内容容器的RenderBox,计算其高度与屏幕高度的比例,作为Sheet的尺寸参数。 - Visibility配置:显式设置
maintainSize: false,确保不可见时不占用布局空间。 - 动态更新尺寸:每次切换Visibility状态后,通过
addPostFrameCallback等待布局完成,再更新Sheet的minChildSize和maxChildSize。 - 滚动处理:用
SingleChildScrollView包裹内容,当内容高度超过Sheet的最大尺寸时(如果将maxChildSize设为1.0),允许用户滚动查看全部内容。
如果你希望Sheet仍可拖动调整大小,可以将_sheetMaxSize设为1.0,这样最小尺寸是内容高度,最大尺寸占满屏幕,用户可以在两者之间拖动。
内容的提问来源于stack exchange,提问作者Milan Gaur
相关产品推荐
相关产品推荐

