一、概述
因為很多的基礎(chǔ)小部件( Widget ) 依賴于布局 Widget ,所以在本套基礎(chǔ)講解中,會穿插各種 Widget 的說明,并沒有按照一定的分組順序進(jìn)行講解。就是用到什么講解什么,但是力爭做到講解的夠清晰。所以現(xiàn)在開始說明一些基礎(chǔ)的布局 Widget, 后續(xù)很多其他的 Widget 在使用中會依賴于或用到布局的 Widget ,所以在此先進(jìn)行說明。
二 、Container Widget
容器 Widget ,其是一個可以設(shè)置寬高,邊距,裝飾等的 Widget ,有一個子 Widget 屬性 child 。如果沒有設(shè)置子 Widget ,容器會盡可能大的展示。它繼承自 StatelessWidget ,是一個無狀態(tài)的 Widget 。構(gòu)造方法如下:
Container({
Key key,
//AlignmentGeometry類型可選命名參數(shù),容器內(nèi)子Widget如何對其,使用其子類Alignment
this.alignment,
//EdgeInsetsGeometry類型可選命名參數(shù),設(shè)置容器內(nèi)邊距
this.padding,
//Color類型可選命名參數(shù),容器填充色
Color color,
//Decoration類型可選命名參數(shù),繪制在child子Widget后面的裝飾,使用BoxDecoration
Decoration decoration,
//Decoration類型可選命名參數(shù),繪制在child子Widget前面的裝飾,使用BoxDecoration
this.foregroundDecoration,
//double類型可選命名參數(shù),容器的寬度
double width,
//double類型可選命名參數(shù),容器的高度
double height,
//BoxConstraints類型可選命名參數(shù),對child設(shè)置的Widget的約束
BoxConstraints constraints,
//EdgeInsetsGeometry類型可選命名參數(shù),設(shè)置容器外邊距
this.margin,
//Matrix4類型可選命名參數(shù),在繪制容器之前要應(yīng)用的轉(zhuǎn)換矩陣
this.transform,
//Widget類型可選命名參數(shù),容器包含的子Widget
this.child,
})
其中 color 與 decoration 不能同時設(shè)置。
Decoration 是一個抽象類,這里需要使用 BoxDecoration ,是一個用于設(shè)置如何繪制盒子的不可變的的描述。盒子的主體是分層繪制的。 最底層是顏色,它填充了框。 在此之上的是漸變,漸變也填充了該框。 最后是圖像,其精確對齊由 DecorationImage 類控制。邊框涂在身體上,陰影自然在其下方繪制。其構(gòu)造方法如下:
const BoxDecoration({
//Color類型可選命名參數(shù),填充背景色
this.color,
//DecorationImage類型可選命名參數(shù),在背景或漸變上繪制的圖像
this.image,
//BoxBorder類型可選命名參數(shù),邊框設(shè)置
this.border,
//BorderRadiusGeometry類型可選命名參數(shù),設(shè)置圓角
this.borderRadius,
//List<BoxShadow>類型可選命名參數(shù),盒子后面的盒子投射的陰影列表
this.boxShadow,
//Gradient類型可選命名參數(shù),填充框時使用的漸變
this.gradient,
//BlendMode類型可選命名參數(shù),應(yīng)用于框的顏色或漸變背景的混合模式
this.backgroundBlendMode,
//BoxShape類型可選命名參數(shù),將背景顏色、漸變和圖像填充到并作為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 會充滿整個屏幕,盡可能大。
其它屬性設(shè)置
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), //設(shè)置內(nèi)邊距,文本的每個邊外都有20的邊距
margin: EdgeInsets.all(50), //設(shè)置外邊距,Container每個邊外有50的外邊距
color: Colors.yellow, //填充色
transform: Matrix4.rotationZ(0.2), //圍繞Z軸旋轉(zhuǎn)指定弧度
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 用于設(shè)置前景裝飾效果,所以如果有重疊,當(dāng)加載圖片時會覆蓋 Container 本身的內(nèi)容。如果不希望覆蓋,使用背景裝飾效果 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), //設(shè)置內(nèi)邊距,文本的每個邊外都有20的邊距
margin: EdgeInsets.all(50), //設(shè)置外邊距,Container每個邊外有50的外邊距
transform: Matrix4.rotationZ(0.2), //圍繞Z軸旋轉(zhuǎn)指定弧度
constraints: BoxConstraints( //設(shè)置最大最小約束
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)), //設(shè)置圓角
),
),
);
}
}
效果如下:

