Flutter入門指北(Part4)之容器部件

NO IMAGE

該文已授權公眾號 「碼個蛋」,轉載請指明出處

上節填完了 Scaffold 留下的坑,這節繼續填坑,之前留下關於 Layout 的坑,又是一堆部件襲來

Container

為了讓我們的界面更容易被擴展,通常會在最外層包裹一層 Container,其構造函數也不是很難理解

Container({
Key key,
this.alignment, // child 的對齊方式,包括左對齊,居中,右對齊,左上對齊..等等
this.padding, // child 和 Container 的邊距
Color color, // Container 的背景色
Decoration decoration, // 樣式,可以設置背景圖,圓角等屬性
this.foregroundDecoration, // child 的樣式
double width, // 寬度
double height, // 高度
BoxConstraints constraints, // 默認使用 BoxConstraints.tightFor,可以手動傳入
this.margin, // Container 同上層容器的邊距
this.transform, // 是個 Matrix4 矩陣,(嗯..這個參數基本很少用,沒怎麼了解 /捂臉)
this.child, // 需要展示的內容
})
// ...
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;

讓我們寫個圓角矩形的外層,內層值顯示白色文字

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
alignment: Alignment.center,
// 寬,高度同上層容器相同
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
margin: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(20.0),
// Container 的樣式
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.red,
//            shape: BoxShape.circle, // 該屬性不可同 borderRadius 一起使用
backgroundBlendMode: BlendMode.colorDodge, // 背景圖片和顏色混合模式
image: DecorationImage(image: AssetImage('images/ali.jpg'), fit: BoxFit.cover)),
child: Text('Container Text', style: TextStyle(color: Colors.white, fontSize: 30.0)),
//        color: Theme.of(context).primaryColor, // 該屬性不可和 decoration 一起使用
)),
);
}
}

效果圖如下:

Flutter入門指北(Part4)之容器部件

該部分代碼查看 column_main.dart 文件

看到這,應該很多小夥伴注意到 marginpadding 屬性用來和別的部件保持間距,那…那我就是不用 Container 呢(專門來挑事的…),當然沒問題,有個專門用來設置間距的部件 Padding,看名字就可以看出來作用了,修改下 child 部分代碼,這邊先提前用下接下來會講的部件

child: Column(
children: <Widget>[
Text('Container Text', style: TextStyle(color: Colors.white, fontSize: 30.0)),
Padding(
// 需要傳入一個間隔值,`Flutter` 提供了很多 EdgeInsets 來設置間隔,
// 參數也很明確,可以一一嘗試
padding: const EdgeInsets.symmetric(vertical: 12.0),
// 傳入需要間隔的部件
child: Text('Container Text', style: TextStyle(color: Colors.white, fontSize: 30.0)))
],
),

效果就不展示了,接下來就要開始我的填坑之旅了….

####Flex,Row,Column

Android 的小夥伴應該比較常用 LinearLayout,在 Flutter 中用兩個部件,Row Column來代替 Android 中的 LinearLayout,其中 Row 是橫向佈局,Column 是垂直佈局,因為 RowColumn 都是繼承於 Flex 部件,Flex 比他們多了 direction 屬性用來指定方向,所以主要拿 Column 來講解,FlexRow 用法相同

Column({
Key key,
// 對齊方式,對於 `Column` start 為頂部,對於 `Row` 需要分語言,和語言同向
// 3 種比較特殊的對齊方式,前端的小夥伴會了解,
// spaceAround 兩個部件之間的間隔是部件和上層容器間隔的兩倍
// spaceBetween 兩側部件同上層容器間隔為 0,部件之間的間隔相等
// spaceEvenly 部件之間的間隔同兩側部件與上層容器間隔
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, 
MainAxisSize mainAxisSize = MainAxisSize.max, // 主軸的大小
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, // 副軸對齊方式
TextDirection textDirection, // 文字方向,決定 start
VerticalDirection verticalDirection = VerticalDirection.down, // 垂直方向
TextBaseline textBaseline,
List<Widget> children = const <Widget>[], // 內部子部件
})

RowColumn 都有主軸和副軸,如何區分呢,佈局平行方向為主軸,垂直方向為副軸,我們把 Containerchild 修改成 Column,然後把 Text 放到 Column 中,多放幾個,然後自己設置 mainAxisAlignment 屬性,查看佈局的變化

// ... 省略相同代碼
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text('Container Text 1', style: TextStyle(color: Colors.white, fontSize: 30.0)),
Text('Container Text 2', style: TextStyle(color: Colors.white, fontSize: 30.0)),
Text('Container Text 3', style: TextStyle(color: Colors.white, fontSize: 30.0)),
Text('Container Text 4', style: TextStyle(color: Colors.white, fontSize: 30.0)),
Text('Container Text 5', style: TextStyle(color: Colors.white, fontSize: 30.0)),
],
)

最後的效果圖如下:

Flutter入門指北(Part4)之容器部件

這邊 Column 內部的子部件因為高度相同,如果不同還需要等分空間的話,就不可以通過設置 mainAxisAlignment 屬性來實現了,這裡介紹一個等分的部件 Expanded

const Expanded({
Key key,
int flex = 1, // 所佔比例
@required Widget child, // 子部件
})

直接給 Text 外層加一個 Expanded 即可實現效果,當然可以按照需求來設置 flex 來修改比例值。

當然,在使用過程中也會遇到那麼些坑,我們修改下代碼,把 child 的代碼修改成如下

child: Row(
children: <Widget>[
Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0)),
Text('ABC' * 5, style: TextStyle(color: Colors.white, fontSize: 16.0))
],
)

然後運行下,你的屏幕就提示你 RIGHT OVERFLOWED BY XXX PIXELS 「**, *****」我猜你內心肯定這樣的,冷靜冷靜

既然遇到問題,當然要解決,不然和產品去撕逼嗎..?這邊,我們把 Row 換成另一個佈局 Wrap 然後再運行,Prefect,WrapRow 的參數基本類似

Wrap

Wrap({
Key key,
this.direction = Axis.horizontal,
this.alignment = WrapAlignment.start,
this.spacing = 0.0, // 兩個子部件之間的間隔,默認 0.0,如果值過大,可能導致原來同行的兩個部件分行
this.runAlignment = WrapAlignment.start, 
this.runSpacing = 0.0, // 排布方向上 兩個子部件的間隔
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
})

當然,很多時候只有以上的佈局是不行的,比如我們需要實現一個圓形頭像,然後一段文字在其上面 ,例如下面的效果

Flutter入門指北(Part4)之容器部件

接下來介紹一個堆疊的部件 Stack,源碼比較簡單,就不貼了,直接上效果代碼

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
// 內部子部件的對齊方式
alignment: Alignment.center,
children: <Widget>[
// 圓形頭像,指定半徑,指定背景圖為頭像即可
CircleAvatar(backgroundImage: AssetImage('images/ali.jpg'), radius: 100.0),
Text(
'Kuky', style: TextStyle(color: Colors.white, fontSize: 34.0)),
],
)),
);
}
}

如果我們需要第三個部件,底部距離圓形頭像10px,那麼只靠 alignment 是不可能實現了

所以,另外一個灰常流弊的部件就出來了 Positioned,其源碼也比較簡單,我還是不貼了吧~,還是直接上代碼,直接修改

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: <Widget>[
CircleAvatar(backgroundImage: AssetImage('images/ali.jpg'), radius: 100.0),
Text(
'Kuky',
style: TextStyle(color: Colors.white, fontSize: 34.0),
),
Positioned(child: Text('另外一段文字', style: TextStyle(color: Colors.white, fontSize: 20.0)), bottom: 10.0), // left, right, top, bottom 分別表示和 stack 的間距
],
)),
);
}
}

最後的效果圖如下:

Flutter入門指北(Part4)之容器部件

此處代碼查看 stack_main.dart 文件

當然,Flutter 還提供別的佈局類的部件,例如 Flow 這個鬼,但是呢……這個鬼會用到「轉換矩陣」來對子部件進行佈局(一般聽到需要用矩陣…emmmm,算…算你牛逼),而且不能自適應子部件的大小等等一些問題,所以還是放棄吧,實際上,前面介紹的佈局完全夠用了,是在不夠用了,再看 Flow

很好,今天填了佈局的這個大坑,而且講的部件貌似還挺多的,雖然還是比較簡單,剩下的就給小夥伴們慢慢消化今天的內容。下節,除了有常用的部件外,我會盡量加上實戰內容

最後代碼的地址還是要的:

  1. 文章中涉及的代碼:demos

  2. 基於郭神 cool weather 接口的一個項目,實現 BLoC 模式,實現狀態管理:flutter_weather

  3. 一個課程(當時買了想看下代碼規範的,代碼更新會比較慢,雖然是跟著課上的一些寫代碼,但是還是做了自己的修改,很多地方看著不舒服,然後就改成自己的實現方式了):flutter_shop

如果對你有幫助的話,記得給個 Star,先謝過,你的認可就是支持我繼續寫下去的動力~

相關文章

Flutter入門指北(Part8)之Sliver組件、NestedScrollView

Flutter入門指北(Part7)之滑動部件

Flutter入門指北(Part6)之路由

Flutter入門指北(Part5)之輸入處理及實戰