Flutter将视图数据抽象成为三个部分,即Widget树、Element树和RenderObject树。
RenderObject是Flutter真正的UI渲染树,负责界面的测量,布局和绘制,Flutter页面布局相关的源码都在RenderObject及其子类中。
上图是RenderObject类中和布局相关的属性和方法.
由于父RenderObject传递给子RenderObject约束信息,子RenderObject传递给父RenderObject大小信息,Flutter布局存在一些限制:
线性布局的子布局在主轴上长度不能设置无限大(double.infinity),因为线性布局给子布局在主轴上的最大长度的约束就是无限大(double.infinity),会导致无限大的子布局无法计算当前布局的大小,布局失败。如下:
// 第一个子Container的高度设置为无限大,导致布局失败Column( children: <Widget>[ Container( width: 200.0, height: double.infinity, color: Colors.blue, ), Container( width: 200.0, height: 200.0, color: Colors.red, ), ],);// 子Column的高度为无限大,导致布局失败Column( children: <Widget>[ Column( children: <Widget>[ Container( width: 200.0, height: 200.0, color: Colors.blue, ), ], ), Container( width: 200.0, height: 200.0, color: Colors.red, ), ],);
如果想要让子布局占满全屏可以增加Expanded布局。如下:
Column( children: <Widget>[ Expanded( child: Container( width: 200.0, height: double.infinity, color: Colors.blue, ), ), Container( width: 200.0, height: 200.0, color: Colors.red, ), ],);Column( children: <Widget>[ Expanded( child: Column( children: <Widget>[ Container( width: 200.0, height: 200.0, color: Colors.blue, ), ], ), ), Container( width: 200.0, height: 200.0, color: Colors.red, ), ],);
栈布局的子布局设置相对位置推荐使用Padding代替Position,并把fit属性设置StackFit.loose,构建时Stack会自适应大小,否则需要使用Container设置Stack和Position的大小;并且Padding默认对上下左右都有相对距离(0),但Position没有默认的相对布局,如果忘记设置,会使Position的子布局无法自动换行或布局不显示。
不推荐:
// Text无法自动换行// Container布局不显示Stack( children: <Widget>[ Positioned( top: 100.0, child: Text( '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试'), ), Positioned( top: 0.0, child: Column( children: <Widget>[ Container( height: 100.0, width: double.infinity, color: Colors.red, ), ], ), ) ],);
推荐:
Stack( children: <Widget>[ Padding( padding: const EdgeInsets.only(top: 10.0), child: Text( '测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试'), ), Padding( padding: const EdgeInsets.only(top: 0.0), child: Column( children: <Widget>[ Container( height: 100.0, width: double.infinity, color: Colors.red, ), ], ), ) ],);
可滚动布局在主轴方向上的长度是无限大,父布局需要转递给可滚动布局最大长度的约束。
Column( children: <Widget>[ Expanded( child: ListView( children: <Widget>[ Text('测试'), ], ), ), ],);
横向可滚动布局必须设置高度。
Column( children: <Widget>[ Container( height: 200.0, child: ListView( scrollDirection: Axis.horizontal, children: <Widget>[ Text('测试'), ], ), ), ],);
可滚动布局嵌套可滚动布局被嵌套布局的shrinkWrap属性必须设置为true,并且可以将physics属性设置为NeverScrollableScrollPhysics()来解决滑动冲突。
ListView( children: <Widget>[ ListView( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), children: <Widget>[ Text('测试'), ], ), ],);
本文章通过源码分析讲述Flutter布局的过程,分析实战中布局不显示的原因,并给出Flutter布局的一些建议。