BoxDecoration 還可以設(shè)置漸變色等屬性。
三、Center Widget
Center 是將子 Widget 放置于其中心的 Widget 。如果其寬高沒有設(shè)置,則其會盡可能大的展示??赏ㄟ^設(shè)置寬度與高度因子來控制大小。比如設(shè)置寬度因子后,Center 的寬度值為子 Widget 的寬度乘以寬度因子的值。寬度與高度因子的值必須為正數(shù)。其構(gòu)造方法如下:
const Center({
Key key,
//double類型可選命名參數(shù),寬度因子
double widthFactor,
//double類型可選命名參數(shù),高度因子
double heightFactor,
//Widget類型可選命名參數(shù),要顯示的子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 可以設(shè)置填充色,便于區(qū)分。效果如下:

四、Padding Widget
Padding 是用來設(shè)置內(nèi)填充(內(nèi)邊距)的 Widget ,在 Container 中也可以設(shè)置 Container 的 padding ,兩者并沒有區(qū)別。Container 是將多個單獨的 Widget 進(jìn)行組合使用,需要時只需設(shè)置相應(yīng)的屬性即可。作用是通過設(shè)置內(nèi)邊距的大小使其進(jìn)行膨脹,在其子 Widget 周圍創(chuàng)造出一定的空間。其構(gòu)造函數(shù)方法如下:
const Padding({
Key key,
//EdgeInsetsGeometry類型必傳參數(shù),內(nèi)邊距
@required this.padding,
//Widget類型可選命名參數(shù),要顯示的Widget
Widget child,
})
使用方法如下,與上述 Center 實現(xiàn)效果差不多,代碼中的 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 可以設(shè)置其子 Widget 相對自己的對齊方式,并可以根據(jù)子 Widget 的大小調(diào)整其自己的大小。其可以設(shè)置寬度和高度因子,如果不設(shè)置,則其會盡可能的大的展示,如果設(shè)置,比如設(shè)置寬度因子,則 Align 的寬度將是其子 Widget 的寬度乘以寬度因子。構(gòu)造方法如下:
const Align({
Key key,
//AlignmentGeometry類型可選命名參數(shù),設(shè)置如何對齊,AlignmentGeometry為抽象類,
//通常使用Alignment或FractionalOffset
this.alignment = Alignment.center,
//double類型可選命名參數(shù),寬度因子
this.widthFactor,
//double類型可選命名參數(shù),高度因子
this.heightFactor,
//Widget類型可選命名參數(shù),要顯示的Widget
Widget child,
})
Align 與 Center 類似,不同的是 Align 可以設(shè)置其子 Widget 相對于自己的對齊方式,而 Center 則是居中對齊。
Alignment 在此用來設(shè)置對其方式,其定義的是一個矩形中的點。其提供了幾種對齊方式可以直接使用,如下:
/// 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() 構(gòu)造方法,如下:
//x,y均為double類型的必傳參數(shù),用于定義一個點的x和y軸值
const Alignment(this.x, this.y)
所以可以直接使用構(gòu)造函數(shù)定義需要通過哪個點對齊,如下:
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 的距離是從矩形的一側(cè)到矩形的另一側(cè)的距離。 因此,水平(或垂直)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 使用的坐標(biāo)系,其原點位于容器的中心點。
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() 定義的是兩個點,這兩個點是單獨確定的。對于當(dāng)前 Widget ,這里為 Text ,其點的計算方式為:(0.2*Text的寬度, 0.5*Text的高度) 。對于父級 Widget ,這里為 Container 則是 (0.2*Container的寬度, 0.5*Container的高度) 。最后使兩個點重合,即將 Text 的點移動到 Container 定位的點處。使用
FractionalOffset 時,其原點位于容器的左上角。
此外,除了提供 FractionalOffset(double x, double y) 構(gòu)造方法,另外還提供了如下兩個構(gòu)造方法:
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 以水平方式進(jìn)行排列。Row Widget 不是一個可以滾動的 Widget ,如果水平顯示的子 Widget 的總范圍超出了可用空間會拋出異常。如果有需要進(jìn)行水平或垂直方向的滾動操作,考慮使用 ListView ,后面會講到。Row 的構(gòu)造方法如下:
Row({
Key key,
//MainAxisAlignment類型可選命名參數(shù),如何沿著主軸放置子Widget
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
//MainAxisSize類型可選命名參數(shù),主軸上應(yīng)占用多少空間,該值傳入最大化還是最小化可用空間
MainAxisSize mainAxisSize = MainAxisSize.max,
//CrossAxisAlignment類型可選命名參數(shù),如何沿著次軸放置子Widget
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
//TextDirection類型可選命名參數(shù),設(shè)置子Widget橫向的排列方向,默認(rèn)為環(huán)境方向
TextDirection textDirection,
//VerticalDirection類型可選命名參數(shù),設(shè)置子Widget縱向的排列順序以及如何解釋垂直方向的開始和結(jié)束
VerticalDirection verticalDirection = VerticalDirection.down,
//TextBaseline類型可選命名參數(shù),如果根據(jù)基線對齊項目,使用哪個基線
TextBaseline textBaseline,
//List<Widget>類型可選命名參數(shù),要顯示的子Widget列表
List<Widget> children = const <Widget>[],
})
MainAxisAlignment 用于設(shè)置子 Widget 在主軸(這里是水平方向)上的排列方式,是一個枚舉類型,有如下值:
enum MainAxisAlignment {
//子Widget放置在盡可能靠近主軸起點的位置。如果在水平方向使用,則必須使
//用TextDirection來確定起點是左側(cè)還是右側(cè)。如果在垂直方向上使用此值,
//則VerticalDirection必須可用以確定起點是頂部還是底部
start,
//子Widget放置在盡可能靠近主軸末端的位置。如果在水平方向上使用此值,則必須使
//用TextDirection來確定末端是左側(cè)還是右側(cè)。如果在垂直方向上使用此值,則必須
//使用VerticalDirection來確定末端是頂部還是底部
end,
//子Widget放置在盡可能靠近主軸的中心
center,
//子Widget均勻的放置在可用空間內(nèi)
spaceBetween,
//將自由空間平均放置在兩個子Widget之間,以及第一個和最后一個Widget前后的一半空間
spaceAround,
//在子Widget之間以及第一個Widget和最后一個Widget之前和之后均勻地放置自由空間
spaceEvenly,
}
CrossAxisAlignment 用于設(shè)置子 Widget 在次軸(這里是垂直方向)上的排列方式,是一個枚舉類型,有如下值:
enum CrossAxisAlignment {
//子Widget放置在盡可能靠近次軸起點的位置。如果在水平方向上使用此值,則必須使
//用TextDirection來確定起點是左側(cè)還是右側(cè)。如果在垂直方向上使用此值,
//則VerticalDirection必須可用以確定起點是頂部還是底部
start,
//子Widget放置在盡可能靠近次軸末端的位置。如果在水平方向上使用此值,則必須使
//用TextDirection來確定末端是左側(cè)還是右側(cè)。如果在垂直方向上使用此值,則必須
//使用VerticalDirection來確定末端是頂部還是底部
end,
//子Widget放置在盡可能靠近次軸的中心
center,
//子Widget填滿次軸
stretch,
//在次軸上放置子Widget,使其與基線對齊,使用此值需要設(shè)置textBaseline
baseline,
}
VerticalDirection 用于設(shè)置垂直的排列方向,是一個枚舉類型值,有如下值:
enum VerticalDirection {
//盒子應(yīng)從底部開始,并垂直向上堆疊?!伴_始”在底部,“結(jié)束”在頂部。
up,
//盒子應(yīng)從頂部開始,并垂直向下堆疊。“開始”在頂部,“結(jié)束”在底部。
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 以垂直方式進(jìn)行排列。Column Widget 不是一個可以滾動的 Widget ,如果垂直顯示的子 Widget 的總范圍超出了可用空間會拋出異常。如果有需要進(jìn)行水平或垂直方向的滾動操作,考慮使用 ListView 。Column 的構(gòu)造方法如下:
Column({
Key key,
//MainAxisAlignment類型可選命名參數(shù),如何沿著主軸放置子Widget
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
//MainAxisSize類型可選命名參數(shù),主軸上應(yīng)占用多少空間,該值傳入最大化還是最小化可用空間
MainAxisSize mainAxisSize = MainAxisSize.max,
//CrossAxisAlignment類型可選命名參數(shù),如何沿著次軸放置子Widget
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
//TextDirection類型可選命名參數(shù),設(shè)置子Widget橫向的排列方向,默認(rèn)為環(huán)境方向
TextDirection textDirection,
//VerticalDirection類型可選命名參數(shù),設(shè)置子Widget縱向的排列順序以及如何解釋垂直方向的開始和結(jié)束
VerticalDirection verticalDirection = VerticalDirection.down,
//TextBaseline類型可選命名參數(shù),如果根據(jù)基線對齊項目,使用哪個基線
TextBaseline textBaseline,
//List<Widget>類型可選命名參數(shù),要顯示的子Widget列表
List<Widget> children = const <Widget>[],
})
構(gòu)造方法與 Row 相同,只是排列的基準(zhǔn)不同。使用如下:
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"),
],
),
),
);
}
}
效果如下:
