Flutter如何将屏幕垂直分为2栏实现整页滚动,可否不使用ConstrainedBox?

现有写法的性能说明
你当前使用MediaQuery.of(context).size.height - AppBar().preferredSize.height作为约束的写法没有显著性能问题,仅存在两个适配隐患:
- 没有自动扣除状态栏、底部系统导航栏的高度,横竖屏切换、有底部导航栏的场景下容易出现布局溢出
- 系统配置变化(如字体大小调整、屏幕旋转)时MediaQuery会触发组件重建,但该操作开销极低,不属于性能问题
无需ConstrainedBox的实现方案
推荐使用Flutter官方适配的CustomScrollView + SliverFillRemaining组合实现需求,无需手动计算页面高度,适配性更强:
核心修改思路
- 替换原有的
SingleChildScrollView + ConstrainedBox结构为CustomScrollView - 上半部分固定内容放入
SliverToBoxAdapter - 下半部分需要撑满剩余高度的GridView放入
SliverFillRemaining
修改后核心代码示例
body: RefreshIndicator( onRefresh: () async { print('testing'); }, child: CustomScrollView( physics: const BouncingScrollPhysics( parent: AlwaysScrollableScrollPhysics() ), slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(left: 24.0, right: 24.0, top: 24.0), child: Container( color: Colors.amber, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'teacher_photo_album_flow.listing_title'.tr(), style: CustomTextStyle.pageSectionTitle(context), ), SizedBox( height: 2 * SizeConfig.heightMultiplier, ), GestureDetector( onTap: () {}, child: Column( children: [ Container( height: 25 * SizeConfig.heightMultiplier, width: double.infinity, child: ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.network( this.photoAlbums1[0]['album_cover'], fit: BoxFit.cover, width: MediaQuery.of(context).size.width, ), ), ), SizedBox( height: 1.5 * SizeConfig.heightMultiplier, ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 4, child: Container( alignment: Alignment.centerLeft, child: Text( this.photoAlbums1[0]['title'], style: CustomTextStyle.titleOnelineEllipses(context), ), ), ), Expanded( child: Container( alignment: Alignment.centerRight, child: Text( this.photoAlbums1[0]['newDate'], style: CustomTextStyle.silentTextDate(context), ), ), ), ], ), ], ), ), SizedBox( height: 3 * SizeConfig.heightMultiplier, ), ], ), ), ), ), if (photoAlbums2.length > 0) SliverFillRemaining( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0), child: GridView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisSpacing: 1, mainAxisSpacing: 1, crossAxisCount: 2 ), itemCount: this.photoAlbums2.length, itemBuilder: (BuildContext ctx, i) { return GestureDetector( onTap: () {}, child: Column( children: [ Container( height: 15 * SizeConfig.heightMultiplier, width: double.infinity, child: ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.network( this.photoAlbums2[i]['album_cover'], fit: BoxFit.cover, width: MediaQuery.of(context).size.width, ), ), ), SizedBox( height: 1.0 * SizeConfig.heightMultiplier, ), Container( alignment: Alignment.centerLeft, child: Text( this.photoAlbums2[i]['title'], style: CustomTextStyle.titleOnelineEllipses(context), ), ), SizedBox( height: 0.5 * SizeConfig.heightMultiplier, ), Container( alignment: Alignment.centerLeft, child: Text( this.photoAlbums2[i]['newDate'], style: CustomTextStyle.silentTextDate(context), ), ), ], ), ); } ), ), ) ], ), );
其他可选优化点
- 若photoAlbums2数据量很大,可以直接把GridView替换为
SliverGrid,不用嵌套SliverFillRemaining,渲染性能会更好 - 空判断可以直接用Dart的collection if语法,不用三元运算符写SizedBox
内容的提问来源于stack exchange,提问作者Manas
相关产品推荐
相关产品推荐

