一、概述
因為很多的基礎小部件( 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"),
],
),
),
);
}
}
效果如下: