以下内容为自学笔记,若有幸被大神看到,望指正其不准,补充其不足。万分感谢!!!
注:
- 简单介绍了18个布局,主要控制绘制,定位,约束和大小的;
- 本篇略长,可根据目录选择性阅读,18种结构都差不多,可以详细看Container。
- 系统还提供了几个裁剪形状的单元素的布局,有兴趣的可以自行去看源码,他们的父类都是一样的。
一、简介
限定子widget在视图中的位置,大小,间距,对齐方式或背景等。
系统提供18个实现类,如下:
布局类 | 描述 |
---|---|
Container | 一个拥有绘制、定位、调整大小的 widget。 |
Padding | 一个widget, 会给其子widget添加指定的填充 |
Center | 将其子widget居中显示在自身内部的widget |
Align | 一个widget,它可以将其子widget对齐,并可以根据子widget的大小自动调整大小。 |
FittedBox | 按自己的大小调整其子widget的大小和位置。 |
AspectRatio | 一个widget,试图将子widget的大小指定为某个特定的长宽比 |
ConstrainedBox | 对其子项施加额外约束的widget |
Baseline | 根据子项的基线对它们的位置进行定位的widget。 |
FractionallySizedBox | 一个widget,它将子部件的大小调整到总可用空间的一小部分。关于布局算法的更多细节,见RenderFractionallySizedOverflowBox |
IntrinsicHeight | 一个widget,根据子部件的固有高度调整整个子部件的大小。 |
IntrinsicWidth | 一个widget,根据子部件的固有宽度调整整个子部件的大小。 |
LimitedBox | 一个当其自身不受约束时才限制其大小的盒子 |
Offstage | 一个布局widget,可以控制其子widget的显示和隐藏。 |
OverflowBox | 对子部件施加的约束与从父部件获得的约束不同,可能允许子部件溢出父部件。 |
SizedBox | 一个特定大小的盒子。这个widget强制它的孩子有一个特定的宽度和高度。如果宽度或高度为NULL,则此widget将调整自身大小以匹配该维度中的孩子的大小。 |
SizedOverflowBox | 一个特定大小的widget,但是会将它的原始约束传递给它的孩子,它可能会溢出。 |
Transform | 在绘制子widget之前应用转换的widget。 |
CustomSingleChildLayout | 一个自定义的拥有单个子widget的布局 |
二、Container
(一)概述
- 控制子widget的绘制,大小,定位,填充,约束等的容器;
- 其父类为:
StatelessWidget
- 是功能比较全面的一个布局widget,绘制也会比较繁琐。
(二)属性介绍
名称 | 类型 | 作用 |
---|---|---|
alignment | AlignmentGeometry | child在容器内的对齐方式 |
padding | EdgeInsetsGeometry | child在容器内的内间距 |
margin | EdgeInsetsGeometry | 容器与父类的间距 |
color | Color | 背景颜色 |
decoration | Decoration | 背景装饰,不能与color属性同时设置,默认不传,值为color的颜色, |
foregroundDecoration | Decoration | 前景装饰,会遮住child,可设置颜色透明度使其可见 |
width | double | 容器的宽度 |
height | double | 容器的高度 |
constraints | BoxConstraints | 容器宽高的限制 |
transform | Matrix4 | 在绘制容器之前应用的转换矩阵。 |
1、alignment
-
child的对齐方式,参数类型为
AlignmentGeometry
。 -
AlignmentGeometry
为抽象类,系统提供了以下三个实现类可供使用:-
Alignment
:一个具有方便常量的类,固定位置对齐方式,不带有方向性;九个常量成员:
-
topLeft
:左上对齐; -
topCenter
:顶部居中对齐; -
topRight
:右上对齐; -
centerLeft
:垂直居中靠左对齐; -
center
:居中; -
centerRight
:垂直居中靠右对齐; -
bottomLeft
:左下对齐; -
bottomCenter
:底部居中对齐; -
bottomRight
:右下对齐; -
构造函数直接传做标指定位置,如:
Alignment(0.0,0.0)
为居中。 -
使用:
new Container( //child在容器内的对齐方式 alignment: Alignment.topLeft,//或Alignment(-1.0,-1.0) color: Colors.white, width: 100.0, height: 100.0, child: Container( color: Colors.lightBlueAccent, child: new Text('-1,-1\ntopLeft'), ), ),
-
效果图,坐标图:
-
-
-
AlignmentDirectional
:带方向性的对齐方式;-
此对齐方式,带有方向性,能与
textDirction
配合使用改变对齐方式。 -
单独使用效果与
Alignment
效果一样。 -
常量成员有9个:
topStart
、topCenter
、topEnd
、centerStart
、center
、centerEnd
、bottomStart
、bottomCenter
、bottomEnd
,start表示左边,end表示右边。 -
构造函数:
//这里的start和y就是开始的坐标 const AlignmentDirectional(this.start, this.y) : assert(start != null), assert(y != null);
-
使用方法:
new Container( //child在容器内的对齐方式 alignment: AlignmentDirectional.topLeft,//AlignmentDirectional(-1.0,-1.0) color: Colors.white, width: 100.0, height: 100.0, child: Container( color: Colors.lightBlueAccent, child: new Text('-1,-1\ntopStart'), ), ),
-
坐标图:
-
FractionalOffset
:-
是
Alignment
的子类,不同点就是将坐标偏移了,使用构造函数时传入坐标点要注意。 -
偏移规则从构造函数可以看出:
const FractionalOffset(double dx, double dy) : assert(dx != null), assert(dy != null), super(dx * 2.0 - 1.0, dy * 2.0 - 1.0);//偏移规则
-
提供了和
Alignment
名字一样的九个常量成员,效果也一样,就是坐标发生了偏移。 -
坐标图
-
-
2、padding和margin
padding
:内边距,child与容器边的间距。margin
:外边距,容器与父容器的间距。
-
两个属性参数类型都是
EdgeInsetsGeometry
,系统提供了以下两个实现类可供使用:-
EdgeInsets
:常用,在四个基本方向中的每个方向上的一组不可变的偏移量。-
提供了几个常量构造方法:
//1.创建一个指定左侧、顶部、右侧和底部的偏移量的对象 const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom); //2.创建一个偏移量都为value值的对象 const EdgeInsets.all(double value) : left = value, top = value, right = value, bottom = value; //3。创建一个给某一个或多个位置指定偏移量的对象 const EdgeInsets.only({ this.left = 0.0, this.right = 0.0, this.top = 0.0, this.bottom = 0.0 }); //4.创建一个没有偏移量的对象 static const EdgeInsets zero = EdgeInsets.only(); //5.创建一个对称的垂直或水平方向上偏移的对象 const EdgeInsets.symmetric({ double vertical = 0.0,double horizontal = 0.0 }) : left = horizontal, top = vertical, right = horizontal, bottom = vertical; //6.创建匹配给定窗口填充的insets。 /// If you need the current system padding or view insets in the context of a /// widget, consider using [MediaQuery.of] to obtain these values rather than /// using the value from [dart:ui.window], so that you get notified of /// changes. EdgeInsets.fromWindowPadding(ui.WindowPadding padding, double devicePixelRatio) : left = padding.left / devicePixelRatio, top = padding.top / devicePixelRatio, right = padding.right / devicePixelRatio, bottom = padding.bottom / devicePixelRatio;
-
使用方式:
new Container( alignment: FractionalOffset.center, color: Colors.white, width: 300.0, height: 300.0, //child与容器内的间距 padding: EdgeInsets.all(10.0), //外边距 margin: EdgeInsets.all(10.0), child: Container( alignment: Alignment.center, width: 300.0, height: 300.0, color: Colors.lightBlueAccent, child: new Text( 'The lightBlue is text.' '\nThe white is container.' '\nThe gray is parentContainer.', textAlign: TextAlign.center, ), ), ),
-
效果图:
-
-
EdgeInsetsDirectional
:-
带方向(水平)的控制偏移量,
-
在四个基本方向中的每个方向上的不可变偏移量集,但其水平分量依赖于写入方向
-
这可以表示文本左填充
extDirection.ltr
和文本右填充TextDirection.rtl
,而不需要知道当前文本的方向。 -
提供构造函数:
//1.创建一个从开始、顶部、结束和底部的偏移量创建insets。 //start表示偏移的开始方向,end表示结束方向的偏移量 const EdgeInsetsDirectional.fromSTEB(this.start, this.top, this.end,this.bottom); //2.创建一个仅使用给定的非零值的insets。 const EdgeInsetsDirectional.only({ this.start = 0.0, this.top = 0.0, this.end = 0.0, this.bottom = 0.0 }); //3.创建一个每个方向偏移量都为0的对象 static const EdgeInsetsDirectional zero = EdgeInsetsDirectional.only();
-
-
3、color、decoration和foregroundDecoration
-
color
:背景颜色,不可与decoration
同时使用。 -
decoration`:背景装饰
- 不设置时默认使用
color
值作为颜色。不可与color
同时使用; - 参数类型为
Decoration
- 不设置时默认使用
-
foregroundDecoration
:前景装饰- 参数类型为
Decoration
- 参数类型为
-
Decoration
为抽象类,系统提供了以下4个实现类:-
BoxDecoration
:绘制一个不可变的矩形或圆形框- 该框有边框、一个主体,并且可以投射盒子后面的阴影。
- 盒子的主体是分层绘制的。最下面的一层是
color
填充框,上面是gradient
,它也填充了这个框,最后是image
,它的精确对齐由DecorationImage
类控制。border
涂在主体上,箱形阴影boxShadow
会在它下面绘制。 - 它的构造方法和参数说明如下:
const BoxDecoration({ //填充框的背景颜色;如果为空,则不会绘制背景颜色 this.color, //使填充框有渐变效果;此属性作用在color上,即此属性不为空,则忽略color //参数类型:Gradient,有三个实现类: //线性LinearGradient、圆形RadialGradient和扫描SweepGradient this.gradient, //在背景color和渐变gradient上面绘制图片;如果为空,则不会绘制背景图片 //图片的形状有shape决定 //参数类型:DecorationImage this.image, //在color、渐变和图片上方绘制边框;如果为空,则不会绘制边框 //形状由shape和borderRadius决定 //参数类型:BoxBorder,有两个实现类Border(无方向)和BorderDirectional(有方向) this.border, //边框的半径,只适合shape为矩形时。如果shape为圆形circle,则此属性必须为空 //参数类型:BorderRadiusGeometry,两个实现类BorderRadius和BorderRadiusDirectional this.borderRadius, //盒子后面的盒子投射的阴影列表,阴影跟随shape的形状 //参数类型:List<BoxShadow>,含有BoxShadow类型的集合 this.boxShadow, //在画布上作画时使用的算法;默认为BlendMode.srcOver //参数类型:BlendMode的枚举值 this.backgroundBlendMode, //填充框的形状,包含矩形rectangle和圆形circle两种;默认为矩形rectangle //此属性必不能为空 //参数类型:BoxShape的枚举值 this.shape = BoxShape.rectangle, }) : assert(shape != null), assert( identical(backgroundBlendMode, null) || color != null || gradient != null, 'backgroundBlendMode applies to BoxDecoration\'s background color or ' 'gradient, but no color or gradient were provided.' );
- 使用:
new Container( color: Color(0xFFC5CAE9), alignment: FractionalOffset.center, width: 300.0,height: 300.0, child: new Container( width: 300.0,height: 300.0, alignment: FractionalOffset.center, padding: EdgeInsets.all(10.0), margin: EdgeInsets.all(10.0), //背景颜色,不可以decoration以前设置,因为decoration不设置时默认使用这个color颜色 // color: Colors.white, //背景装饰,不能与color属性同时出现,即在child后面,可设置颜色,形状,图片等 decoration: BoxDecoration( //背景颜色,最底层 color: Colors.brown, //渐变,第二层 gradient: LinearGradient(//线性渐变 //渐变的颜色集合 colors: [Colors.red, Colors.green, Colors.blue], ), //背景图片,第三层 image: DecorationImage(image: AssetImage('images/b.jpg')), //边框,第四层 border: Border( left: BorderSide(width: 4.0), right: BorderSide(color: Colors.purpleAccent,width: 4.0), top: BorderSide(color: Colors.yellowAccent,width: 4.0), bottom: BorderSide(color: Colors.pink,width: 4.0), ), //画布上绘画使用的算法,默认为srcOver,我也不懂原理 backgroundBlendMode: BlendMode.srcOver, //边框圆角半径,shape为圆形时不能设置,而且边框不同颜色设置时也不能设置圆角 // borderRadius: BorderRadius.all(Radius.circular(20.0)), //阴影集合 boxShadow: [ BoxShadow( //阴影的颜色 color: const Color(0xFF000000), //x轴和y轴的偏移量 offset: Offset(1.0, 1.0), //高斯函数与盒子形状卷积的标准偏差。 blurRadius: 1.0, //在使用模糊效果之前,框的膨胀量。 spreadRadius: 1.0, ) ], //形状,矩形 shape: BoxShape.rectangle, ), //前景装饰,即在child前面,会遮住child,可以更改颜色透明度来使其可见 foregroundDecoration: BoxDecoration( gradient: RadialGradient(//圆形渐变,还有个扫描式渐变SweepGradient colors: [Colors.red, Colors.green, Colors.blue], ), //边框 border: Border.all(width: 5.0, color: Colors.white), //边框的角度,圆形时不能设置圆角半径 // borderRadius: BorderRadius.all(Radius.circular(20.0)), //形状,圆形 shape: BoxShape.circle, ), child: Container( color: Colors.lightBlueAccent, child: new Text( 'The lightBlue is text.\nThe center is container.\nThe gray is parentContainer.', textAlign: TextAlign.center, ), ), ), ),
- 效果:
-
ShapeDecoration
:绘制一个不可变的任意形状。- 该框有边框、一个主体,并且可以投射盒子后面的阴影。
- 提供了一种绘制边框的方法,可以选择填充颜色或渐变,可以选择在其中绘制图像,也可以选择投射阴影。
- 提供的构造方法如下:
const ShapeDecoration({ //颜色,与渐变gradient冲突,至少有一个为null //如果为null则不设置颜色 this.color, //图片,如果为null则不设置图片 this.image, //渐变,与颜色color冲突,至少有一个为null //如果为null则不设置渐变 this.gradient, //阴影,如果为null则不设置阴影 this.shadows, //必写属性,而且必不能为null //填充[颜色]、[渐变]和[图像]的形状,并作为[阴影]投射。 //提供了8个实现类,如下: //Border:一个具有上下左右四个边的盒子 //BorderDirectional:一个有四个边的盒子,边的侧面(左右两边)根据阅读方向翻转,使用这个会报错,很惭愧,没有找到原因 //CircleBorder:在可用空间内适合圆形的边框。 //UnderlineInputBorder:容器的底部绘制一条水平线,并定义容器的形状。常用于InputDecoration //OutlineInputBorder:容器周围绘制一个圆角矩形。常用于InputDecoration //RoundedRectangleBorder:带有圆角的矩形边框。 //StadiumBorder:体育场形状,即长边两边是半圆 //BeveledRectangleBorder:有扁平或“斜角”的矩形边框。 @required this.shape, }) : assert(!(color != null && gradient != null)),//冲突设置 assert(shape != null);
- 使用:
new Container( color: Color(0xFFC5CAE9), alignment: AlignmentDirectional.center, width: 300.0,height: 300.0, child: new Container( width: 300.0,height: 300.0, alignment: AlignmentDirectional.center, margin: EdgeInsets.all(10.0), padding: EdgeInsets.all(10.0), //背景装饰,不能与color属性同时出现,即在child后面,可设置颜色,形状,图片等 decoration: ShapeDecoration( //颜色,最底层,与渐变gradient冲突 // color: Colors.brown, //渐变,第二层 gradient: LinearGradient(colors: [Colors.red, Colors.green, Colors.blue]), //背景图片,第三层 image: DecorationImage( image: AssetImage('images/b.jpg'), fit: BoxFit.cover), //阴影集合 /*shadows: [],*/ //形状 shape: Border.all( color: Colors.red,//边框颜色 width: 4.0,//边框宽度 ) +//加号可以使这三个边框从内向外叠加,以下都是可以叠加的 Border.all(color: Colors.green, width: 4.0,) + Border.all(color: Colors.blue, width: 4.0, //圆形边框 /* CircleBorder( side: BorderSide( color: Colors.green,width: 4.0) ) + CircleBorder( side: BorderSide(color: Colors.red, width: 4.0) ) */ //在底部画条水平线。可以改变形状, /*UnderlineInputBorder( borderSide: BorderSide(color: Colors.purpleAccent, width: 14.0), borderRadius: BorderRadius.all(Radius.circular(7.0)), )+ UnderlineInputBorder( borderSide: BorderSide(color: Colors.lightBlueAccent, width: 14.0), borderRadius: BorderRadius.all(Radius.circular(7.0)), )*/ //容器周围绘制一个圆角矩形 /* OutlineInputBorder( borderSide: BorderSide(color: Colors.purpleAccent,width: 5.0), borderRadius: BorderRadius.all(Radius.circular(20.0)), )*/ //带有圆角的矩形边框 /* RoundedRectangleBorder( side: BorderSide(color: Colors.purpleAccent, width: 5.0), borderRadius: BorderRadius.all(Radius.circular(20.0)), )*/ //体育场边框 /*StadiumBorder( side: BorderSide(color: Colors.purpleAccent,width: 5.0), )*/ //斜角边框 /*BeveledRectangleBorder( side: BorderSide(color: Colors.purpleAccent,width: 5.0), borderRadius: BorderRadius.all(Radius.circular(20.0)), )*/ ), child: Container( color: Colors.lightBlueAccent, child: new Text( 'The lightBlue is text.\nThe center is container.\nThe gray is parentContainer.', textAlign: TextAlign.center, ), ), ), ),
- 效果:
-
FlutterLogoDecoration
:- 绘制一个不变的Flutter的logo和标签。
- 构造函数如下:
const FlutterLogoDecoration({ //这两个是logo的颜色,提倡使用默认的 this.lightColor = const Color(0xFF42A5F5), // Colors.blue[400] this.darkColor = const Color(0xFF0D47A1), // Colors.blue[900] //Flutter文本标签的颜色 this.textColor = const Color(0xFF616161), //logo样式,分三种FlutterLogoStyle的枚举值,如下: //markOnly:默认值,表示只显示logo,不显示标签 //horizontal:表示从左到右显示logo和标签 //stacked: this.style = FlutterLogoStyle.markOnly, //距离边框的间距 this.margin = EdgeInsets.zero, }) : assert(lightColor != null), assert(darkColor != null), assert(textColor != null), assert(style != null), assert(margin != null), _position = style == FlutterLogoStyle.markOnly ? 0.0 : style == FlutterLogoStyle.horizontal ? 1.0 : -1.0, _opacity = 1.0;
- 使用
decoration: FlutterLogoDecoration( lightColor: const Color(0xFF42A5F5), darkColor : const Color(0xFF0D47A1), textColor : const Color(0xFF616161), style: FlutterLogoStyle.stacked, margin : EdgeInsets.zero, ),
- 效果:
-
UnderlineTabIndicator
:- 使用
TabBar.indicator
属性时,在选定的选项卡下方画一条水平线。 - 构造函数如下
const UnderlineTabIndicator({ //边框宽度和颜色 this.borderSide = const BorderSide(width: 2.0, color: Colors.white), this.insets = EdgeInsets.zero,//距选项卡的间距 }) : assert(borderSide != null), assert(insets != null);
- 使用
new TabBar( labelColor: Colors.yellowAccent, unselectedLabelColor: Colors.white, tabs: myTabs, controller: _tabController, //紫色 indicator: UnderlineTabIndicator( borderSide: BorderSide(color: Colors.purpleAccent, width: 4.0), insets: EdgeInsets.zero, ), ), ),
- 效果
- 使用
-
4、constraints
-
对容器宽高的限制,同时作用在child上
-
可设置容器最大宽高和最小宽高;
-
参数类型为:
BoxConstraints
,其构造函数如下:const BoxConstraints({ //最小宽高的限制,默认是0,不可为负 this.minWidth = 0.0,this.minHeight = 0.0, //最大宽高的限制,默认为double.infinity,不可为负 this.maxWidth = double.infinity,this.maxHeight = double.infinity });
-
使用
new Container(
color: Color(0xFFC5CAE9),
alignment: AlignmentDirectional.center,
width: 300.0,
height: 300.0,
child: new Container(
alignment: Alignment.center,
width: 300.0,
height: 300.0,
margin: EdgeInsets.all(10.0),
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/b.jpg'), fit: BoxFit.cover),
border: Border(
left: BorderSide(width: 4.0),
right: BorderSide(color: Colors.purpleAccent, width: 4.0),
top: BorderSide(color: Colors.yellowAccent,width: 4.0),
bottom: BorderSide(color: Colors.pink,width: 4.0),
),
shape: BoxShape.rectangle,
),
//宽高的限制
constraints: BoxConstraints(
maxWidth: 250.0,
maxHeight: 300.0,
),
child: Container(
alignment: Alignment.center,
width: 300.0,
height: 300.0,
color: Colors.lightBlueAccent,
child: new Text(
'The lightBlue is text.\nThe center is container.\nThe gray is parentContainer.',
textAlign: TextAlign.center,
),
),
),
),
- 效果:
5、transform
- 在绘制容器之前应用的转换矩阵。
- 提供了一些方法,将容器旋转,平移等效果。
- 参数类型为:
Matrix4
,构造函数如下:
//延X轴旋转,参数为弧度
factory Matrix4.rotationX(double radians)
//延Y轴旋转
factory Matrix4.rotationY(double radians)
//延Z轴旋转
factory Matrix4.rotationZ(double radians)
//延给定的空间向量方向平移,参数为xyz方向的空间向量 参数为像素
factory Matrix4.translation(Vector3 translation)
//延给定的X,Y,Z方向平移
factory Matrix4.translationValues(double x, double y, double z)
//延空间向量方向缩放
factory Matrix4.diagonal3(Vector3 scale)
//延给定的X,Y,Z方法缩放,参数为这个三个方向的缩放大小
factory Matrix4.diagonal3Values(double x, double y, double z)
//绕X轴的斜矩阵
factory Matrix4.skewX(double alpha)
//绕Y轴的斜矩阵
factory Matrix4.skewY(double beta)
//绕X轴和Y轴的斜矩阵
factory Matrix4.skew(double alpha, double beta)
- 使用
Container(
color: Color(0xFFC5CAE9),
width: 250.0,height: 200.0,
alignment: Alignment.center,
child: Container(
color: Colors.cyanAccent,
child: Text('transform参数'),
transform: Matrix4.diagonal3(Vector3.all(1.5)),
),
)
- 效果如下:
(三)绘制流程
- 源码
@override
Widget build(BuildContext context) {
Widget current = child;
//先画child
//首先判断child是否为空,和约束是否为空或是否正确(最小约束是否大于等于最大约束,
//如果返回true,则将LimitedBox赋值给current,即child,并为其设置约束
//返回false,则使用当前设置的child和约束
if (child == null && (constraints == null || !constraints.isTight)) {
current = LimitedBox(
maxWidth: 0.0,
maxHeight: 0.0,
child: ConstrainedBox(constraints: const BoxConstraints.expand())
);
}
//再画alignment
//判断对齐方式,不为空则设置对齐方式
if (alignment != null)
current = Align(alignment: alignment, child: current);
//再画padding
//这里设置间距,首先判断边框decoration的padding,如果没有直接使用容器的padding,
//如果有,则将容器的padding和边框的padding相加设置给child
final EdgeInsetsGeometry effectivePadding = _paddingIncludingDecoration;
if (effectivePadding != null)
current = Padding(padding: effectivePadding, child: current);
//再画decoration背景
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);
//再画margin,由此可看出margin也是有Padding实现
if (margin != null)
current = Padding(padding: margin, child: current);
//再画旋转规则
if (transform != null)
current = Transform(transform: transform, child: current);
//完成
return current;
}
- 图示:从内到外绘制
三、定位型布局
1、Padding
- 为child提供内边距的布局。功能比较单一
- 父类为
SingleChildRenderObjectWidget
,大部分单子元素布局的父类,提供了单个child的存储布局,不提供实际的更新逻辑 - 构造函数如下:
const Padding({Key key,
//必传参数,并且必不为空,
//与Container的padding属性一样,详情请看上面介绍
@required this.padding,
Widget child,
}) : assert(padding != null),
super(key: key, child: child);
2、Align和Center
-
Align
:将child在内部对齐,并可以调整其大小。- 父类为:
SingleChildRenderObjectWidget
- 构造函数如下:
const Align({Key key, //对齐方式,详情请移步Container的alignment属性,两者一样 this.alignment = Alignment.center, //宽度因子,如果设置并且Align没有约束时,Align的宽度就是child的宽度乘以这个值,不能为负数。 this.widthFactor, //高度因子,如果设置并且Align没有约束时,Align的高度就是child的高度乘以这个值,不能为负数。 this.heightFactor, Widget child }) : assert(alignment != null), assert(widthFactor == null || widthFactor >= 0.0), assert(heightFactor == null || heightFactor >= 0.0), super(key: key, child: child);
-
当
widthFactor
和heightFactor
为null
时,- 当其有限制条件的时候,
Align
会根据限制条件尽量的扩展自己的尺寸; - 当没有限制条件的时候,会调整到child的尺寸;
- 当其有限制条件的时候,
-
当
widthFactor
或者heightFactor
不为null
时,- 当其有限制条件的时候,
Align
会根据限制条件尽量的扩展自己的尺寸; - 当没有限制条件的时候,
Aligin
会根据factor
属性,扩展自己的尺寸,例如widthFactor
为2.0的时候,那么,Align
的宽度将会是child的两倍。
- 当其有限制条件的时候,
-
使用:
Container( color: Color(0xFFC5CAE9), // width: 150.0, height: 150.0, child: new Align( alignment: Alignment.center, //宽高因子,觉得Align的大小,与child对应的宽高相乘 widthFactor: 2.0, heightFactor: 2.0, child: Container( color: Colors.cyanAccent, width: 100.0, height: 20.0, child: Text('This is Padding'), ), ), )
-
效果:
- 父类为:
-
Center
:将child居中显示- 父类为:
Align
,只不过是将alignment
设置为Alignment.center
; - 与
Align
一样使用。
- 父类为:
3、Baseline
- 根据child的基线定位child距顶部的距离的布局控件。
- 父类为:
SingleChildRenderObjectWidget
- 构造函数如下:
const Baseline({Key key,
//child的基线距离此框顶部的逻辑像素的数量。
@required this.baseline,
//child的基线类型,TextBaseline的枚举值,如下:
//TextBaseline.alphabetic 对齐字母字符的字形底部的水平线。
//TextBaseline.ideographic 对齐表意文字的水平线。
@required this.baselineType,
Widget child
}) : assert(baseline != null),
assert(baselineType != null),
super(key: key, child: child);
- 使用
Container(
color: Color(0xFFC5CAE9),
width: 200.0,
height: 60.0,
child: new Baseline(
baseline: 0.0,
baselineType: TextBaseline.alphabetic,
child: new Container(
color: Colors.cyanAccent,
child: new Text('this is Baseline yjd基线p'),
),
),
)
- 效果
四、约束型布局
1、LimitedBox
- child只有在不受约束的情况下才限制其大小的盒子。
- 限制child的最大宽和高;
- 通常使用在列表中,以试图匹配它们父级的大小,以便它们在列表中表现得合理;
- 当在
Row
横向列表中,高度不受约束;当在Column
中,宽度不约束; - 构造函数如下:
const LimitedBox({ Key key,
//限定的最大宽、高,默认值是double.infinity,不能为负数。
this.maxWidth = double.infinity,
this.maxHeight = double.infinity,
Widget child,
}) : assert(maxWidth != null && maxWidth >= 0.0),
assert(maxHeight != null && maxHeight >= 0.0),
super(key: key, child: child);
- 使用:
Column(
children: <Widget>[
Container(color: Color(0xFFC5CAE9),width: 100.0, height: 100.0),
LimitedBox(
maxWidth: 40.0,maxHeight: 14.0,
child: Container(
width: 130.0,height: 70.0,
alignment: Alignment.center,
color: Colors.cyanAccent,
child: Text('This is Transform'),
),
),
],
)
//--------------Row-----------------------
Row(
children: <Widget>[
Container(color: Color(0xFFC5CAE9),width: 100.0, height: 100.0),
LimitedBox(
//限定的最大宽、高,默认值是double.infinity,不能为负数。
maxWidth: 40.0,maxHeight: 14.0,
child: Container(
width: 130.0,height: 70.0,
alignment: Alignment.center,
color: Colors.cyanAccent,
child: Text('This is Transform'),
),
),
],
)
- 效果
2、ConstrainedBox
- 对child施加额外的约束,对其宽高的最大值和最小值进行了限制;
- 构造函数如下:
ConstrainedBox({
Key key,
//必传参数,且不可为空,详情请看Container的constraints属性。
@required this.constraints,
Widget child
}) : assert(constraints != null),
assert(constraints.debugAssertIsValid()),
super(key: key, child: child);
- 使用
ConstrainedBox(
constraints:BoxConstraints(
//child最小不会小于这对值
minHeight: 20.0, minWidth: 80.0,
//child最大不会超过这对值
maxHeight: 40.0,maxWidth: 150.0,
),
child: Container(
width: 200.0,height: 70.0,
alignment: Alignment.center,
color: Colors.cyanAccent,
child: Text('This is ConstrainedBox'),
),
)
- 效果
3、FittedBox(包含定位)
- 根据
fit
设置值,在自己体内对孩子进行缩放和定位。 - 父类为:
SingleChildRenderObjectWidget
- 如果没有外部约束,则
FittedBox
与child的大小一致,指定的缩放和定位将失效 - 如果有外部约束,则将自身大小按外部约束调整,child则响应指定的缩放和定位设置
- 构造函数如下:
const FittedBox({Key key,
//child缩放放置的方式,参数值为BoxFit的枚举值,不可为空默认为BoxFit.contain
this.fit = BoxFit.contain,
//对齐方式,同上面介绍
this.alignment = Alignment.center,
Widget child,
}) : assert(fit != null),
assert(alignment != null),
super(key: key, child: child);
BoxFit
的枚举值如下:
enum BoxFit {
///child将按照FitBox的宽高比充满目标框,child将会变形
fill,
///尽可能大的将child完全包含在目标框中。不改变child的宽高比
///即不改变child宽高比下,同时延长child的宽高,当第一个边达到目标框对应边的大小时,则停止延长
contain,
///尽可能小的将child覆盖整个目标框。不改变child的宽高比,但会丢弃溢出部分
///即不改变child宽高比下,同时延长child的宽高,当最后的边达到目标框对应边的大小时,则停止延长
cover,
///不改变child的宽高比,将child的宽填满目标框的宽,但高如果溢出将会丢弃溢出部分
fitWidth,
///不改变child的宽高比,将child的高填满目标框的高,但宽如果溢出将会丢弃溢出部分
fitHeight,
///child尺寸不变,将child对齐在目标框中(默认为居中),但如果child宽高大于目标框,则会丢弃溢出部分
none,
///将child对齐在目标框中(默认为居中),
///如果child的宽或高大于目标框时,即需要缩放,则效果同contain
///如果child宽和高小于目标框时,即不需要缩放,则效果同none
scaleDown,
}
- 使用
Container(
color: Color(0xFFC5CAE9),
//如果不设置宽高(即没有外部约束),
//则FittedBox的大小将与child大小一致,所设置的fit和alignment将不起作用
width: 200.0,height: 80.0,
child: new FittedBox(
fit: BoxFit.scaleDown, //调整child大小的方式
alignment: Alignment.center, //child的对齐方式
child: new Container(
width: 220.0,
height: 100.0,
color: Colors.purpleAccent,
child: new Text('FittedBox'),
),
),
)
- 效果
4、AspectRatio
- 根据指定的宽高比来调整child的大小
- 父类为:
SingleChildRenderObjectWidget
- 注意事项:
- 当
AspectRatio
父布局同时指定了宽和高时,则其设置的宽高比aspectRatio
将失效; aspectRatio
与父布局的宽或高其中的一个配合使用才会生效。- 当其自身有约束时:
- 如果约束值范围,包含
AspectRatio
根据宽高比算出的大小时,则使用计算出的尺寸; - 如果约束值范围,不包含根据宽高比算的的尺寸时,则放弃使用计算出的尺寸,而使用约束值。
- 如果约束值范围,包含
- 当
- 构造函数如下:
const AspectRatio({Key key,
//必传参数,并且能为空;调整child要使用的宽高比
@required this.aspectRatio,
Widget child
}) : assert(aspectRatio != null),
super(key: key, child: child);
- 使用
Container(
color: Color(0xFFC5CAE9),
padding: EdgeInsets.all(4.0),
// width:200.0,
height: 40.0,
//当设置约束时,AspectRatio最终尺寸不在这个范围内时,则以约束为准
// constraints: BoxConstraints(maxWidth: 110.0,maxHeight: 35.0),
child: new AspectRatio(
aspectRatio: 3.0, //宽高比(宽/高),等于1时相当于宽高相等,将child按这个比值设置大小
child: new Container(
alignment: Alignment.center,
color: Colors.cyanAccent,
child: new Text('AspectRatio'),
),
),
)
- 效果
5、SizedBox
- 创建一个指定宽高的框,将child的大小固定
- 父类为:
SingleChildRenderObjectWidget
- 设置宽和高时,取决于父框有没有限制
- 当父框有限制时:则依赖父框限制设置其大小,
- 当父框没有限制时:则使用设置的宽高
- 构造函数如下:
const SizedBox({ Key key,
//指定child的宽,高
this.width,.height,
Widget child }): super(key: key, child: child);
//child在父框允许的范围填充
const SizedBox.expand({ Key key, Widget child })
: width = double.infinity,
height = double.infinity,
super(key: key, child: child);
//child在父类的范围内尽可能的小,即没有
const SizedBox.shrink({ Key key, Widget child })
: width = 0.0,
height = 0.0,
super(key: key, child: child);
//通过Size将宽高传给child
SizedBox.fromSize({ Key key,
Widget child,
Size size,//Size内包含宽和高
}): width = size?.width,
height = size?.height,
super(key: key, child: child);
- 使用:
Container(
width: 180.0,
height: 100.0,
padding: EdgeInsets.all(5.0),
color: Colors.red,//红色大背景
alignment: Alignment.center,
child: Container(
color: Color(0xFFC5CAE9),//灰色,SizedBox的父框颜色
padding: EdgeInsets.all(10.0),
child: new SizedBox(
width: 120.0,
height: 50.0,
child: Container(
color: Colors.cyanAccent,//SizeBox的child颜色,也相当于Size的Box的颜色
),
),
),
)
- 效果如下:
6、OverflowBox(带定位)
- 对子部件施加的约束与从父部件获得的约束不同,可能允许子部件溢出父部件。
- 父类为:
SingleChildRenderObjectWidget
- 构造函数如下:
const OverflowBox({Key key,
this.alignment = Alignment.center,//对齐方式
this.minWidth,//对child的最小宽度约束,如果为null(默认),则使用父元素的约束
this.maxWidth,//对child的最大宽度约束,如果为null(默认),则使用父元素的约束
this.minHeight,//对child的最小高度约束,如果为null(默认),则使用父元素的约束
this.maxHeight,//对child的最大高度约束,如果为null(默认),则使用父元素的约束
Widget child,
}) : super(key: key, child: child);
- 使用
Container(
width: 200.0,height: 200.0,
padding: EdgeInsets.all(5.0),//内边距5
color: Color(0xFFC5CAE9),//父框背景色--灰色
child: new OverflowBox(
alignment: Alignment.topLeft,//左上对齐
maxWidth: 300.0,
maxHeight: 250.0,
child: new Container(
color: Colors.cyanAccent,//相当于OverflowBox背景色---浅蓝色
width: 150.0, height: 300.0,
),
),
),
- 效果如下:
7、SizedOverflowBox(带定位)
- 具有特定大小的小部件,但将其原始约束传递给子部件,然后子部件可能会溢出。
- 与6相似,但效果未出来!!!!!放弃吧。。。。。
8、FractionallySizedBox(带定位)
- 将child的大小调整到总可用空间的一小部分。超出会溢出
- 父类为:
SingleChildRenderObjectWidget
; - 构造函数如下:
- 当宽高因子不为空时,最终指定给child的大小为,约束的宽高 乘以对应的宽高因子。
const FractionallySizedBox({Key key,
this.alignment = Alignment.center,//对齐方式默认居中
this.widthFactor,//宽因子,不可为负
this.heightFactor,//高因子,不可为负
Widget child,
}) : assert(alignment != null),
assert(widthFactor == null || widthFactor >= 0.0),
assert(heightFactor == null || heightFactor >= 0.0),
super(key: key, child: child);
- 使用
Container(
width: 150.0,height: 100.0,
padding: EdgeInsets.all(5.0),
color: Color(0xFFC5CAE9),//父框颜色 灰色
child: new FractionallySizedBox(
alignment : Alignment.topLeft,//左上对齐
//宽高因子
widthFactor:0.5, heightFactor:0.5,
child: new Container(
alignment: Alignment.center,
color: Colors.cyanAccent,
),
),
)
- 效果 如下
9、IntrinsicHeight
- 一个小部件,它根据一个child的固有高度调整child组的高度;
- 父类为:
SingleChildRenderObjectWidget
; - 官方说很有用,但效率低,因为在最终布局阶段之前添加了一个推测布局传递,因此 不建议使用。
- 他的作用,是将可能高度不受限制的child,调整到一个合适并且合理的尺寸。
- 构造函数如下:
const IntrinsicHeight({Key key,
//只有一个child属性
Widget child
}) : super(key: key, child: child);
- 使用:
new IntrinsicHeight(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Container(color: Colors.blue, width: 100.0),
new Container(color: Colors.red, width: 50.0, height: 50.0),
new Container(color: Colors.cyanAccent, width: 70.0, height: 70.0),
new Container(color: Colors.yellow, width: 130.0),
],
),
)
- 效果:
10、IntrinsicWidth
- 一个小部件,它根据一个child的固有宽度调整child组的宽度;
- 父类为:
SingleChildRenderObjectWidget
; - 跟
IntrinsicHeight
类似,存在效率问题,建议能不用就不用; - 构造函数如下:
stepHeight
:不为空时- 当设置的值小于等于child的总高度时,则将把
IntrinsicWidth
的高度设置成stepHeight
的倍数,使最终值接近child的总高度;如例中:此值为100
,则最终值为100*3=300
。 - 当设置高度大于child总高度时,最终就是设置的值。
- 当设置的值小于等于child的总高度时,则将把
stepWidth
:不为空时:- 当设置的值小于等于child组内固定值中最大的值时,则将把
IntrinsicWidth
的宽度设置成stepWidth
的倍数,使最终值接近child的最大值;如例:此值为40
,则最终值为40*2 =80
。 - 当设置高度大于child宽度最大值时,最终就是设置的值。
- 当设置的值小于等于child组内固定值中最大的值时,则将把
const IntrinsicWidth({ Key key,
//参数类型为double
//如果不为null,则强制child宽度为该值,
//或该值的/*倍数*/(该值大小小于等于child内总宽度时),使其大小接近近child内总宽度
this.stepWidth,
//如果不为null,则强制child高度为该值,
//或该值的/*倍数*/(该值大小小于等于child内总高度时),使其大小接近近child内总高度
this.stepHeight,
Widget child
}): super(key: key, child: child);
- 使用
Container(
color:Color(0xFFC5CAE9),
child: new IntrinsicWidth(
//
stepHeight: 27.0,
stepWidth: 10.0,
child: new Column(
children: <Widget>[
new Container(color: Colors.blue, height: 60.0),
new Container(color: Colors.cyanAccent, width: 50.0, height: 50.0),
new Container(color: Colors.red, width: 70.0, height: 70.0),
new Container(color: Colors.yellow, height: 90.0),
],
),
),
)
- 效果如下
五、Offstage
- 控制child是否隐藏,默认是隐藏
- 父类为:
SingleChildRenderObjectWidget
- 构造函数如下:通过参数
offstage
决定- 当offstage为true,当前控件不会被绘制在屏幕上,不会响应点击事件,也不会占用空间;
- 当Offstage不可见的时候,如果child有动画,应该手动停掉,Offstage并不会停掉动画;
- 当offstage为false,当前控件则跟平常用的控件一样渲染绘制。
const Offstage({ Key key,
//通过这个参数控制child的是否隐藏,默认为true隐藏
this.offstage = true,
Widget child }): assert(offstage != null),super(key: key, child: child);
- 使用
class _SingleChildFullPageState extends State<SingleChildFullPage> {
bool offstage = false;
var hint = '隐藏';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar( title: Text('Offstage')),
body: Column(
children: <Widget>[
RaisedButton(
//点击更改offstage属性,即点击隐藏或显示Offstage的child
onPressed: () {
setState(() {
offstage = !offstage;
hint = offstage ? '显示' : '隐藏';
});
},
child: Text(hint),
),
Offstage(
offstage: offstage,
child: Container(color: Colors.cyanAccent,child: Text('Offstage')),
),
],
),
);
}
}
- 效果如下:
六、Transform(带定位)
- 在绘制容器之前做转换矩阵;
- 可以对child做平移、旋转、缩放等操作。
- 父类为:
SingleChildRenderObjectWidget
- 构造函数如下:
//默认构造函数
const Transform({Key key,
//必填参数,且不能为空,参数类型为Matrix4(变形矩阵)
@required this.transform,
this.origin,//平移的偏移量
this.alignment,//对齐方式
this.transformHitTests = true,//是否在执行hit测试时应用转换。默认为true
Widget child,}) : assert(transform != null), super(key: key, child: child);
//旋转功能的构造函数
Transform.rotate({Key key,
//必填参数,旋转的角度
@required double angle,
this.origin,
this.alignment = Alignment.center,
this.transformHitTests = true,
Widget child,}) : transform = Matrix4.rotationZ(angle),super(key: key, child: child);
//平移功能的构造函数
Transform.translate({Key key,
//必填,平移的偏移量
@required Offset offset,
this.transformHitTests = true,
Widget child,
}) : transform = Matrix4.translationValues(offset.dx, offset.dy, 0.0),
origin = null,alignment = null,super(key: key, child: child);
//缩放功能的构造函数
Transform.scale({Key key,
//必填 缩放大小
@required double scale,
this.origin,
this.alignment = Alignment.center,
this.transformHitTests = true,
Widget child,
}) : transform = Matrix4.diagonal3Values(scale, scale, 1.0),
super(key: key, child: child);
- 使用,以默认构造为例,延Z轴旋转,其他的直接传参即可
Container(
color:Color(0xFFC5CAE9),
width: 250.0,
height: 200.0,
alignment: Alignment.center,
child: Transform(
transform: Matrix4.rotationZ(0.5),
child: Container(
color: Colors.blue,
width: 100.0,
alignment: Alignment.center,
height: 100.0,
child: Text('Transform'),
),
),
)
- 效果如下
七、CustomSingleChildLayout
- 通过一个代理来控制child的布局,也可理解为自定义的单元素布局。
- 这个代理
delegate
可以控制child的约束constaints
和位置;在parent的尺寸不依赖child的情况下,还可以对顶parent的尺寸。 - 父类为:
SingleChildRenderObjectWidget
- 构造函数:
const CustomSingleChildLayout({Key key,
//必传非空参数,一个代理,类型为SingleChildLayoutDelegate,
//需要自己自定义实现类
@required this.delegate,
Widget child
}) : assert(delegate != null),
super(key: key, child: child);
- SingleChildLayoutDelegate抽象类
abstract class SingleChildLayoutDelegate {
//提供的构造函数,可以传一个监听,来监听relayout
const SingleChildLayoutDelegate({ Listenable relayout }) : _relayout = relayout;
final Listenable _relayout;
//传入约束的此对象的大小。
//默认为满足给定约束的最大大小。
Size getSize(BoxConstraints constraints) => constraints.biggest;
//传入约束的child的约束。
//在布局过程中,child会得到这个函数返回的布局约束。child需要为自己选择一个满足这些约束的大小。
//默认为传入的指定的约束。
BoxConstraints getConstraintsForChild(BoxConstraints constraints) => constraints;
/**
* 放置child的位置
* @param size 父元素的大小
* @param childSize 子元素的大小
*
* 默认将子元素定位在父元素的左上角。
*/
Offset getPositionForChild(Size size, Size childSize) => Offset.zero;
/**
* 判断是否需要重新布局的。
* 我们在自定义写delegate的时候,可以选择需要进行修改的属性进行重写,并给予相应的布局属性即可。
* 返回true时,则需要relayout。false时不需要
*
* @param oldDelegate 老的布局,可以依据这个与新布局对比,不一样则返回true
*/
bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate);
}
- 布局源码
@override
void performLayout() {
//首先通过传来的代理,获取设置尺寸大小
size = _getSize(constraints);
if (child != null) { //child不为空时
//通过传来的代理,获取设置child的约束
final BoxConstraints childConstraints = delegate.getConstraintsForChild(constraints);
assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));
child.layout(childConstraints, parentUsesSize: !childConstraints.isTight);
final BoxParentData childParentData = child.parentData;
//通过传来的代理,获取设置位置
childParentData.offset = delegate.getPositionForChild(size, childConstraints.isTight ? childConstraints.smallest : child.size);
}
}
- 使用
//这个只针对了尺寸做了设置
class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate {
FixedSizeLayoutDelegate(this.size);
final Size size;
@override
Size getSize(BoxConstraints constraints) => size;
//创建仅受给定大小约束的框约束。
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return new BoxConstraints.tight(size);
}
//这个只针对了尺寸,当尺寸改变时,才会relayout
@override
bool shouldRelayout(FixedSizeLayoutDelegate oldDelegate) {
return size != oldDelegate.size;
}
}
//-----------------------------------------------------
Container(
color: Color(0xFFC5CAE9),
width: 200.0,height: 150.0,
alignment: Alignment.center,
child: new CustomSingleChildLayout(
delegate: FixedSizeLayoutDelegate(
Size(170.0, 20.0),
),
child: Container(
child: Text('CustomSingleChildLayout'),color: Colors.cyanAccent),
),
)
- 效果