Flutter中如何实现适配全机型、位于ListView.builder内的等宽高正方形容器
问题根因
- 你使用全局屏幕宽度
MediaQuery.of(context).size.width计算Item宽度,当组件父级存在边距、或未占满全屏时,计算出的宽度和实际ListView可用宽度不匹配,会导致正方形比例失效 - 横向ListView的Item只指定了宽度没有限制高度,当外层
AspectRatio(2.8/1)的高度约束较小时,Column会挤压内部正方形区域的高度,AspectRatio会优先服从父级的紧约束,无法保持1:1比例
解决方案
修改后的完整代码如下:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( backgroundColor:Colors.black, body: Center( child: MyWidget(), ), ), ); } } class MyWidget extends StatelessWidget { @override Widget build(BuildContext context) { return AspectRatio( aspectRatio: 2.8 / 1, child: LayoutBuilder( // 用LayoutBuilder拿当前ListView的可用宽度,避免全局宽度的误差 builder: (context, constraints) { // 单个Item的宽度 = 可用宽度的30%,减去右侧5的padding final itemWidth = constraints.maxWidth * 0.3 - 5; return ListView.builder( scrollDirection: Axis.horizontal, shrinkWrap: true, itemCount: 3, itemBuilder: (BuildContext context, int index) { return SizedBox( width: itemWidth, // 强制Item高度和宽度匹配,保证内部正方形不会被挤压 height: itemWidth, child: GestureDetector( child: Padding( padding: const EdgeInsets.only(right: 5.0), child: Card( elevation: 0, child: Column( children: [ ClipRRect( borderRadius: BorderRadius.only( topLeft: Radius.circular(5), topRight: Radius.circular(5), ), child: AspectRatio( aspectRatio: 1 / 1, child: Padding( padding: const EdgeInsets.all(5.0), child: Container(color: Colors.white,), ), ), ), // 文字区域用剩余空间,避免挤压正方形 Expanded( child: Padding( padding: const EdgeInsets.only( left: 2.0, right: 2.0), child: FittedBox( fit: BoxFit.fitWidth, child: Text( 'Test', style: TextStyle( fontSize: 12.0, color: Colors.black), ), ), ), ), ], ), ), ), ), ); }, ); } ), ); } }
修改点说明
- 新增
LayoutBuilder包裹ListView,获取当前组件可用的实际宽度,不再依赖全局屏幕宽度做计算,适配所有父容器场景 - 给每个Item的
SizedBox同时指定height和width为相同逻辑的计算值,避免父级高度约束挤压内部正方形 - 文字区域外层套
Expanded,自动占满剩余空间,不会和正方形区域抢占高度
修改后可以适配安卓、iOS全尺寸设备,正方形区域始终保持1:1比例。
内容的提问来源于stack exchange,提问作者Mohammed Nabil
相关产品推荐
相关产品推荐

