Flutter中Modal Bottom Sheet内SizedBox滚动消失问题求解决方案
解决Modal Bottom Sheet全屏时CustomScrollView顶部Sliver滚动消失的问题
当Modal Bottom Sheet设置isScrollControlled: true实现全屏后,CustomScrollView向下滚动时,顶部用于占位AppBar高度的SliverToBoxAdapter会随滚动消失——这是因为它属于滚动内容的一部分,会被滚出视口。以下是两种可行的解决方案:
方案1:将顶部占位移到CustomScrollView外部
把AppBar高度的SizedBox从CustomScrollView的slivers列表中移出,作为BottomSheet的直接子元素,和CustomScrollView同级。这样占位区域会固定在顶部,不会随滚动移动。
修改后的代码示例:
// BottomSheet的子结构改为Column包裹 Column( children: [ // 固定的顶部占位,不随滚动消失 SizedBox( height: AppBar().preferredSize.height, ), Expanded( child: CustomScrollView( controller: scrollController, slivers: [ SliverSheetBar( title: tr('invoice_steps.step1.branch.title'), searchHint: tr('invoice_steps.step1.branch.search_hint'), onSearch: (searchTerm) { page = 1; if (searchTerm.isNotEmpty) { this.searchTerm = searchTerm; } else { this.searchTerm = null; } scrollController.moveToTop(); controller.add(GetBranches(page, limit, searchTerm)); }, sheetItemsLength: state is BranchesSuccess ? tr( 'invoice_steps.step1.branch.total_number', args: [ replaceWithFarsiNumber( context, state.totalNumOfBranches.toString(), ), ], ) : null, ), const SliverToBoxAdapter( child: SizedBox( height: 7.0, ), ), SliverToBoxAdapter( child: Builder( builder: (context) { if (state is BranchesSuccess) { noNextPages = state.noNextPages; if (!noNextPages!) { page++; } return state.branches.isNotEmpty ? ListView.builder( shrinkWrap: true, itemCount: state.branches.length + 1, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { return buildItem(state, index); }, ) : searchTerm == null ? EmptyList( emptyMessage: tr( 'invoice_steps.step1.branch.empty_message'), ) : NoSearchResults( message: tr( 'invoice_steps.step1.branch.no_suggestions'), ); } else if (state is BranchesFailure) { return ErrorMessage( errorMessage: state.failureMessage, ); } return const RadioListTileShimmer(); }, ), ), ], ), ), ], )
方案2:用SliverPersistentHeader固定顶部占位
如果必须将占位区域放在CustomScrollView内部,可以用SliverPersistentHeader替代SliverToBoxAdapter,通过设置pinned: true让它始终固定在视口顶部。
修改后的代码示例:
CustomScrollView( controller: scrollController, slivers: [ SliverPersistentHeader( pinned: true, // 固定在顶部不滚动 delegate: _SizedBoxHeaderDelegate( height: AppBar().preferredSize.height, ), ), // 以下sliver内容保持不变 SliverSheetBar( title: tr('invoice_steps.step1.branch.title'), searchHint: tr('invoice_steps.step1.branch.search_hint'), onSearch: (searchTerm) { page = 1; if (searchTerm.isNotEmpty) { this.searchTerm = searchTerm; } else { this.searchTerm = null; } scrollController.moveToTop(); controller.add(GetBranches(page, limit, searchTerm)); }, sheetItemsLength: state is BranchesSuccess ? tr( 'invoice_steps.step1.branch.total_number', args: [ replaceWithFarsiNumber( context, state.totalNumOfBranches.toString(), ), ], ) : null, ), const SliverToBoxAdapter( child: SizedBox( height: 7.0, ), ), SliverToBoxAdapter( child: Builder( builder: (context) { if (state is BranchesSuccess) { noNextPages = state.noNextPages; if (!noNextPages!) { page++; } return state.branches.isNotEmpty ? ListView.builder( shrinkWrap: true, itemCount: state.branches.length + 1, physics: const NeverScrollableScrollPhysics(), itemBuilder: (context, index) { return buildItem(state, index); }, ) : searchTerm == null ? EmptyList( emptyMessage: tr( 'invoice_steps.step1.branch.empty_message'), ) : NoSearchResults( message: tr( 'invoice_steps.step1.branch.no_suggestions'), ); } else if (state is BranchesFailure) { return ErrorMessage( errorMessage: state.failureMessage, ); } return const RadioListTileShimmer(); }, ), ), ], ) // 自定义SliverPersistentHeaderDelegate类 class _SizedBoxHeaderDelegate extends SliverPersistentHeaderDelegate { final double height; _SizedBoxHeaderDelegate({required this.height}); @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { return SizedBox(height: height); } @override double get maxExtent => height; @override double get minExtent => height; @override bool shouldRebuild(_SizedBoxHeaderDelegate oldDelegate) { return height != oldDelegate.height; } }
方案1实现简单,适合不需要将占位与滚动内容绑定的场景;方案2则适合必须在CustomScrollView内部处理顶部占位的情况,按需选择即可。
内容的提问来源于stack exchange,提问作者Haya Hesham Abdel-azim
相关产品推荐
相关产品推荐

