- Flutter和Dart系列文章
- 项目GitHub地址
- 容器类
Widget
和布局类Widget
都作用于其子Widget
,不同的是:
- 布局类
Widget
一般都需要接收一个widget
数组(children
),他们直接或间接继承自(或包含)MultiChildRenderObjectWidget
- 而容器类
Widget
一般只需要接受一个子Widget
(child
),他们直接或间接继承自(或包含)SingleChildRenderObjectWidget
- 布局类
Widget
是按照一定的排列方式来对其子Widget
进行排列
- 而容器类
Widget
一般只是包装其子Widget
,对其添加一些修饰(补白或背景色等)、变换(旋转或剪裁等)、或限制(大小等)。
Flutter
官方并没有对Widget
进行官方分类,我们对其分类主要是为了方便讨论和对Widget
功能的区分记忆
- 相关容器类
Widget
主要分为以下几种
- 填充类容器
Padding
- 布局限制类容器
ConstrainedBox
、SizeBox
- 装饰类容器
DecoratedBox
- 变换类容器
Transform
- 组合容器
Container
- 导航类容器
Scaffold
、TabBar
、AppBar
等
Padding
Padding
可以给其子元素设置内边距
1 2 3 4 5 6 7 8 9 10
| class Padding extends SingleChildRenderObjectWidget { const Padding({ Key key, @required this.padding, Widget child, }) final EdgeInsetsGeometry padding; }
|
EdgeInsetsGeometry
是一个抽象类,一般情况都使用EdgeInsets
,它是EdgeInsetsGeometry
的一个子类, 下面是的定义的一些方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class EdgeInsets extends EdgeInsetsGeometry { const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);
const EdgeInsets.all(double value) const EdgeInsets.only({ this.left = 0.0, this.top = 0.0, this.right = 0.0, this.bottom = 0.0 }); const EdgeInsets.symmetric({ double vertical = 0.0, double horizontal = 0.0 }) static const EdgeInsets zero = EdgeInsets.only(); }
|
示例
1 2 3 4 5 6 7 8 9
| class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.all(20), child: Icon(Icons.phone, color: Colors.cyan,), ); } }
|
ConstrainedBox
ConstrainedBox
和SizedBox
都是通过RenderConstrainedBox
来渲染的
SizedBox
只是ConstrainedBox
一个定制
ConstrainedBox
主要用于对子widget
添加额外的约束
1 2 3 4 5 6 7 8 9 10
| class ConstrainedBox extends SingleChildRenderObjectWidget { ConstrainedBox({ Key key, @required this.constraints, Widget child })
final BoxConstraints constraints;
|
BoxConstraints
BoxConstraints
设置Widget
的约束, 内部设置了四个属性: 最大/小宽度和最大小高度, 下面是其相关构造函数和实例函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class BoxConstraints extends Constraints { const BoxConstraints({ this.minWidth = 0.0, this.maxWidth = double.infinity, this.minHeight = 0.0, this.maxHeight = double.infinity });
BoxConstraints.tight(Size size) : minWidth = size.width, maxWidth = size.width, minHeight = size.height, maxHeight = size.height;
const BoxConstraints.tightFor({ double width, double height }): minWidth = width != null ? width : 0.0, maxWidth = width != null ? width : double.infinity, minHeight = height != null ? height : 0.0, maxHeight = height != null ? height : double.infinity;
const BoxConstraints.tightForFinite({ double width = double.infinity, double height = double.infinity }): minWidth = width != double.infinity ? width : 0.0, maxWidth = width != double.infinity ? width : double.infinity, minHeight = height != double.infinity ? height : 0.0, maxHeight = height != double.infinity ? height : double.infinity;
BoxConstraints.loose(Size size) : minWidth = 0.0, maxWidth = size.width, minHeight = 0.0, maxHeight = size.height; const BoxConstraints.expand({ double width, double height }): minWidth = width != null ? width : double.infinity, maxWidth = width != null ? width : double.infinity, minHeight = height != null ? height : double.infinity, maxHeight = height != null ? height : double.infinity; }
|
使用实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class ConstrainedBoxView extends StatelessWidget { @override Widget build(BuildContext context) { return ConstrainedBox( constraints: BoxConstraints( minWidth: double.infinity, minHeight: 50, ), child: Container( height: 10, child: DecoratedBox(decoration: BoxDecoration(color: Colors.orange)), ), ); } }
|
- 可以看到, 虽然将
Container
的高度设置为10像素,但是最终却是50像素,这正是ConstrainedBox
的最小高度限制生效了
- 如果将
Container
的高度设置为80像素,那么最终红色区域的高度也会是80像素,因为在此示例中,ConstrainedBox
只限制了最小高度,并未限制最大高度
SizedBox
SizedBox
用于给子widget
指定固定的宽高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class SizedBox extends SingleChildRenderObjectWidget { const SizedBox({ Key key, this.width, this.height, Widget child }) : super(key: key, child: child);
const SizedBox.expand({ Key key, Widget child }) : width = double.infinity, height = double.infinity, super(key: key, child: child);
const SizedBox.shrink({ Key key, Widget child }) : width = 0.0, height = 0.0, super(key: key, child: child);
SizedBox.fromSize({ Key key, Widget child, Size size }) : width = size?.width, height = size?.height, super(key: key, child: child); }
|
这里我们创建一个指定宽高的Widget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class SizedBoxView extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( width: 80, height: 80, child: Container( child: DecoratedBox(decoration: BoxDecoration(color: Colors.orange)), ), ); } }
ConstrainedBox( constraints: BoxConstraints.tightFor(width: 80.0,height: 80.0), child: Container( child: DecoratedBox(decoration: BoxDecoration(color: Colors.orange)), ), )
|
而实际上ConstrainedBox
和SizedBox
都是通过RenderConstrainedBox
来渲染的,我们可以看到ConstrainedBox
和SizedBox
的createRenderObject()
方法都返回的是一个RenderConstrainedBox
对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // SizedBox class SizedBox extends SingleChildRenderObjectWidget { RenderConstrainedBox createRenderObject(BuildContext context) { return RenderConstrainedBox( additionalConstraints: _additionalConstraints, ); }
BoxConstraints get _additionalConstraints { return BoxConstraints.tightFor(width: width, height: height); } }
// ConstrainedBox class ConstrainedBox extends SingleChildRenderObjectWidget { RenderConstrainedBox createRenderObject(BuildContext context) { return RenderConstrainedBox(additionalConstraints: constraints); }
final BoxConstraints constraints; }
|
- 如果某一个
widget
有多个父ConstrainedBox
限制
- 对于
minWidth
和minHeight
来说,是取父子中相应数值较大的。只有这样才能保证父限制与子限制不冲突
- 对于
maxWidth
和maxHeight
来说, 无效, 最终宽高都是0
UnconstrainedBox
1 2 3 4 5 6 7 8 9 10
| const UnconstrainedBox({ Key key, Widget child, this.textDirection, this.alignment = Alignment.center, this.constrainedAxis, })
|
UnconstrainedBox
不会对子Widget
产生任何限制,它允许其子Widget
按照其本身大小绘制
- 一般情况下,我们会很少直接使用此
widget
,但在”去除”多重限制的时候也许会有帮助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ConstrainedBox( constraints: BoxConstraints( minWidth: 60, minHeight: 100, ), child: UnconstrainedBox( textDirection: TextDirection.ltr, alignment: Alignment.center, constrainedAxis: Axis.horizontal, child: ConstrainedBox( constraints: BoxConstraints( minWidth: 90, minHeight: 20, ), child: DecoratedBox(decoration: BoxDecoration(color: Colors.red)), ), ) );
|
- 上面代码中,如果没有中间的
UnconstrainedBox
,那么根据上面所述的多重限制规则,那么最终将显示一个90×100的红色框
- 但是由于
UnconstrainedBox
“去除”了父ConstrainedBox
的限制,则最终会按照子ConstrainedBox
的限制来绘制红色框,即90×20:
- 但是需要注意,
UnconstrainedBox
对父限制的“去除”并非是真正的去除,上面例子中虽然红色区域大小是90×20,但上方仍然有80的空白空间。
- 也就是说父限制的
minHeight
(100.0)仍然是生效的,只不过它不影响最终子元素的大小,但仍然还是占有相应的空间,可以认为此时的父ConstrainedBox
是作用于子ConstrainedBox
上,而红色框只受子ConstrainedBox
限制,这一点请读者务必注意
- 并且目前没有什么方式能够彻底去除父
BoxConstraints
的限制
- 在定义一个通用的
widget
时,如果对子widget
指定限制时一定要注意,因为一旦指定限制条件,子widget
如果要进行相关自定义大小时将可能非常困难,因为子widget
在不更改父widget
的代码的情况下无法彻底去除其限制条件
AspectRatio
AspectRatio
组件是固定宽高比的组件
1 2 3 4 5 6
| const AspectRatio({ Key key, @required this.aspectRatio, Widget child, })
|
如果组件的宽度固定,希望高是宽的1/2,可以用AspectRatio
实现此效果
1 2 3 4
| AspectRatio( aspectRatio: 2 / 1, child: Container(color: Colors.red), )
|
FractionallySizedBox
设置一个控件的尺寸是相对尺寸时,比如当前按钮的宽度占父组件的70%,可以使用FractionallySizedBox
来实现此效果
1 2 3 4 5 6 7 8 9 10
| const FractionallySizedBox({ Key key, this.alignment = Alignment.center, this.widthFactor, this.heightFactor, Widget child, })
|
LimitedBox
LimitedBox
组件是当不受父组件约束时限制它的尺寸, LimitedBox
的父组件受到约束,此时LimitedBox
将会不做任何操作,我们可以认为没有这个组件
1 2 3 4 5 6
| const LimitedBox({ Key key, this.maxWidth = double.infinity, this.maxHeight = double.infinity, Widget child, })
|
DecoratedBox
DecoratedBox
可以在其子widget
绘制前(或后)绘制一个装饰Decoration
(如背景、边框、渐变等), 构造函数如下
1 2 3 4 5 6
| const DecoratedBox({ Key key, @required this.decoration, this.position = DecorationPosition.background, Widget child })
|
decoration
: 代表将要绘制的装饰,它类型为Decoration
是一个抽象类,它定义了一个接口 createBoxPainter()
,子类的主要职责是需要通过实现它来创建一个装饰器, 所以后面我们将使用BoxDecoration
来实现该属性
position
:此属性决定在哪里绘制Decoration
,它接收DecorationPosition
的枚举类型,该枚举类两个值:
background
:在子widget
之后绘制,即背景装饰(是默认值)
foreground
:在子widget
之上绘制,即前景
BoxDecoration
BoxDecoration
是一个Decoration
的子类, 通常我们都是使用它来实现上面的类似decoration
的相关属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const BoxDecoration({ this.color, this.image, this.border, this.borderRadius, this.boxShadow, this.gradient, this.backgroundBlendMode, this.shape = BoxShape.rectangle, })
|
image
设置背景图片DecorationImage
1 2 3 4 5 6 7 8 9 10
| const DecorationImage({ @required this.image, this.colorFilter, this.fit, this.alignment = Alignment.center, this.centerSlice, this.repeat = ImageRepeat.noRepeat, this.matchTextDirection = false, })
|
image
- 图片的设置方式, 是
ImageProvider
类型的
ImageProvider
是一个抽象类, 需要使用其子类实现
NetworkImage
FileImage
MemoryImage
colorFilter
- 在绘制图像之前应用于图像的滤色器, 这个属性值是
ColorFilter
类
ColorFilter
的构造方法中有两个属性, 分别设置颜色图像和图片图像, 后面也将使用这两个名词解释各个枚举值
1 2
| const ColorFilter.mode(Color color, BlendMode blendMode)
|
BlendMode
有以下枚举值, 带有src
表示图片图像不显示, dst
表示颜色图像不显示
blendMode |
枚举值意义 |
clear |
颜色图像和图片图像都不显示 |
src |
显示颜色图像不显示图片图像 |
dst |
显示图片图像不显示颜色图像 |
srcOver |
颜色图像在图片图像的上面 |
dstOver |
颜色图像在图片图像的下面 |
srcIn |
显示图片图像, 但只显示和颜色图像重合的部分(两者的交集) |
dstIn |
显示颜色图像, 但只显示和图片图像重合的部分(两者的交集) |
srcOut |
显示图片图像, 但只显示和颜色图像不重合的部分(两者的差集) |
dstOut |
显示颜色图像, 但只显示和图片图像不重合的部分(两者的差集), 一般都是空了 |
srcATop |
将图片图像合成到颜色图像上面, 只合成交集的部分 |
dstATop |
将颜色图像合成到图片图像上面, 只合成交集的部分 |
xor |
图片图像和颜色图像合成的结果 |
plus |
图片和颜色的合成, 但是会受透明度的影响 |
modulate |
将图片图像和颜色图像的颜色分量相乘。这只能产生相同或较暗的颜色(乘以白色,1.0,结果不变;乘以黑色,0.0,结果为黑色 |
screen |
将图片图像和颜色图像的颜色分量的倒数相乘, 并反转结果 |
overlay |
在调整图片图像和颜色图像的组件以使其有利于目标之后,将其相乘 |
darken |
通过从每个颜色通道中选择最低值来合成图片图像和颜色图像 |
lighten |
通过从每个颜色通道中选择最高值来合成图片图像和颜色图像 |
除此之外还有好几个枚举值, 但是我确实不知道该怎么解释了, 上面的解释好像也不是很清晰, 模模糊糊, 还是建议大家自测看效果图吧, 或者看看官方文档, 也有效果图, 其实很多枚举值还是用不到的, 如果有比较好的解释的话, 欢迎大家多多提出建议………大写的尴尬
border
设置边框的样式,BoxBorder
是一个抽象类, 有以下两个类共三种实现方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Border extends BoxBorder { const Border({ this.top = BorderSide.none, this.right = BorderSide.none, this.bottom = BorderSide.none, this.left = BorderSide.none, }) factory Border.all({ Color color = const Color(0xFF000000), double width = 1.0, BorderStyle style = BorderStyle.solid, }) }
class BorderSide { const BorderSide({ this.color = const Color(0xFF000000), this.width = 1.0, this.style = BorderStyle.solid, }) }
class BorderDirectional extends BoxBorder { const BorderDirectional({ this.top = BorderSide.none, this.start = BorderSide.none, this.end = BorderSide.none, this.bottom = BorderSide.none, }) }
|
boxShadow
设置盒子的阴影, 这个阴影和盒子的形状保持一致, 接受的值是一个存储BoxShadow
的列表, 下面下看一下BoxShadow
的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const BoxShadow({ Color color = const Color(0xFF000000), Offset offset = Offset.zero, double blurRadius = 0.0, this.spreadRadius = 0.0 })
[ BoxShadow(color: Colors.grey, offset: Offset(-5, -5), blurRadius: 10, spreadRadius: 0), BoxShadow(color: Colors.red, offset: Offset(5, 5), blurRadius: 10, spreadRadius: 0), ],
|
gradient
设置背景颜色为渐变色, Gradient
又是一个抽象类, 如下一共有三个子类
LinearGradient
RadialGradient
SweepGradient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const LinearGradient({ this.begin = Alignment.centerLeft, this.end = Alignment.centerRight, @required List<Color> colors, List<double> stops, this.tileMode = TileMode.clamp, })
const RadialGradient({ this.center = Alignment.center, this.radius = 0.5, @required List<Color> colors, List<double> stops, this.tileMode = TileMode.clamp, this.focal, this.focalRadius = 0.0 })
const SweepGradient({ this.center = Alignment.center, this.startAngle = 0.0, this.endAngle = math.pi * 2, @required List<Color> colors, List<double> stops, this.tileMode = TileMode.clamp, })
|
三种渐变色效果如下所示
LinearGradient
线性渐变色下, 渐变模式TileMode
各枚举值对应的效果如下
shape
设置背景的形状, 针对背景色, 背景图片和渐变色, BoxShape
类型是个枚举值
1 2 3 4 5 6 7
| enum BoxShape { rectangle,
circle, }
|
Transform
可以在其子Widget
绘制时对其进行一个矩阵变换, 可以对child
做平移、旋转、缩放等操作
Matrix4
是一个4D矩阵,通过它可以实现各种矩阵操作
先来看下Transform
的一些构造函数吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| class Transform extends SingleChildRenderObjectWidget { const Transform({ Key key, @required this.transform, this.origin, this.alignment, this.transformHitTests = true, Widget child, }) Transform.rotate({ Key key, @required double angle, this.origin, this.alignment = Alignment.center, this.transformHitTests = true, Widget child, }) Transform.translate({ Key key, @required Offset offset, this.transformHitTests = true, Widget child, }) Transform.scale({ Key key, @required double scale, this.origin, this.alignment = Alignment.center, this.transformHitTests = true, Widget child, }) }
|
下面是每一种变换形式的具体示例
旋转
Transform.rotate
可以对子widget
进行旋转变换, 如下代码
1 2 3 4 5 6 7 8 9 10 11 12
| Container( color: Colors.black, child: Transform.rotate( angle: -math.pi / 4, child: Container( padding: const EdgeInsets.all(8.0), color: const Color(0xFFE8581C), child: const Text('https://titanjun.top'), ), ) )
|
平移
Transform.translate
接收一个offset
参数,可以在绘制时沿x、y轴对子widget
平移指定的距离
1 2 3 4 5 6 7 8 9 10 11 12
| Container( color: Colors.black, child: Transform.translate( offset: const Offset(5.0, 15.0), child: Container( padding: const EdgeInsets.all(8.0), color: const Color(0xFF7F7F7F), child: const Text('Quarter'), ), ) )
|
缩放
Transform.scale
可以对子Widget
进行缩小或放大
1 2 3 4 5 6 7 8 9 10 11 12 13
| Container( color: Colors.black, child: Transform.scale( origin: Offset(5, 5), scale: 0.5, child: Container( padding: const EdgeInsets.all(8.0), color: const Color(0xFFE8581C), child: const Text('Bad Ideas'), ), ) )
|
注意点
Transform
的变换是应用在绘制阶段,而并不是应用在布局(layout
)阶段
- 所以无论对子widget应用何种变化,其占用空间的大小和在屏幕上的位置都是固定不变的,因为这些是在布局阶段就确定的
1 2 3 4 5 6 7 8 9 10 11 12
| Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ DecoratedBox( decoration:BoxDecoration(color: Colors.red), child: Transform.scale(scale: 1.5, child: Text("Hello world") ) ), Text("你好", style: TextStyle(color: Colors.green, fontSize: 18.0),) ], )
|
- 由于第一个
Text
应用变换(放大)后,其在绘制时会放大,但其占用的空间依然为红色部分,所以第二个text
会紧挨着红色部分,最终就会出现文字有重合部分。
- 由于矩阵变化只会作用在绘制阶段,所以在某些场景下,在UI需要变化时,可以直接通过矩阵变化来达到视觉上的UI改变,而不需要去重新触发build流程,这样会节省
layout
的开销,所以性能会比较好
- 如之前介绍的
Flow widget
,它内部就是用矩阵变换来更新UI,除此之外,Flutter
的动画widget
中也大量使用了Transform
以提高性能
RotatedBox
RotatedBox
和Transform.rotate
功能相似,它们都可以对子widget
进行旋转变换,但是有一点不同:RotatedBox
的变换是在layout
阶段,会影响在子widget
的位置和大小
- 我们将上面介绍
Transform.rotate
时的示例改一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: RotatedBox( quarterTurns: 1, child: Text("Hello world"), ), ), Text("你好", style: TextStyle(color: Colors.green, fontSize: 18.0),) ], ),
|
由于RotatedBox
是作用于layout
阶段,所以widget
会旋转90度(而不只是绘制的内容),decoration
会作用到widget
所占用的实际空间上,所以就是上图的效果。读者可以和前面Transform.rotate
示例对比理解
Matrix4
一个4D变换矩阵, Transform
使用Matrix4
使其子Widget
进行矩阵变换, 下面是其相关构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| factory Matrix4(double arg0, double arg1, double arg2, double arg3, double arg4, double arg5, double arg6, double arg7, double arg8, double arg9, double arg10, double arg11, double arg12, double arg13, double arg14, double arg15)
factory Matrix4.columns(Vector4 arg0, Vector4 arg1, Vector4 arg2, Vector4 arg3)
factory Matrix4.compose(Vector3 translation, Quaternion rotation, Vector3 scale)
factory Matrix4.copy(Matrix4 other)
factory Matrix4.diagonal3(Vector3 scale)
factory Matrix4.diagonal3Values(double x, double y, double z)
Matrix4.fromBuffer(ByteBuffer buffer, int offset)
Matrix4.fromFloat64List(Float64List _m4storage)
factory Matrix4.fromList(List<double> values)
factory Matrix4.identity()
factory Matrix4.inverted(Matrix4 other)
factory Matrix4.outer(Vector4 u, Vector4 v)
factory Matrix4.rotationX(double radians)
factory Matrix4.rotationY(double radians)
factory Matrix4.rotationZ(double radians)
factory Matrix4.skew(double alpha, double beta)
factory Matrix4.skewX(double alpha)
factory Matrix4.skewY(double beta)
factory Matrix4.translation(Vector3 translation)
factory Matrix4.translationValues(double x, double y, double z)
factory Matrix4.zero()
|
Container
Container
是一个容器类widget
,它本身不对应具体的RenderObject
,它是DecoratedBox
、ConstrainedBox
、Transform
、Padding
、Align
等widget
的一个组合widget
- 所以我们只需通过一个
Container
可以实现同时需要装饰、变换、限制的场景
- 下面是
Container
的相关定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Container({ Key key, this.alignment, this.padding, Color color, Decoration decoration, this.foregroundDecoration, double width, double height, BoxConstraints constraints, this.margin, this.transform, this.child, })
|
- 容器的大小可以通过
width
、height
属性来指定,也可以通过constraints
来指定,如果同时存在时,width
、height
优先。实际上Container
内部会根据width
、height
来生成一个constraints
color
和decoration
是互斥的,实际上,当指定color
时,Container
内会自动创建一个decoration
使用实例
通过使用来实现如下效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| Container( margin: EdgeInsets.only(top: 50.0, left: 120.0), constraints: BoxConstraints.tightFor(width: 200.0, height: 150.0), decoration: BoxDecoration( gradient: RadialGradient( colors: [Colors.red, Colors.orange], center: Alignment.topLeft, radius: .98 ), boxShadow: [ BoxShadow( color: Colors.black54, offset: Offset(2.0, 2.0), blurRadius: 4.0 ) ] ), transform: Matrix4.rotationZ(.2), alignment: Alignment.center, child: Text( "5.20", style: TextStyle(color: Colors.white, fontSize: 40.0), ), );
|
总结
ConstrainedBox
:适用于需要设置最大/小宽高,组件大小以来子组件大小,但不能超过设置的界限。
UnconstrainedBox
:用到情况不多,当作ConstrainedBox
的子组件可以“突破”ConstrainedBox
的限制,超出界限的部分会被截取。
SizedBox
:适用于固定宽高的情况,常用于当作2个组件之间间隙组件。
AspectRatio
:适用于固定宽高比的情况。
FractionallySizedBox
:适用于占父组件百分比的情况。
LimitedBox
:适用于没有父组件约束的情况。
Container
:适用于不仅有尺寸的约束,还有装饰(颜色、边框、等)、内外边距等需求的情况。
参考文献