一、概述
因为很多的基础小部件( Widget
) 依赖于布局 Widget
,所以在本套基础讲解中,会穿插各种 Widget 的说明,并没有按照一定的分组顺序进行讲解。就是用到什么讲解什么,但是力争做到讲解的够清晰。所以现在开始说明一些基础的布局 Widget, 后续很多其他的 Widget 在使用中会依赖于或用到布局的 Widget ,所以在此先进行说明。
二 、Container Widget
容器 Widget ,其是一个可以设置宽高,边距,装饰等的 Widget ,有一个子 Widget 属性 child
。如果没有设置子 Widget ,容器会尽可能大的展示。它继承自 StatelessWidget
,是一个无状态的 Widget 。构造方法如下:
Container({
Key key,
//AlignmentGeometry类型可选命名参数,容器内子Widget如何对其,使用其子类Alignment
this.alignment,
//EdgeInsetsGeometry类型可选命名参数,设置容器内边距
this.padding,
//Color类型可选命名参数,容器填充色
Color color,
//Decoration类型可选命名参数,绘制在child子Widget后面的装饰,使用BoxDecoration
Decoration decoration,
//Decoration类型可选命名参数,绘制在child子Widget前面的装饰,使用BoxDecoration
this.foregroundDecoration,
//double类型可选命名参数,容器的宽度
double width,
//double类型可选命名参数,容器的高度
double height,
//BoxConstraints类型可选命名参数,对child设置的Widget的约束
BoxConstraints constraints,
//EdgeInsetsGeometry类型可选命名参数,设置容器外边距
this.margin,
//Matrix4类型可选命名参数,在绘制容器之前要应用的转换矩阵
this.transform,
//Widget类型可选命名参数,容器包含的子Widget
this.child,
})
其中 color
与 decoration
不能同时设置。
Decoration
是一个抽象类,这里需要使用 BoxDecoration
,是一个用于设置如何绘制盒子的不可变的的描述。盒子的主体是分层绘制的。 最底层是颜色,它填充了框。 在此之上的是渐变,渐变也填充了该框。 最后是图像,其精确对齐由 DecorationImage
类控制。边框涂在身体上,阴影自然在其下方绘制。其构造方法如下:
const BoxDecoration({
//Color类型可选命名参数,填充背景色
this.color,
//DecorationImage类型可选命名参数,在背景或渐变上绘制的图像
this.image,
//BoxBorder类型可选命名参数,边框设置
this.border,
//BorderRadiusGeometry类型可选命名参数,设置圆角
this.borderRadius,
//List<BoxShadow>类型可选命名参数,盒子后面的盒子投射的阴影列表
this.boxShadow,
//Gradient类型可选命名参数,填充框时使用的渐变
this.gradient,
//BlendMode类型可选命名参数,应用于框的颜色或渐变背景的混合模式
this.backgroundBlendMode,
//BoxShape类型可选命名参数,将背景颜色、渐变和图像填充到并作为boxShadow投射的形状
this.shape = BoxShape.rectangle,
})
Container
基本使用方式如下:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container( //Container
color: Colors.yellow,
),
);
}
}
此时的 Container
会充满整个屏幕,尽可能大。
其它属性设置
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container( //Container
width: 300,
height: 200,
child: Text("这是一个文本",),
alignment: Alignment.topCenter, //子Widget的相对于父级Container的对齐方式
padding: EdgeInsets.all(20), //设置内边距,文本的每个边外都有20的边距
margin: EdgeInsets.all(50), //设置外边距,Container每个边外有50的外边距
color: Colors.yellow, //填充色
transform: Matrix4.rotationZ(0.2), //围绕Z轴旋转指定弧度
foregroundDecoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage("http://www.mwpush.com/uploads/avatar.png"),
fit: BoxFit.fill
),
border: Border.all(
width: 5,
color: Colors.blue
),
),
),
);
}
}
效果如下:
foregroundDecoration
用于设置前景装饰效果,所以如果有重叠,当加载图片时会覆盖 Container
本身的内容。如果不希望覆盖,使用背景装饰效果 decoration
即可,使用方式相同,只是使用 decoration
时不能使用 color
,使用如下:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
width: 300,
height: 200,
child: Text("这是一个文本",),
alignment: Alignment.topCenter, //子Widget的相对于父级Container的对齐方式
padding: EdgeInsets.all(20), //设置内边距,文本的每个边外都有20的边距
margin: EdgeInsets.all(50), //设置外边距,Container每个边外有50的外边距
transform: Matrix4.rotationZ(0.2), //围绕Z轴旋转指定弧度
constraints: BoxConstraints( //设置最大最小约束
minHeight: 300,
minWidth: 300,
maxHeight: 400,
maxWidth: 400,
),
decoration: BoxDecoration( //decoration
image: DecorationImage(
image: NetworkImage("http://www.mwpush.com/uploads/avatar.png"),
fit: BoxFit.fill
),
border: Border.all(
width: 5,
color: Colors.blue
),
borderRadius: BorderRadius.all(Radius.circular(10)), //设置圆角
),
),
);
}
}
效果如下:
BoxDecoration
还可以设置渐变色等属性。
三、Center Widget
Center
是将子 Widget 放置于其中心的 Widget 。如果其宽高没有设置,则其会尽可能大的展示。可通过设置宽度与高度因子来控制大小。比如设置宽度因子后,Center
的宽度值为子 Widget 的宽度乘以宽度因子的值。宽度与高度因子的值必须为正数。其构造方法如下:
const Center({
Key key,
//double类型可选命名参数,宽度因子
double widthFactor,
//double类型可选命名参数,高度因子
double heightFactor,
//Widget类型可选命名参数,要显示的子Widget
Widget child
})
使用如下:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
color: Colors.yellowAccent,
child: Center( //Center
widthFactor: 2,
heightFactor: 2,
child: Container(
color: Colors.red,
child: Text("中心文本",),
),
),
)
);
}
}
这里为了看清显示的效果,使用了 Container
Widget 来包裹 Center
和 Center
的 child
Widget ,因为 Container
可以设置填充色,便于区分。效果如下:
四、Padding Widget
Padding
是用来设置内填充(内边距)的 Widget ,在 Container
中也可以设置 Container
的 padding
,两者并没有区别。Container
是将多个单独的 Widget 进行组合使用,需要时只需设置相应的属性即可。作用是通过设置内边距的大小使其进行膨胀,在其子 Widget 周围创造出一定的空间。其构造函数方法如下:
const Padding({
Key key,
//EdgeInsetsGeometry类型必传参数,内边距
@required this.padding,
//Widget类型可选命名参数,要显示的Widget
Widget child,
})
使用方法如下,与上述 Center
实现效果差不多,代码中的 Container
也是为了使效果看的更清晰:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
color: Colors.yellow,
child: Padding( //Padding
padding: EdgeInsets.all(50),
child: Container(
color: Colors.red,
child: Text("Padding"),
),
),
),
);
}
}
五、Align Widget
Align
可以设置其子 Widget 相对自己的对齐方式,并可以根据子 Widget 的大小调整其自己的大小。其可以设置宽度和高度因子,如果不设置,则其会尽可能的大的展示,如果设置,比如设置宽度因子,则 Align
的宽度将是其子 Widget
的宽度乘以宽度因子。构造方法如下:
const Align({
Key key,
//AlignmentGeometry类型可选命名参数,设置如何对齐,AlignmentGeometry为抽象类,
//通常使用Alignment或FractionalOffset
this.alignment = Alignment.center,
//double类型可选命名参数,宽度因子
this.widthFactor,
//double类型可选命名参数,高度因子
this.heightFactor,
//Widget类型可选命名参数,要显示的Widget
Widget child,
})
Align
与 Center
类似,不同的是 Align
可以设置其子 Widget 相对于自己的对齐方式,而 Center
则是居中对齐。
Alignment
在此用来设置对其方式,其定义的是一个矩形中的点。其提供了几种对齐方式可以直接使用,如下:
/// The top left corner.
static const Alignment topLeft = Alignment(-1.0, -1.0);
/// The center point along the top edge.
static const Alignment topCenter = Alignment(0.0, -1.0);
/// The top right corner.
static const Alignment topRight = Alignment(1.0, -1.0);
/// The center point along the left edge.
static const Alignment centerLeft = Alignment(-1.0, 0.0);
/// The center point, both horizontally and vertically.
static const Alignment center = Alignment(0.0, 0.0);
/// The center point along the right edge.
static const Alignment centerRight = Alignment(1.0, 0.0);
/// The bottom left corner.
static const Alignment bottomLeft = Alignment(-1.0, 1.0);
/// The center point along the bottom edge.
static const Alignment bottomCenter = Alignment(0.0, 1.0);
/// The bottom right corner.
static const Alignment bottomRight = Alignment(1.0, 1.0);
比较简单,不做中文说明。从其定义可以看出,其定义的方式都是使用 Alignment()
构造方法,如下:
//x,y均为double类型的必传参数,用于定义一个点的x和y轴值
const Alignment(this.x, this.y)
所以可以直接使用构造函数定义需要通过哪个点对齐,如下:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
color: Colors.yellow,
child: Align(
child: Container(child: Text("Align"), color: Colors.red,),
alignment: Alignment(0.2, 0.5),
widthFactor: 10,
heightFactor: 10,
),
),
);
}
}
Alignment(0.0, 0.0)
表示矩形的中心点。从 -1.0 到 +1.0 的距离是从矩形的一侧到矩形的另一侧的距离。 因此,水平(或垂直)2.0 个单位等于矩形的宽度(或高度)。Alignment(-1.0, -1.0)
表示矩形的左上方,Alignment(1.0, 1.0)
表示矩形的右下角。Alignment(0.0, 3.0)
表示一个点,该点相对于矩形水平居中,垂直于矩形底部低于矩形的高度。Alignment(0.0, -0.5)
表示相对于矩形水平居中且顶部边缘与中心之间垂直居中的点。
以上面的代码为例,其对齐的点的计算方法为( 0.2*Text的宽度/2 + Text的宽度/2, 0.5*Text的高度/2+Text的高度/2 )
。
Alignment
使用的坐标系,其原点位于容器的中心点。
FractionalOffset
用来定义一个偏移量,其也提供了几个常用的值,如下:
/// The top left corner.
static const FractionalOffset topLeft = FractionalOffset(0.0, 0.0);
/// The center point along the top edge.
static const FractionalOffset topCenter = FractionalOffset(0.5, 0.0);
/// The top right corner.
static const FractionalOffset topRight = FractionalOffset(1.0, 0.0);
/// The center point along the left edge.
static const FractionalOffset centerLeft = FractionalOffset(0.0, 0.5);
/// The center point, both horizontally and vertically.
static const FractionalOffset center = FractionalOffset(0.5, 0.5);
/// The center point along the right edge.
static const FractionalOffset centerRight = FractionalOffset(1.0, 0.5);
/// The bottom left corner.
static const FractionalOffset bottomLeft = FractionalOffset(0.0, 1.0);
/// The center point along the bottom edge.
static const FractionalOffset bottomCenter = FractionalOffset(0.5, 1.0);
/// The bottom right corner.
static const FractionalOffset bottomRight = FractionalOffset(1.0, 1.0);
可以看到,其与 Alignment
类似,不同之处在于 Alignment()
定义的是一个点(计算方法在上面),而 FractionalOffset()
定义的是两个点,这两个点是单独确定的。对于当前 Widget ,这里为 Text
,其点的计算方式为:(0.2*Text的宽度, 0.5*Text的高度)
。对于父级 Widget ,这里为 Container
则是 (0.2*Container的宽度, 0.5*Container的高度)
。最后使两个点重合,即将 Text
的点移动到 Container
定位的点处。使用
FractionalOffset
时,其原点位于容器的左上角。
此外,除了提供 FractionalOffset(double x, double y)
构造方法,另外还提供了如下两个构造方法:
factory FractionalOffset.fromOffsetAndSize(Offset offset, Size size) {
assert(size != null);
assert(offset != null);
return FractionalOffset(
offset.dx / size.width,
offset.dy / size.height,
);
}
factory FractionalOffset.fromOffsetAndRect(Offset offset, Rect rect) {
return FractionalOffset.fromOffsetAndSize(
offset - rect.topLeft,
rect.size,
);
}
六、Row Widget
Row
是一个可以同时显示多个子 Widget 的 Widget ,这些子 Widget 以水平方式进行排列。Row
Widget 不是一个可以滚动的 Widget ,如果水平显示的子 Widget 的总范围超出了可用空间会抛出异常。如果有需要进行水平或垂直方向的滚动操作,考虑使用 ListView
,后面会讲到。Row
的构造方法如下:
Row({
Key key,
//MainAxisAlignment类型可选命名参数,如何沿着主轴放置子Widget
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
//MainAxisSize类型可选命名参数,主轴上应占用多少空间,该值传入最大化还是最小化可用空间
MainAxisSize mainAxisSize = MainAxisSize.max,
//CrossAxisAlignment类型可选命名参数,如何沿着次轴放置子Widget
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
//TextDirection类型可选命名参数,设置子Widget横向的排列方向,默认为环境方向
TextDirection textDirection,
//VerticalDirection类型可选命名参数,设置子Widget纵向的排列顺序以及如何解释垂直方向的开始和结束
VerticalDirection verticalDirection = VerticalDirection.down,
//TextBaseline类型可选命名参数,如果根据基线对齐项目,使用哪个基线
TextBaseline textBaseline,
//List<Widget>类型可选命名参数,要显示的子Widget列表
List<Widget> children = const <Widget>[],
})
MainAxisAlignment
用于设置子 Widget 在主轴(这里是水平方向)上的排列方式,是一个枚举类型,有如下值:
enum MainAxisAlignment {
//子Widget放置在尽可能靠近主轴起点的位置。如果在水平方向使用,则必须使
//用TextDirection来确定起点是左侧还是右侧。如果在垂直方向上使用此值,
//则VerticalDirection必须可用以确定起点是顶部还是底部
start,
//子Widget放置在尽可能靠近主轴末端的位置。如果在水平方向上使用此值,则必须使
//用TextDirection来确定末端是左侧还是右侧。如果在垂直方向上使用此值,则必须
//使用VerticalDirection来确定末端是顶部还是底部
end,
//子Widget放置在尽可能靠近主轴的中心
center,
//子Widget均匀的放置在可用空间内
spaceBetween,
//将自由空间平均放置在两个子Widget之间,以及第一个和最后一个Widget前后的一半空间
spaceAround,
//在子Widget之间以及第一个Widget和最后一个Widget之前和之后均匀地放置自由空间
spaceEvenly,
}
CrossAxisAlignment
用于设置子 Widget 在次轴(这里是垂直方向)上的排列方式,是一个枚举类型,有如下值:
enum CrossAxisAlignment {
//子Widget放置在尽可能靠近次轴起点的位置。如果在水平方向上使用此值,则必须使
//用TextDirection来确定起点是左侧还是右侧。如果在垂直方向上使用此值,
//则VerticalDirection必须可用以确定起点是顶部还是底部
start,
//子Widget放置在尽可能靠近次轴末端的位置。如果在水平方向上使用此值,则必须使
//用TextDirection来确定末端是左侧还是右侧。如果在垂直方向上使用此值,则必须
//使用VerticalDirection来确定末端是顶部还是底部
end,
//子Widget放置在尽可能靠近次轴的中心
center,
//子Widget填满次轴
stretch,
//在次轴上放置子Widget,使其与基线对齐,使用此值需要设置textBaseline
baseline,
}
VerticalDirection
用于设置垂直的排列方向,是一个枚举类型值,有如下值:
enum VerticalDirection {
//盒子应从底部开始,并垂直向上堆叠。“开始”在底部,“结束”在顶部。
up,
//盒子应从顶部开始,并垂直向下堆叠。“开始”在顶部,“结束”在底部。
down,
}
Row
的使用方式如下:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
height: 100,
color: Colors.yellow,
child: Row( //Row
textBaseline: TextBaseline.alphabetic,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
// Image.network("http://www.mwpush.com/uploads/avatar.png"),
Container(child: Text("Row1"), color: Colors.red,),
Text("Row2"),
Text("Row3"),
],
),
),
);
}
}
效果如下:
七、Column Widget
Column
是一个可以同时显示多个子 Widget 的 Widget ,这些子 Widget 以垂直方式进行排列。Column
Widget 不是一个可以滚动的 Widget ,如果垂直显示的子 Widget 的总范围超出了可用空间会抛出异常。如果有需要进行水平或垂直方向的滚动操作,考虑使用 ListView
。Column
的构造方法如下:
Column({
Key key,
//MainAxisAlignment类型可选命名参数,如何沿着主轴放置子Widget
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
//MainAxisSize类型可选命名参数,主轴上应占用多少空间,该值传入最大化还是最小化可用空间
MainAxisSize mainAxisSize = MainAxisSize.max,
//CrossAxisAlignment类型可选命名参数,如何沿着次轴放置子Widget
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
//TextDirection类型可选命名参数,设置子Widget横向的排列方向,默认为环境方向
TextDirection textDirection,
//VerticalDirection类型可选命名参数,设置子Widget纵向的排列顺序以及如何解释垂直方向的开始和结束
VerticalDirection verticalDirection = VerticalDirection.down,
//TextBaseline类型可选命名参数,如果根据基线对齐项目,使用哪个基线
TextBaseline textBaseline,
//List<Widget>类型可选命名参数,要显示的子Widget列表
List<Widget> children = const <Widget>[],
})
构造方法与 Row
相同,只是排列的基准不同。使用如下:
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
width: 500,
height: 200,
color: Colors.yellow,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// Image.network("http://www.mwpush.com/uploads/avatar.png"),
Container(child: Text("Row1"), color: Colors.red,),
Text("Row2"),
Text("Row3"),
],
),
),
);
}
}
效果如下: