歡迎關注微信公衆號:FSA全棧行動 👋css
單子佈局, 顧名思義就是隻能包含一個子控件的 widgetgit
Center
能夠將子控件居中顯示, 默認會盡量拉伸填滿父控件:微信
class CenterDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Icon(Icons.pets),
);
}
}
複製代碼
經過查看 Center
的源碼能夠得知, Center
本質上就是沒法指定 alignment
的 Align
:markdown
class Center extends Align {
/// Creates a widget that centers its child.
const Center({ Key? key, double? widthFactor, double? heightFactor, Widget? child })
: super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}
class Align extends SingleChildRenderObjectWidget {
/// Creates an alignment widget.
///
/// The alignment defaults to [Alignment.center].
const Align({
Key? key,
this.alignment = Alignment.center,
this.widthFactor,
this.heightFactor,
Widget? child,
}) : assert(alignment != null),
assert(widthFactor == null || widthFactor >= 0.0),
assert(heightFactor == null || heightFactor >= 0.0),
super(key: key, child: child);
複製代碼
所以, 徹底可使用 Align
來代替 Center
:less
class AlignDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Center其實就是指定了alignment爲center的Align
// return Center(
// child: Icon(Icons.pets),
// );
return Align(
widthFactor: 5, // 寬度是child寬度的5倍
heightFactor: 5, // 高度是child高度的5倍
alignment: Alignment.center,
child: Icon(Icons.pets),
);
}
}
複製代碼
通常狀況下, 會直接在外層嵌套
Container
直接指定確切的寬度值, 而不會使用widthFactor
ide
通常的 Widget 是沒有 padding 屬性的(Container
除外), 若是但願對子 widget 有 padding 效果的話, 能夠爲子 widget 套一層 Padding
, Padding
只有 2 個屬性, 分別是 child 和 padding, padding
屬性對應 EdgeInsetsGeometry
類型的對象, 通常會結合 EdgeInsets
的幾個常量命名構造函數來使用:函數
class PaddingDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(8.0),
child: item("hello lqr"),
),
Divider(height: 1, color: Colors.black),
Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
child: item("hello gitlqr"),
),
Divider(height: 1, color: Colors.black),
Padding(
padding: EdgeInsets.fromLTRB(8, 8, 8, 8),
child: item("hello charylin"),
),
Divider(height: 1, color: Colors.black),
Padding(
padding: EdgeInsets.only(left: 8),
child: item("hello charylin"),
),
Divider(height: 1, color: Colors.black),
],
);
}
Widget item(String content) {
return Text(
content,
style: TextStyle(
fontSize: 30,
backgroundColor: Colors.red,
color: Colors.white,
),
);
}
}
複製代碼
Container
是 Flutter 中最特殊的 widget, 能夠指定尺寸、內外間距、2D 轉換等:oop
Border.all(width: 5)
來指定BorderRadius.circular(8)
來指定class ContainerDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
// width、height不指定,默認是包裹內容
width: 200,
height: 200,
child: Icon(Icons.pets, size: 50, color: Colors.white),
// 子元素所在位置
alignment: Alignment.topLeft,
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(10),
// 旋轉5度,縮小一半
transform: Matrix4.rotationZ(degree2Radia(5)).scaled(0.5),
color: Colors.red,
),
Container(
width: 200,
height: 200,
child: Icon(Icons.accessibility, size: 50, color: Colors.white),
// color與decoration衝突,二者只有選擇其中一個
// color: Colors.red,
decoration: BoxDecoration(
color: Colors.red, // 背景色
border: Border.all(width: 5, color: Colors.blueAccent), // 邊框
borderRadius: BorderRadius.circular(8), // 圓角
boxShadow: [
BoxShadow(
color: Colors.blueGrey, // 陰影顏色
offset: Offset(10, 10), // 陰影偏移量
spreadRadius: 5, // 延伸,至關於offset爲 15,15
),
]),
)
],
);
}
double degree2Radia(double degree) {
return degree * pi / 180;
}
}
複製代碼
多子佈局, 顧名思義就是隻能包含多個子控件的 widget佈局
Flutter 中的 Flex
與 css 中的 flex 佈局很相似, 能夠很靈活的控制內部子 widget 的擺放, 不過通常狀況下不會直接使用, 而是使用其子類 Row
/ Column
:flex
默認狀況下, Row 在水平方向上會盡量佔據比較大的空間, 這是由於其 mainAxisSize
屬性默認爲 MainAxisSize.max
致使:
Column 與 Row 除了方向不一樣, 其它基本一致, 故掌握 Row 的狀況後, Column 天然也會掌握
class ButtonRowDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// Row特色:
/// - 水平方向儘量佔據比較大的空間
/// * 若是水平方向但願包裹內容,能夠設置 mainAxisSize = min
/// - 垂直方向包裹內容
return Column(
children: [
RaisedButton(
child: Row(
children: [Icon(Icons.bug_report), Text("bug報告(MainAxisSize.max)")],
),
onPressed: () {},
),
RaisedButton(
child: Row(
mainAxisSize: MainAxisSize.min, // 包裹內容。包裹是max佔滿父widget
children: [Icon(Icons.bug_report), Text("bug報告(MainAxisSize.min)")],
),
onPressed: () {},
),
],
);
}
}
複製代碼
Row 比較重點的是主軸及交叉軸的對齊:
class RowDemo1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
itemRow("start", MainAxisAlignment.start, "center", CrossAxisAlignment.center),
itemRow("end", MainAxisAlignment.end, "center", CrossAxisAlignment.center),
itemRow("center", MainAxisAlignment.center, "center", CrossAxisAlignment.center),
itemRow("spaceBetween", MainAxisAlignment.spaceBetween, "center", CrossAxisAlignment.center),
itemRow("spaceAround", MainAxisAlignment.spaceAround, "center", CrossAxisAlignment.center),
itemRow("spaceEvenly", MainAxisAlignment.spaceEvenly, "center", CrossAxisAlignment.center),
],
);
}
Widget itemRow(
String mainAxisAlignmentStr,
MainAxisAlignment mainAxisAlignment,
String crossAxisAlignmentStr,
CrossAxisAlignment crossAxisAlignment) {
return Container(
height: 120,
margin: const EdgeInsets.only(bottom: 8.0),
color: Colors.pink[100],
child: Stack(
fit: StackFit.expand,
children: [
Row(
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
// textDirection: TextDirection.ltr, // rtl: 從右到左排版; ltr: 從左到右排版(默認)
children: [
Container(width: 80, height: 60, color: Colors.red),
Container(width: 120, height: 100, color: Colors.green),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
],
),
Positioned(
left: 0,
bottom: 0,
child: Text(
"GitLqr >>> main:$mainAxisAlignmentStr , cross:$crossAxisAlignmentStr",
style: TextStyle(fontSize: 20, backgroundColor: Colors.black54, color: Colors.white),
),
)
],
),
);
}
}
複製代碼
class RowDemo2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 基線對齊
return Column(
children: [
itemRow("spaceEvenly", MainAxisAlignment.spaceEvenly, "start", CrossAxisAlignment.start),
itemRow("spaceEvenly", MainAxisAlignment.spaceEvenly, "center", CrossAxisAlignment.center),
itemRow("spaceEvenly", MainAxisAlignment.spaceEvenly, "end", CrossAxisAlignment.end),
itemRow("spaceEvenly", MainAxisAlignment.spaceEvenly, "stretch", CrossAxisAlignment.stretch),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.baseline,
// alphabetic 與 ideographic 這2種基線幾乎沒差
textBaseline: TextBaseline.ideographic,
children: [
Container(width: 80,height: 60, color: Colors.red, child: Text("Hellxo", style: TextStyle(fontSize: 20))),
Container(width: 120, height: 100, color: Colors.green, child: Text("Woxrld", style: TextStyle(fontSize: 30)),),
Container(width: 90, height: 80, color: Colors.blue, child: Text("abxc", style: TextStyle(fontSize: 12))),
Container(width: 50, height: 120, color: Colors.orange, child: Text("cxba", style: TextStyle(fontSize: 40))),
],
),
],
);
}
Widget itemRow(
String mainAxisAlignmentStr,
MainAxisAlignment mainAxisAlignment,
String crossAxisAlignmentStr,
CrossAxisAlignment crossAxisAlignment) {
return Container(
height: 140,
margin: const EdgeInsets.only(bottom: 8.0),
color: Colors.pink[100],
child: Stack(
fit: StackFit.expand,
children: [
Row(
mainAxisAlignment: mainAxisAlignment,
crossAxisAlignment: crossAxisAlignment,
// textDirection: TextDirection.ltr, // rtl: 從右到左排版; ltr: 從左到右排版(默認)
children: [
Container(width: 80, height: 60, color: Colors.red),
Container(width: 120, height: 100, color: Colors.green),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
],
),
Positioned(
left: 0,
bottom: 0,
child: Text(
"GitLqr >>> main:$mainAxisAlignmentStr , cross:$crossAxisAlignmentStr",
style: TextStyle(fontSize: 20, backgroundColor: Colors.black54, color: Colors.white),
),
)
],
),
);
}
}
複製代碼
最後一組是
CrossAxisAlignment.baseline
的效果, 能夠看到無論文字多大, 字母x
的底部都是在一條線上的, 這就是基線對齊.
值得注意的是, 使用CrossAxisAlignment.baseline
必須同時指定基線textBaseline(默認值爲null)
, 其值TextBaseline.ideographic
與TextBaseline.alphabetic
幾乎沒差
Column 與 Row 都是繼承自 Flex, 二者除了在方向上有區別外, 其餘特性幾乎徹底同樣, 這裏只補充一點它們排版方向上的不一樣之處:
class ColumnDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
verticalDirection: VerticalDirection.down,
// up: 從下到上排版; down: 從上到下排版(默認)
children: [
Container(width: 80, height: 60, color: Colors.red),
Container(width: 120, height: 100, color: Colors.green),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
Text(
"GitLqr >>> VerticalDirection.down",
style: TextStyle(fontSize: 20, backgroundColor: Colors.black54, color: Colors.white),
),
],
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
verticalDirection: VerticalDirection.up,
// up: 從下到上排版; down: 從上到下排版(默認)
children: [
Container(width: 80, height: 60, color: Colors.red),
Container(width: 120, height: 100, color: Colors.green),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
Text(
"GitLqr >>> VerticalDirection.up",
style: TextStyle(fontSize: 20, backgroundColor: Colors.black54, color: Colors.white),
),
],
),
),
]);
}
}
複製代碼
Flexible 中的屬性:
Expanded = Flexible(fit: FlexFit.tight)
class ExpandedDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
item1(),
tip("Flexible fit: FlexFit.tight flex: 1"),
item2(),
tip("Expanded flex: 1 , flex: 1 (width: 120)"),
item3(),
tip("Expanded flex: 1 , flex: 2 (width: 10000)"),
],
);
}
Widget item1() {
return Row(
children: [
Flexible(
fit: FlexFit.tight,
flex: 1,
child: Container(width: 80, height: 60, color: Colors.red),
),
Flexible(
fit: FlexFit.tight,
flex: 1,
child: Container(width: 120, height: 100, color: Colors.green),
),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
],
);
}
Widget item2() {
return Row(
children: [
Expanded(
flex: 1,
child: Container(width: 80, height: 60, color: Colors.red),
),
Expanded(
flex: 1,
child: Container(width: 120, height: 100, color: Colors.green),
),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
],
);
}
Widget item3() {
return Row(
children: [
Expanded(
flex: 1,
child: Container(width: 80, height: 60, color: Colors.red),
),
Expanded(
flex: 2,
child: Container(width: 10000, height: 100, color: Colors.green),
),
Container(width: 90, height: 80, color: Colors.blue),
Container(width: 50, height: 120, color: Colors.orange),
],
);
}
Widget tip(String content) {
return Text(
"GitLqr >>> $content",
style: TextStyle(
fontSize: 20,
color: Colors.white,
backgroundColor: Colors.black54,
),
);
}
}
複製代碼
Stack
可讓子 Widget 堆疊在一塊兒, 默認的大小是包裹內容的, 其屬性有:
全部的子 Widget
Positioned
(Widget): 對 單個子 Widget
進行定位Overflow.visible
class StackDemo1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
alignment: AlignmentDirectional.bottomStart,
// fit: StackFit.expand,
overflow: Overflow.visible,
children: [
Image.asset("assets/images/FSA_QR.png"),
Positioned(
left: 20,
bottom: -50,
child: Container(width: 150, height: 150, color: Colors.red),
),
Positioned(
right: 0,
child: Text(
"lqr",
style: TextStyle(fontSize: 30, color: Colors.white, backgroundColor: Colors.black),
),
)
],
);
}
}
複製代碼
class StackDemo2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
children: [
Image.asset("assets/images/FSA_QR.png"),
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.0),
color: Color.fromARGB(160, 0, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"歡迎關注公衆號: FSA全棧行爲",
style: TextStyle(fontSize: 20, color: Colors.white),
),
IconButton(
icon: Icon(Icons.favorite),
color: Colors.red,
onPressed: () => print("點擊了收藏"),
)
],
),
),
)
],
);
}
}
複製代碼
若是文章對您有所幫助, 請不吝點擊關注一下個人微信公衆號:FSA全棧行動, 這將是對我最大的激勵. 公衆號不只有Android技術, 還有iOS, Python等文章, 可能有你想要了解的技能知識點哦~