➢ layout 获取和设置 布局约束:先看一下,下面这段代码:
body: Container( width: 400, height: 400, color: Colors.blue, child: Container( width: 100, height: 100, color: Colors.orange, child: const FlutterLogo( size: 50, ), ), ),
我们:想要的是 400*400 蓝色底,100*100橘色的底, 50*50 FlutterLoge, 而实际情况确是这样:
究其原因是因为:上面的组件没有满足 Flutter的约束条件:会被修正,可以理解为 child width 和height 只是个建议尺寸,实际的尺寸 是有 父级对象 决定的!
Flutter 向下传递约束 Flutter 向上传递建议尺寸最后父级通过 位置计算得到 每个 child尺寸,在决定把 child 放在那里经过简单的处理之后 我们得到了应该有的布局
body: Container( width: 400, height: 400, color: Colors.blue, child: Align( //alignment: Alignment(0.5,0.5)等价于 alignment: Alignment.center, // alignment: Alignment.bottomCenter, child: Container( width: 250, height: 250, color: Colors.orange, //没有规定child 放在那里,所有只能 放满全屏,如果 添加一个Center/Align 让父组件怎么 如何摆放child,这时就能确定 child位置和 尺寸 //Center 继承自 Align child: Center( child: const FlutterLogo( size: 50, ), ), ), ), ),
➢ layout d性布局:Flutter 布局约束:
紧约束松约束 (最小值为0的约束 )constraints:BoxConstraints(w=350.0, h=350.0)
BoxConstraints(0.0<=w<=350.0, 0.0<=h<=350.0)
下面的代码中 FlutterLoge() 设置的 size: 9999,是没有效果的,会被父层级Container 修正!
body: Center( child: Container( width: 350, height: 350, color: Colors.orangeAccent, child: FlutterLogo( size: 9999, ), ), )
可以使用 LayoutBuilder 查看父层级的约束:
child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { print("constraints:$constraints"); return const FlutterLogo( size: 9999, ); }, )
打印结果为一个: 紧约束,所有这里不管我们怎么 设置子组件尺寸都是 没有用的,父组件会强制设置子组件的尺寸为 350*350
I/flutter (13229): constraints:BoxConstraints(w=350.0, h=350.0)
➽ 我们给 LayoutBuilder 包裹一个 Center 组件, 可以改变父组件的 约束,成为一个 松约束,让后告诉父组件我们要摆放的位置;就能实现控制子组件的大小了
constraints:BoxConstraints(0.0<=w<=350.0, 0.0<=h<=350.0)
body: Center( child: Container( width: 350, height: 350, color: Colors.orangeAccent, child: Center( child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { print("constraints:$constraints"); return const FlutterLogo( size: 50, ); }, ), ), ), ),
➽ ConstrainedBox,修改我们的约束, 前提是 我们当前组件满足父组件的约束:先将我们的约束设置为 松约束,如果不设置,我么的定义约束还是会被父组件 强制修改的定定义我们的约束body: Center( child: Container( width: 350, height: 350, color: Colors.orangeAccent, child: Center( //1 先将我们的约束设置为 松约束 child: ConstrainedBox( constraints: const BoxConstraints( //2 定定义我们的约束 minWidth: 50, maxWidth: 250, minHeight: 50, maxHeight: 250), child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { print("constraints:$constraints"); return const FlutterLogo( size: 50, ); }, ), ), ), ), ),
上面代码中如果我们不使用,Center组件(不修改 强制约束 为 松约束),那么 ConstrainedBox 是不会起作用的!
下面的代码设置 自定义约束就不会起作用!:
//错误示范 body: Center( child: Container( width: 350, height: 350, color: Colors.orangeAccent, //1 先将我们的约束设置为 松约束;负责不起作用 child: ConstrainedBox( constraints: const BoxConstraints( //2 定定义我们的约束 minWidth: 50, maxWidth: 250, minHeight: 50, maxHeight: 250), child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { print("constraints:$constraints"); return const FlutterLogo( size: 50, ); }, ), ), ), ),
Column 或者 Row 中嵌入 ListView: 报错这个现象:
body: Center(
child: Container(
color: Colors.orange,
width: 250,
child: Column(
// mainAxisSize: MainAxisSize.max,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(onPressed: () {}, child: Text("")),
ListView(
children: [for (int i = 0; i < 10; i++) Text("List view $i")],
),
ElevatedButton(onPressed: () {}, child: Text("")),
],
),
),
),
RenderBox was not laid out: RenderRepaintBoundary#341bb relayoutBoundary=up5 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1929 pos 12: 'hasSize'
上面代码当中:
Column 元素会首先布局 固定大小(有约束限制)的组件:其中 ElevatedButton组件高度固定,
Listview 组件是一个d性组件高度无限制,而 Column 也是一个高度无限制的组件;所以就会出错!在这里给ListView 包裹一层就能解决这个问题!这里归根结底的原因就是:Column 本身是一个无限制的元素,而子元素 中除过 ListView 也是一个无限制元素;两个无限制元素同时出现时 Flutter就无法确定 元素大小就会报错
I/flutter (11566): constraints:BoxConstraints(0.0<=w<=250.0, 0.0<=h<=Infinity)
所以 当无约束的空间 嵌套无约束的空间,就是出异常,在这里给 ListView 包裹 Expand组件就能决绝 这个问题:Expand占剩余空间 大小确定
body: Center(
child: Container(
color: Colors.orange,
width: 250,
child: Column(
// mainAxisSize: MainAxisSize.max,
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(onPressed: () {}, child: Text("")),
Expanded(
child: ListView(
children: [
for (int i = 0; i < 100; i++) Text("List view $i")
],
),
),
ElevatedButton(onPressed: () {}, child: Text("")),
],
),
),
),
➢ layout Stack 层叠:影响 Stack 组件大小的因素
➢ layout 决定 Container 组件大小的因素在 Stack 布局中 有一个有意思的现象:
子组件中 有Positioned 组件 和 其他组件的情况下 Stack 组件大小由 最大 非Positioned 组件 决定;如下代码中 则是由 最长 Text 组件决定的body: Center( child: Container( color: Colors.blue, child: Stack( // alignment: Alignment.center, alignment: Alignment(-1, 0.75), children: [ Text( 'You have You have You', style: TextStyle(fontSize: 20, color: Colors.orange), ), Text( 'GoGoGO', style: TextStyle(fontSize: 15, color: Colors.red), ), Positioned( left: 0, top: 0, child: FlutterLogo( size: 250, ), ), Positioned( left: 0, top: 0, child: Container( width: 24, height: 24, color: Colors.black, ), ), ], ), ), ),
2.子组件中 都是 Positioned 组件,那么 Stack 的尺寸则会占满父组件 最大约束
之所以会有这样的现象 主要是应为:本身Stack 组件为 用户实现层叠样式实现的,如果有其他组件(非Positioned)那么State 就有参照!所以可以按照参照进行布局,如果全部都是 Positoned 则没有参照;所以会 占满父组件;
当然也可通过 Satck fit 属性进行设置:修改默认规则
child: Stack( //fit: StackFit.expand,//最大适配子元素 fit:StackFit.loose,//最小适配元素 //fit:StackFit.passthrough,//直接把 Stack 父组件的约束传递给子组件们
body: Center( child: Container( color: Colors.blue, child: Stack( fit: _isSelect ? StackFit.expand : StackFit.loose, // fit: StackFit.loose, //最小适配 // fit: StackFit.expand, //最大适配 // alignment: Alignment.center, alignment: Alignment(-1, 0.75), children: [ Text("Stack 影响组件大小的 因素"), Positioned( left: 0, top: 0, child: FlutterLogo( size: 250, ), ), Positioned( left: 0, top: 0, child: Container( width: 24, height: 24, color: Colors.black, ), ), Switch( value: _isSelect, onChanged: (value) { setState(() { _isSelect = value; }); print('Switch--------value:$value'); print('Switch--------_isSelect:$_isSelect'); }, ), ], ), ), ),
Stack 超出显示和裁切:超出部分 虽然可以显示出来,但是不能响应 用户点击事件
overflow: Overflow.visibleclipBehavior: Clip.none,//超出部分 虽然可以显示出来,但是不能响应 用户点击事件body: Center( child: Container( color: Colors.blue, constraints: const BoxConstraints( maxWidth: 350, minWidth: 10, maxHeight: 350, minHeight: 10, ), child: Stack( //alignment: Alignment.bottomCenter, overflow: Overflow.visible, clipBehavior: Clip.none,//超出部分 虽然可以显示出来,但是不能响应 用户点击事件 children: [ FlutterLogo(size: 250), Positioned( left: 20, top: -20, width: 250, height: 50, child: Container( width: 24, height: 24, color: Colors.orange, ), ), ], ), ), ),
当 Container 没有 子组件 的时候,自身尺寸 占满父组件最大约束;当 Contaiber 有子组件 的时候,自身尺寸 就是子组件的尺寸*当 Contaiber 有对齐属性的时候:Contaiber 自身尺寸 占满父组件最大约束;(对齐属性)类似包裹了一层 layout*当 Contaiber 父组件是类似 Column/Row/ListView 这种无边界约束的时候 unborder,因为不能确定约束的最大的值! 这时候 Contaiber 会取约束的最小值没有 子组件 的时候:
body: Center( child: Container( color: Colors.orange, // child: FlutterLogo(size: 100), ), ),
有子组件 的时候:
body: Center( child: Container( color: Colors.orange, child: FlutterLogo(size: 100), ), ),
右对齐属性:
body: Container( alignment: Alignment.center, color: Colors.orange, child: const FlutterLogo( size: 50, ), ),
Column/Row/ListView 这种无边界约束的时候:
body: Column( children: [ Container( // alignment: Alignment.center, color: Colors.orange, child: const FlutterLogo( size: 50, ), ) ], ),
当然也通过可以修改 Container 的 constraints 改变父组件约束,来修 占满父组件最大约束
body: Center( child: Container( constraints: const BoxConstraints( maxHeight: 250, minHeight: 10, maxWidth: 250, minWidth: 10, ), color: Colors.orange, // child: FlutterLogo(size: 30), ), ),
如果有 约束传递/多组件约束叠加 的情况发生,那么取 约束的交集
Center 组件是一个 松约束 范围 width: 0 到 屏幕宽; height: 0 到 屏幕高SizedBox 强制宽度到 200;高度没有 影响Container :受父组件约束 高度200,受子组件影响 宽度 100FlutterLog : 宽100 高100;所以在这里 Container 组件的大小 实际上 受到多个组建的叠加影响!
body: Center( //松约束 0-屏幕宽高 child: SizedBox( width: 200, // 宽度紧约束 child: Container( //页数叠加: 宽度由SizeBox决定, 由于Container 又子组件高度为 子组件的高度 200*100 color: Colors.orange, child: FlutterLogo(size: 100), //子组件 紧约束 100*100 ), ), ),
本质上:Container组件 类以一个语法糖的写法;里面的很多属性 都是一个一个的 类型去实现的:等等
如果有 alignment 属性则会包裹一层 Align(alignment: alignment!, child: current);如果有 color 属性,则会包裹一层 ColoredBox(color: color!, child: current);他的部分源码:
@override Widget build(BuildContext context) { Widget? current = child; if (child == null && (constraints == null || !constraints!.isTight)) { current = LimitedBox( maxWidth: 0.0, maxHeight: 0.0, child: ConstrainedBox(constraints: const BoxConstraints.expand()), ); } if (alignment != null) current = Align(alignment: alignment!, child: current); final EdgeInsetsGeometry? effectivePadding = _paddingIncludingDecoration; if (effectivePadding != null) current = Padding(padding: effectivePadding, child: current); if (color != null) current = ColoredBox(color: color!, child: current); if (clipBehavior != Clip.none) { assert(decoration != null); current = ClipPath( clipper: _DecorationClipper( textDirection: Directionality.maybeOf(context), decoration: decoration!, ), clipBehavior: clipBehavior, child: current, ); } if (decoration != null) current = DecoratedBox(decoration: decoration!, child: current); if (foregroundDecoration != null) { current = DecoratedBox( decoration: foregroundDecoration!, position: DecorationPosition.foreground, child: current, ); } if (constraints != null) current = ConstrainedBox(constraints: constraints!, child: current); if (margin != null) current = Padding(padding: margin!, child: current); if (transform != null) current = Transform(transform: transform!, alignment: transformAlignment, child: current); return current!; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)