我们可以将 FittedBox 理解为合适的盒子,将其它布局放到FittedBox这样一个盒子中,从而实现 盒子里面的子布局更好的放置。
参考 Flutter实战 空间适配(FittedBox)
在开发的过程中经常会遇到子widget大小超过父widget大小的现象。子widget应该遵循父widget的约束,如果子widget的原始大小超过了父原始视图的大小,就需要进行相应的处理(比如:缩小、裁剪等)。
如果父 widget 宽度固定高度不固定,则默认情况下 Text 会在文本到达父组件宽度的时候换行。
如果我们想让 Text 文本在超过父组件的宽度时不要换行而是字体缩小,这时候就需要用到 FittedBox 组件。
Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 30, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], )
Row( children: [ Image.network( 'https://www.2008php.com/2011_Website_appreciate/2011-03-28/20110328134546.jpg'), ], )
final BoxFit fit ; // 适配方式 final AlignmentGeometry alignment ; // 对齐方式 final Clip clipBehavior ; // 是否剪裁
显示屏幕范围宽度的内容, 超过屏幕范围的内容不显示
FittedBox( fit: BoxFit.none, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 7, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), )
文本所有内容都显示到屏幕范围内,按字体大小进行缩小
FittedBox( fit: BoxFit.contain, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 7, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), )
需要设置 FittedBox 的大小,不然设置 BoxFit.fitWidth 和 BoxFit.fitHeight 无效。
当没有设置 FittedBox 大小时,文本始终显示在屏幕的范围内,填充屏幕宽度。
SizedBox( /*height: 20.w, width: double.infinity,*/ child: FittedBox( fit: BoxFit.fitHeight, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
SizedBox( /*height: 20.w, width: double.infinity,*/ child: FittedBox( fit: BoxFit.fitWidth, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
当设置 FittedBox 大小时,文本始终显示在屏幕的范围内;如果是 BoxFit.fitWidth 以填充屏幕宽度的形式展示文本,如果是 BoxFit.fitHeight 以填充屏幕高度的形式展示文本。
SizedBox( height: 50.w, width: double.infinity, child: FittedBox( fit: BoxFit.fitHeight, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
SizedBox( height: 50.w, width: double.infinity, child: FittedBox( fit: BoxFit.fitWidth, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
保证不超过 父 Widget的大小。如果字体原始的高度比父容器的高度小,那么就按照父容器的高度进行缩小;如果原始字体宽度比父容器的宽度小,就按照父容器的宽度进行缩小。
SizedBox( height: 10.w, width: double.infinity, child: FittedBox( fit: BoxFit.scaleDown, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
SizedBox( height: 40.w, width: double.infinity, child: FittedBox( fit: BoxFit.scaleDown, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
保证不超过 父 Widget的大小。如果字体原始的高度比父容器的高度小,那么久拉伸字体高度到容器高度,相反就压缩字体高度到父容器高度。
SizedBox( height: 140.w, width: double.infinity, child: FittedBox( fit: BoxFit.fill, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
SizedBox( height: 14.w, width: double.infinity, child: FittedBox( fit: BoxFit.fill, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
内容按照原始尺寸填充父容器的宽或者高,但可能会超过父容器的范围。
如果字体原始高度小于 父容器高度,这时候就填充高度 ,可能会出现超过父容器范围的情况。
如果字体原始高度不小于 父容器高度,这时候就填充宽度,不会出现超过父容器的情况。
SizedBox( height: 80.w, width: double.infinity, child: FittedBox( fit: BoxFit.cover, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
SizedBox( height: 2.w, width: double.infinity, child: FittedBox( fit: BoxFit.cover, child: Row( children: [ Text( '文本内容过长就超出屏幕宽度/' * 3, style: TextStyle(color: Colors.blue, fontSize: 60.sp), ), ], ), ), )
如果子Widget的高度超过父视图的高度,不做裁剪,那么高度就显示成子视图的高度。
Container( width: 160, height: 150, color: Colors.red, child: FittedBox( fit: BoxFit.fitWidth, clipBehavior: Clip.none, child: Container(width: 160, height: 200, color: Colors.blue), ), )
剪切前
如果子Widget的高度超过父视图的高度,就进行裁剪,高度就显示成夫视图的高度。
Container( width: 160, height: 150, color: Colors.red, child: FittedBox( fit: BoxFit.fitWidth, clipBehavior: Clip.hardEdge, child: Container(width: 160, height: 200, color: Colors.blue), ), )
剪切后
ClipRect( // 将超出子组件布局范围的绘制内容剪裁掉 child: Container( width: 50, height: 50, color: Colors.red, child: FittedBox( fit: BoxFit.none, child: Container(width: 60, height: 70, color: Colors.blue), ), ), )
class LayoutLogPrint
extends StatelessWidget { const LayoutLogPrint({ Key? key, this.tag, required this.child, }) : super(key: key); final Widget child; final T? tag; //指定日志tag @override Widget build(BuildContext context) { return LayoutBuilder(builder: (_, constraints) { // assert在编译release版本时会被去除 assert(() { if (kDebugMode) { print('${tag ?? key ?? child}: $constraints'); } return true; }()); return child; }); } } Column( children: [ wRow(' 内容是否发生溢出/ '), FittedBox(child: wRow(' 内容是否发生溢出/ ')), ] .map((e) => Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: e, )) .toList(), )
// 直接使用Row Widget wRow(String text) { Widget child = Text( text, style: const TextStyle(color: Colors.black, fontSize: 20.0), ); child = Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [child, child, child], ); return LayoutLogPrint(child: child); }
I/flutter (13152): Row(direction: horizontal, mainAxisAlignment: spaceEvenly, crossAxisAlignment: center): BoxConstraints(0.0<=w<=392.7, 0.0<=h<=Infinity) I/flutter (13152): Row(direction: horizontal, mainAxisAlignment: spaceEvenly, crossAxisAlignment: center): BoxConstraints(unconstrained)
class SingleLineFittedBox extends StatelessWidget { const SingleLineFittedBox({Key? key,this.child}) : super(key: key); final Widget? child; @override Widget build(BuildContext context) { return LayoutBuilder( builder: (_, constraints) { return FittedBox( child: ConstrainedBox( constraints: constraints.copyWith( //让 maxWidth 使用屏幕宽度 maxWidth: constraints.maxWidth ), child: child, ), ); }, ); } }
Column( children: [ wRow(' 内容是否发生溢出 '), SingleLineFittedBox(child: wRow(' 内容是否发生溢出 ')), wRow(' 少内容 '), FittedBox( child: wRow(' 内容太挤内容太挤内容太挤内容太挤内容太挤内容太挤内容太挤内容太挤内容太挤内容太挤 '), ) ] .map((e) => Padding( padding: const EdgeInsets.symmetric(vertical: 20), child: e, )) .toList(), )
// 直接使用Row Widget wRow(String text) { Widget child = Text( text, style: const TextStyle(color: Colors.black, fontSize: 16.0), ); child = Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [child, child, child], ); return child; }
SingleLineFittedBox 中将传给 Row 的 maxWidth 置为屏幕宽度后,效果和不加 SingleLineFittedBox 的效果是一样的。
解决方案: 最小宽度(minWidth)约束指定为屏幕宽度 , 因为Row必须得遵守父组件的约束,所以 Row 的宽度至少等于屏幕宽度,所以就不会出现缩在一起的情况;同时我们将 maxWidth 指定为无限大 (double.infinity),则就可以处理数字总长度超出屏幕宽度的情况。