往期回顧:
前面幾期的專欄對大家來說學習起來還算輕松加愉快,我們簡單認識了flutter這門新技術,并且嘗試著學習了像Text、Image、TextField幾個簡單的Widget,并且我們用這幾個Widget做了一些簡單的交互,好像我們并沒有注重Widget的顯示位置跟排版,我們只是讓他顯示出來而已,然后要想把這些Widget組合起來放在一個渲染到整個手機屏幕上,我們需要合理的選用一個容器來包裹這些Widget,或者說讓這些Widget舒適的排列在一個恰當?shù)娜萜骼铮拖裎覀冊谧鲈鷄ndroid時,如果UI上需要繪制一個水平或者豎直的view排列,我們會選用LinearLayout而不會去選FrameLayout,同理在Flutter上布局的排放我們也要適當?shù)倪x擇正確的容器。
在Flutter中也給我們提供了各種不同應用場景的layout,我們可以根據(jù)UI上排版的需要來選用不同的layout去完成我們對UI的繪制,在這些layout中,有些layout的借鑒了前端的盒子布局模型,有些完全跟原生android思想一致,所以對于我們來說學習起來并沒有那么抽象,下面我列出幾個常用的layout,并且舉例跟大家一塊看看Flutter到底是怎么完成Widget的布局的。
Flutter中容器:
- Row、Column
- Stack
- Center
- Container
- ListView
- Align
- Padding
- SizedBox
- AspectRadio
- DecoratedBox
- Opactity
下面我列出幾種常用的Layout使用場景
1.Row、Column
這兩種布局方式幾乎一樣,所以我把它倆放在一塊講解,先看下源碼對二者的描述
class Column extends Flex {
/// Creates a vertical array of children.
class Row extends Flex {
/// Creates a horizontal array of children.
從源碼注釋中我們了解到二者都是一個盛放children widget的array,不同的是一個是在水平方向(horizontal),另一個是豎直方向(vertical)
由于二者類似,我們只拿Row做講解,來一塊分析下Row構造方法,看下提供給我們可定制的屬性有哪些
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
})
1.1 屬性解析
1.1.1 MainAxisAlignment:
主軸方向上的對齊方式,會對child的位置起作用,默認是start。其中MainAxisAlignment枚舉值:
- center:將children放置在主軸的中心;
- end:將children放置在主軸的末尾;
- spaceAround:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,但是首尾child的空白區(qū)域為1/2;
- spaceBetween:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,首尾child都靠近首尾,沒有間隙;
- spaceEvenly:將主軸方向上的空白區(qū)域均分,使得children之間的空白區(qū)域相等,包括首尾child;
- start:將children放置在主軸的起點;
其中spaceAround、spaceBetween以及spaceEvenly的區(qū)別,就是對待首尾child的方式。其距離首尾的距離分別是空白區(qū)域的1/2、0、1。
1.1.2 MainAxisSize:
在主軸方向占有空間的值,默認是max。MainAxisSize的取值有兩種:
- max:根據(jù)傳入的布局約束條件,最大化主軸方向的可用空間;
- min:與max相反,是最小化主軸方向的可用空間;
1.1.3 CrossAxisAlignment:
children在交叉軸方向的對齊方式,與MainAxisAlignment略有不同。CrossAxisAlignment枚舉值有如下幾種:
- baseline:在交叉軸方向,使得children的baseline對齊;
- center:children在交叉軸上居中展示;
- end:children在交叉軸上末尾展示;
- start:children在交叉軸上起點處展示;
- stretch:讓children填滿交叉軸方向;
1.1.4TextDirection:
阿拉伯語系的兼容設置,一般無需處理。
1.1.5 VerticalDirection
定義了children擺放順序,默認是down。VerticalDirection枚舉值有兩種:
- down:從top到bottom進行布局;
- up:從bottom到top進行布局。
- top對應Row以及Column的話,就是左邊和頂部,bottom的話,則是右邊和底部。
1.1.6 TextBaseline:
使用的TextBaseline的方式,有兩種,前面已經介紹過。
干巴巴的說了這么多確實有點枯燥,上個圖直觀感受一下Row,我在Row里面水平排列了幾個RaisedButton
效果圖:
樣例代碼:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new LayoutDemo()));
}
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("水平方向布局"),
),
//布局方向 Row:水平布局 Column:垂直布局
body: new Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
new RaisedButton(
onPressed: () {
print('點擊紅色按鈕');
},
color: const Color(0xffff0000),
child: new Text('紅色按鈕'),
),
new RaisedButton(
onPressed: () {
print("點擊藍色按鈕");
},
color: const Color(0xff000099),
child: new Text('藍色按鈕'),
),
new RaisedButton(
onPressed: () {
print("點擊粉色按鈕");
},
color: const Color(0xffee9999),
child: new Text('粉色按鈕'),
)
],
),
);
;
}
}
cloumn與Row大同小異,讀者可自行測試,我就不詳細貼代碼演示了。下面我們一塊來看另外一種layout方式。
2.Stack
Stack即層疊布局,跟原生Android里面的FrameLayout如出一轍,能夠將子widget層疊排列。如果不指定顯示位置,默認布局在左上角,如果希望子空間顯示在具體的位置,我們可以通過Positioned控件包裹子widget,然后根據(jù)定位的子控件的top、right、bottom、left屬性來將它們放置在Stack的合適位置上。
Stack布局效果圖:
上述效果圖樣例代碼:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new StackLayoutDemo()));
}
class StackLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('層疊布局'),
),
body: new Center(
child: new Stack(
children: <Widget>[
new Image.network(
'https://avatar.csdn.net/6/0/6/1_xieluoxixi.jpg',
scale: 0.5,
),
new Positioned(
left: 35.0,
right: 35.0,
top: 45.0,
child: new Text(
'第二層內容區(qū)域',
style: new TextStyle(
fontSize: 20.0,
fontFamily: 'serif',
),
)),
new Positioned(
left: 55.0,
right: 55.0,
top: 55.0,
child: new Text(
'第三層 this is the third child',
style: new TextStyle(
fontSize: 20.0,
color: Colors.blue,
fontFamily: 'serif',
),
))
],
),
));
}
}
3.Center
Center布局使用比較簡單,場景也比較單一,一般用于協(xié)助其他子widget布局,包裹其child widget顯示在上層布局的中心位置。上述Stack布局中就利用Center讓其顯示在屏幕正中心,看下官方對Center的解釋:
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);
}
比較簡單直接,我就不貼效果圖了,讀者可自行利用center包裹子widget做測試,從而直觀體驗一下center布局。
//Center既中心定位控件,能夠將子控件放在其內部中心。
class CenterLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('中心布局'),
),
body: new Center(
child: new Text('我在屏幕中央'),
),
);
}
}
4.ListView
Listview使用場景跟原生Android一樣,都是用于展示長列表數(shù)據(jù),也是我們開發(fā)中使用的比較多的widget之一,本章節(jié)主要分析layout,就不詳細展開分析,帶大家簡單認識下ListView的用法,后續(xù)我們專門抽出一個章節(jié)講解ListView,跟listView類似的還有GridView。
ListView效果圖:

