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
能够给其子元素设置内边距git
class Padding extends SingleChildRenderObjectWidget {
const Padding({
Key key,
// 内边距
@required this.padding,
Widget child,
})
final EdgeInsetsGeometry padding;
}
复制代码
EdgeInsetsGeometry
是一个抽象类,通常状况都使用EdgeInsets
,它是EdgeInsetsGeometry
的一个子类, 下面是的定义的一些方法github
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 })
// 静态变量, 上下左右, 都是0
static const EdgeInsets zero = EdgeInsets.only();
}
复制代码
示例数组
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(20),
child: Icon(Icons.phone, color: Colors.cyan,),
);
}
}
复制代码
ConstrainedBox
和SizedBox
都是经过RenderConstrainedBox
来渲染的SizedBox
只是ConstrainedBox
一个定制ConstrainedBox
主要用于对子widget
添加额外的约束class ConstrainedBox extends SingleChildRenderObjectWidget {
ConstrainedBox({
Key key,
@required this.constraints,
Widget child
})
/// 给子widget添加约束
final BoxConstraints constraints;
复制代码
BoxConstraints
设置Widget
的约束, 内部设置了四个属性: 最大/小宽度和最大小高度, 下面是其相关构造函数和实例函数bash
class BoxConstraints extends Constraints {
/// 构造函数
const BoxConstraints({
// 最小宽度
this.minWidth = 0.0,
// 最大宽度
this.maxWidth = double.infinity,
// 最小高度
this.minHeight = 0.0,
// 最大高度
this.maxHeight = double.infinity
});
/// 根据指定的Size设置约束
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;
// 根据Size参数, 设置其最大值, 最小值为0
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;
}
复制代码
使用实例微信
class ConstrainedBoxView extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return ConstrainedBox(
constraints: BoxConstraints(
minWidth: double.infinity, // 宽度设置最大
minHeight: 50, // 高度最小值设置50
),
child: Container(
height: 10, // 设置高度为10
child: DecoratedBox(decoration: BoxDecoration(color: Colors.orange)),
),
);
}
}
复制代码
Container
的高度设置为10像素,可是最终倒是50像素,这正是ConstrainedBox
的最小高度限制生效了Container
的高度设置为80像素,那么最终红色区域的高度也会是80像素,由于在此示例中,ConstrainedBox
只限制了最小高度,并未限制最大高度SizedBox
用于给子widget
指定固定的宽高markdown
// SizedBox的几种构造函数
class SizedBox extends SingleChildRenderObjectWidget {
/// 设置固定的高度
const SizedBox({ Key key, this.width, this.height, Widget child })
: super(key: key, child: child);
/// 建立一个最大宽高的box
const SizedBox.expand({ Key key, Widget child })
: width = double.infinity,
height = double.infinity,
super(key: key, child: child);
/// 建立一个最小宽高(都是0)的box
const SizedBox.shrink({ Key key, Widget child })
: width = 0.0,
height = 0.0,
super(key: key, child: child);
/// 建立一个指定size 的box
SizedBox.fromSize({ Key key, Widget child, Size size })
: width = size?.width,
height = size?.height,
super(key: key, child: child);
}
复制代码
这里咱们建立一个指定宽高的Widget
less
class SizedBoxView extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return SizedBox(
width: 80,
height: 80,
child: Container(
child: DecoratedBox(decoration: BoxDecoration(color: Colors.orange)),
),
);
}
}
// 实际上SizedBox和只是ConstrainedBox一个定制, 上面的代码等价于
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
对象ide
// 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
来讲, 无效, 最终宽高都是0const UnconstrainedBox({
Key key,
Widget child,
// TextDirection, 表示水平方向子widget的布局顺序
this.textDirection,
// 子Widget在主轴上的对其方式
this.alignment = Alignment.center,
// 设置约束的轴, 水平or垂直, Axis.horizontal
this.constrainedAxis,
})
复制代码
UnconstrainedBox
不会对子Widget
产生任何限制,它容许其子Widget
按照其自己大小绘制widget
,但在"去除"多重限制的时候也许会有帮助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
的代码的状况下没法完全去除其限制条件DecoratedBox
能够在其子widget
绘制前(或后)绘制一个装饰Decoration
(如背景、边框、渐变等), 构造函数以下布局
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
是一个Decoration
的子类, 一般咱们都是使用它来实现上面的相似decoration
的相关属性
const BoxDecoration({
// 背景颜色
this.color,
// 背景图片, DecorationImage
this.image,
// 边框
this.border,
// 圆角
this.borderRadius,
// 阴影
this.boxShadow,
// 渐变色
this.gradient,
// 背景颜色和背景图片的混合渲染模式`BlendMode`, 下面会介绍该枚举值
this.backgroundBlendMode,
// 形状
this.shape = BoxShape.rectangle,
})
复制代码
设置背景图片DecorationImage
const DecorationImage({
// ImageProvider类型
@required this.image,
this.colorFilter,
this.fit,
this.alignment = Alignment.center,
this.centerSlice,
this.repeat = ImageRepeat.noRepeat,
this.matchTextDirection = false,
})
复制代码
ImageProvider
类型的ImageProvider
是一个抽象类, 须要使用其子类实现
NetworkImage
FileImage
MemoryImage
ColorFilter
类ColorFilter
的构造方法中有两个属性, 分别设置颜色图像和图片图像, 后面也将使用这两个名词解释各个枚举值// 两个属性, 分别设置颜色图像和图片图像
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 |
经过从每一个颜色通道中选择最高值来合成图片图像和颜色图像 |
除此以外还有好几个枚举值, 可是我确实不知道该怎么解释了, 上面的解释好像也不是很清晰, 模模糊糊, 仍是建议你们自测看效果图吧, 或者看看官方文档, 也有效果图, 其实不少枚举值仍是用不到的, 若是有比较好的解释的话, 欢迎你们多多提出建议.........大写的尴尬
设置边框的样式,BoxBorder
是一个抽象类, 有如下两个类共三种实现方式
// Border的两种实现方式
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,
})
}
// 下面是BorderSide的构造函数, 以前的文字中都介绍过, 这里就不在说起了
class BorderSide {
const BorderSide({
this.color = const Color(0xFF000000),
this.width = 1.0,
this.style = BorderStyle.solid,
})
}
// BorderDirectional的构造函数
class BorderDirectional extends BoxBorder {
const BorderDirectional({
this.top = BorderSide.none,
this.start = BorderSide.none,
this.end = BorderSide.none,
this.bottom = BorderSide.none,
})
}
复制代码
设置盒子的阴影, 这个阴影和盒子的形状保持一致, 接受的值是一个存储BoxShadow
的列表, 下面下看一下BoxShadow
的构造函数
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
又是一个抽象类, 以下一共有三个子类
LinearGradient
RadialGradient
SweepGradient
// 线性渐变
const LinearGradient({
// 起始点
this.begin = Alignment.centerLeft,
// 终点
this.end = Alignment.centerRight,
// 色值数组
@required List<Color> colors,
// 值列表,装有0.0到1.0的数值
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
各枚举值对应的效果以下
设置背景的形状, 针对背景色, 背景图片和渐变色, BoxShape
类型是个枚举值
enum BoxShape {
// 保持不变
rectangle,
// 剪切成圆形, 和borderRadius属性冲突
circle,
}
复制代码
Transform
能够在其子Widget
绘制时对其进行一个矩阵变换, 能够对child
作平移、旋转、缩放等操做Matrix4
是一个4D矩阵,经过它能够实现各类矩阵操做先来看下Transform
的一些构造函数吧
class Transform extends SingleChildRenderObjectWidget {
// 建立一个矩阵变换Widget
const Transform({
Key key,
// 矩阵执行的变换操做, 接受一个Matrix4对象
@required this.transform,
// 旋转点,相对于左上角顶点的偏移。默认旋转点事左上角顶点,
// 接受一个Offset对象
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,
// 设置缩放比例, 0-1的数值
@required double scale,
this.origin,
this.alignment = Alignment.center,
this.transformHitTests = true,
Widget child,
})
}
复制代码
下面是每一种变换形式的具体示例
Transform.rotate
能够对子widget
进行旋转变换, 以下代码
Container(
color: Colors.black,
child: Transform.rotate(
// 这里☞旋转的角度, math.pi是指180度
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
平移指定的距离
Container(
color: Colors.black,
child: Transform.translate(
// 默认原点为左上角,右移5像素,向下平移15像素
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
进行缩小或放大
Container(
color: Colors.black,
child: Transform.scale(
origin: Offset(5, 5),
// 缩小为原来的0.5倍
scale: 0.5,
child: Container(
padding: const EdgeInsets.all(8.0),
color: const Color(0xFFE8581C),
child: const Text('Bad Ideas'),
),
)
)
复制代码
Transform
的变换是应用在绘制阶段,而并非应用在布局(layout
)阶段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
会紧挨着红色部分,最终就会出现文字有重合部分。layout
的开销,因此性能会比较好Flow widget
,它内部就是用矩阵变换来更新UI,除此以外,Flutter
的动画widget
中也大量使用了Transform
以提升性能RotatedBox
和Transform.rotate
功能类似,它们均可以对子widget
进行旋转变换,可是有一点不一样:RotatedBox
的变换是在layout
阶段,会影响在子widget
的位置和大小Transform.rotate
时的示例改一下Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
//将Transform.rotate换成RotatedBox
child: RotatedBox(
// int类型
quarterTurns: 1, //旋转90度(1/4圈)
child: Text("Hello world"),
),
),
Text("你好", style: TextStyle(color: Colors.green, fontSize: 18.0),)
],
),
复制代码
因为
RotatedBox
是做用于layout
阶段,因此widget
会旋转90度(而不仅是绘制的内容),decoration
会做用到widget
所占用的实际空间上,因此就是上图的效果。读者能够和前面Transform.rotate
示例对比理解
一个4D变换矩阵, Transform
使用Matrix4
使其子Widget
进行矩阵变换, 下面是其相关构造函数
// 4 x 4矩阵
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)
// 复制一个4*4的张量(矩阵)
factory Matrix4.copy(Matrix4 other)
// 缩放矩阵, Vector3(double x, double y, double z)
factory Matrix4.diagonal3(Vector3 scale)
// 缩放矩阵, 只是参数不一样而已
factory Matrix4.diagonal3Values(double x, double y, double z)
Matrix4.fromBuffer(ByteBuffer buffer, int offset)
// 使用给定的Float64List构造Matrix4
Matrix4.fromFloat64List(Float64List _m4storage)
// 将一个16位的一维数组转换成4*4的矩阵
factory Matrix4.fromList(List<double> values)
// 恢复初始状态,也就是4*4的单位矩阵
factory Matrix4.identity()
// 取相反的矩阵,就是反着来
factory Matrix4.inverted(Matrix4 other)
// 两个4维向量的乘积合并
factory Matrix4.outer(Vector4 u, Vector4 v)
// 围绕X轴旋转
factory Matrix4.rotationX(double radians)
// 围绕Y轴旋转
factory Matrix4.rotationY(double radians)
// 围绕Z轴旋转
factory Matrix4.rotationZ(double radians)
// 扭曲变换
factory Matrix4.skew(double alpha, double beta)
// 沿着x轴扭曲
factory Matrix4.skewX(double alpha)
// 沿着y轴扭曲
factory Matrix4.skewY(double beta)
// 移动矩阵
factory Matrix4.translation(Vector3 translation)
// 移动矩阵, 参数不一样而已
factory Matrix4.translationValues(double x, double y, double z)
// 全是0的4*4的张量
factory Matrix4.zero()
复制代码
Container
是一个容器类widget
,它自己不对应具体的RenderObject
,它是DecoratedBox
、ConstrainedBox
、Transform
、Padding
、Align
等widget
的一个组合widgetContainer
能够实现同时须要装饰、变换、限制的场景Container
的相关定义Container({
Key key,
// 对其方式
this.alignment,
// 内边距
this.padding,
// 背景颜色
Color color,
// 背景装饰
Decoration decoration,
// 前景装饰
this.foregroundDecoration,
double width,
double height,
//容器大小的限制条件
BoxConstraints constraints,
// 容器的外边距, EdgeInsets
this.margin,
// 设置变换矩阵
this.transform,
this.child,
})
复制代码
width
、height
属性来指定,也能够经过constraints
来指定,若是同时存在时,width
、height
优先。实际上Container
内部会根据width
、height
来生成一个constraints
color
和decoration
是互斥的,实际上,当指定color
时,Container
内会自动建立一个decoration
经过使用来实现以下效果
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),
),
);
复制代码
欢迎您扫一扫下面的微信公众号,订阅个人博客!