如何修复Widget:让内容固定抽屉顶部并消除底部间隙
双部分布局Widget问题修复请求
布局需求
- 顶部部分:固定在顶部,用户下拉抽屉时会轻微拉伸;
- 抽屉部分:占据屏幕剩余区域,为可滚动部分。
状态展示
静止状态

下拉时状态

存在的问题
- 下拉抽屉时,内容被拉伸(标题远离抽屉顶部),希望内容保持原相对位置;
- Widget底部会形成间隙,下拉时间隙扩大,希望消除该间隙。
现有代码
/// A Scrolling widget that have a sticky widget at the top and a scrolling drawer below class ScaffoldPinnedWidgetAndAd extends Stateful Widget { final Widget topChild; final List<Widget> children; final double imageCoverRate; final bool withAd; /// The scrolling container background color final Color? color; const ScaffoldPinnedWidgetAndAd({ super.key, required this.topChild, required this.children, this.imageCoverRate = 0.9, this.color, this.withAd = true, }); @override State<ScaffoldPinnedWidgetAndAd> createState() => _ScaffoldPinnedWidgetAndAdState(); } class _ScaffoldPinnedWidgetAndAdState extends State<ScaffoldPinnedWidgetAndAd> { late final size = MediaQuery.of(context).size; late final normalImageHeight = size.height / 4 * widget.imageCoverRate; late double imageHeight = size.height / 4; final _scrollController = ScrollController(); double? scrollDelta; double _roundingAttenuation = 1; static const double _kRoundingRadius = 32.0; Widget get _body => SizedBox( width: size.width, height: size.height, child: Stack( children: [ SizedBox( height: imageHeight, width: double.infinity, child: widget.topChild, ), // The scrolling content NotificationListener<ScrollNotification>( // Listen for scroll events onNotification: (notification) { if (notification is ScrollUpdateNotification) { final rate = _scrollController.position.pixels < 0 ? 4.0 : 1.0; _roundingAttenuation = 1; setState(() { scrollDelta = notification.scrollDelta; imageHeight = normalImageHeight - _scrollController.position.pixels * rate; // Clamp imageHeight to prevent negative values imageHeight = imageHeight.clamp(0.0, size.height * .9); if (imageHeight == 0) _roundingAttenuation = 0; }); } return true; // Allow further notification propagation }, child: Positioned( top: imageHeight * widget.imageCoverRate, child: SizedBox( width: size.width, height: size.height * 0.9 - imageHeight, child: Container( padding: EdgeInsets.fromLTRB( _kRoundingRadius / 2, 0, _kRoundingRadius / 2, _kRoundingRadius / 2, ), decoration: BoxDecoration( color: widget.color ?? Theme.of(context) .scaffoldBackgroundColor, borderRadius: BorderRadius.only( topLeft: Radius.circular( _kRoundingRadius * _roundingAttenuation), topRight: Radius.circular( _kRoundingRadius * _roundingAttenuation), ), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.5), spreadRadius: 5, blurRadius: 7, offset: const Offset(0, 3), ), ], ), child: ListView.builder( padding: EdgeInsets.only(top: _kRoundingRadius), controller: _scrollController, physics: const BouncingScrollPhysics(), itemCount: widget.children.length, itemBuilder: (context, index) => widget.children[index], ), ), ), ), ), ], ), ); @override Widget build(BuildContext context) => Scaffold(body: _body); @override void dispose() { _scrollController.dispose(); super.dispose(); } }
内容的提问来源于stack exchange,提问作者Stéphane de Luca
相关产品推荐
相关产品推荐