上述實現(xiàn)樣例代碼:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new ListViewLayoutDemo()));
}
class ListViewLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('滾動布局'),
),
body: new ListView(
children: <Widget>[
new Center(
child: new Text(
'\n大標題',
style: new TextStyle(fontFamily: 'serif', fontSize: 20.0),
),
),
new Center(
child: new Text(
'小標題',
style: new TextStyle(
fontFamily: 'serif',
fontSize: 12.0,
),
),
),
new Center(
child: new Text(
'''
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
內容sadf手動閥防守打法 發(fā)生富士達發(fā)生發(fā)生飛都是
''',
style: new TextStyle(fontSize: 14.0),
),
)
],
),
);
}
}
5.Align:
Align即對齊控件,能將子控件按照所指定的方式對齊,并根據(jù)子控件的大小調整自己的大小。Align對齊子控件的方式有如下幾種
bottomCenter (0.5, 1.0) 底部中心
bottomLeft (0.0, 1.0) 左下角
bottomRight (1.0, 1.0) 右下角
center (0.5, 0.5) 水平垂直居中
centerLeft (0.0, 0.5) 左邊緣中心
centerRight (1.0, 0.5) 右邊緣中心
topCenter (0.5, 0.0) 頂部中心
topLeft (0.0, 0.0) 左上角
topRight (1.0, 0.0) 右上角
來簡單使用一下:
代碼:
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new AlignLayoutDemo()));
}
class AlignLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Align布局'),
),
body: new Stack(
children: <Widget>[
new Align(
alignment: new FractionalOffset(0.0, 0.5),
child: new Text(
'我在左邊緣中心',
style: new TextStyle(fontSize: 35.0),
),
),
new Align(
alignment: new FractionalOffset(1.0, 1.0),
child: new Text(
'我在右下角',
style: new TextStyle(fontSize: 30.0),
),
)
],
),
);
}
}
6.SizedBox
SizedBox能夠強制控制子控件的寬高顯示,比如我指定一個寬高都為200的SiezedBox布局,其里面的child widget的寬高也就被限定死了最大寬高為SizedBox的寬高,即使他有更大的寬高。
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new SizedBoxLayoutDemo()));
}
class SizedBoxLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('SizedBox布局'),
),
body: new SizedBox(
width: 200.0,
height: 200.0,
child: new Container(
decoration: new BoxDecoration(color: Colors.red),
),
),
);
}
}
利用SizedBox限定Container的寬高為200:
7.Opacity
Opacity控件能調整子控件的不透明度,使子控件部分透明,不透明度的量從0.0到1.1之間,0.0表示完全透明,1.1表示完全不透明。
如圖:我在StackLayout中放置兩個子widget,其中Text在Stack下方,我把上層Opacity布局的透明度設置為0.5,我們可以隱約看見下層Text顯示的內容,讀者可自行把透明度更改值測試結果。
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new OpacityLayoutDemo()));
}
//Opacity控件能調整子控件的不透明度,使子控件部分透明,不透明度的量從0.0到1.1之間,0.0表示完全透明,1.1表示完全不透明。
class OpacityLayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.white,
appBar: new AppBar(
title: new Text('Opacity'),
),
body: new Center(
child: new Stack(
alignment: AlignmentDirectional.center,
children: <Widget>[
new Text("我在透明區(qū)域下方"),
new Opacity(
opacity: 0.5,
child: new Container(
width: 200.0,
height: 220.0,
decoration: new BoxDecoration(color: Colors.redAccent),
),
),
],
),
),
);
}
}
本篇內容有點多,旨在帶大家一起總結掌握Flutter中常用的幾種布局,另外幾種布局在現(xiàn)實開發(fā)中使用場景比較少,就沒有逐一講解分析, 讀者感興趣的話,可以自行查閱官方文檔嘗試下具體用法。