Flutter中的Flex佈局和Web的CSS中的Flex佈局相似。html
在Flutter 中用於控制Flex佈局的有Row,Column,Expanded,Flexible,Spacer,Flex這些控件。web
屬性名 | 類型 | 默認值 | 說明 |
---|---|---|---|
mainAxisAlignment | MainAxisAlignment | MainAxisAlignment.start | 主軸的排列方式 |
mainAxisSize | MainAxisSize | MainAxisSize.max | 主軸佔空間的大小 |
crossAxisAlignment | CrossAxisAlignment | CrossAxisAlignment.center | 次軸的排列方式 |
textDirection | TextDirection | null | 肯定children在水平方向的擺放順序 |
verticalDirection | VerticalDirection | VerticalDirection.down | 肯定children在垂直方向的擺放順序 |
textBaseline | TextBaseline | null | 文字基準線對齊 |
咱們首先建立三個大小不一的Containerapi
class LyoutRowDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("水平佈局"),
),
body: Container(
child: Row(
children: <Widget>[
Container(
height: 100,
width: 50,
color: Colors.redAccent,
),
Container(
height: 50,
width: 50,
color: Colors.blueAccent,
),
Container(
color: Colors.black,
height: 75,
width: 75,
)
],
),
));
}
}
複製代碼
子元素children的排列方式由這兩個屬性決定textDirection和verticalDirection。textDirection決定水平方向的排列方式TextDirection.ltr
從左往右排列(把左看成起始位置),TextDirection.rtl
從右往左排列(把右看成起始位置)。verticalDirection時水平方向的排列方式。當值爲down(向下排列)時起始位置在頂部。當值爲up(向上排列)是起始位置在底部。app
start (默認值) 根據textDirection屬性排列方向。less
將children放置在主軸的起點ide
textDirection屬性值爲rtl時右圖函數
end源碼分析
根據textDirection屬性排列方向。佈局
將children放置在主軸的末尾測試
textDirection屬性值爲rtl時右圖
center
根據textDirection屬性排列方向。
將children放置在主軸的中心
textDirection屬性值爲rtl時右圖
spaceBetween
根據textDirection屬性排列方向。
將主軸方向上的空白區域均分,使得children之間的空白區域相等,首尾child都靠近首尾,沒有間隙
textDirection屬性值爲rtl時右圖
spaceAround
根據textDirection屬性排列方向。
將主軸方向上的空白區域均分,使得children之間的空白區域相等,可是首尾空白區域爲一半
textDirection屬性值爲rtl時右圖
spaceEvenly
根據textDirection屬性排列方向
將主軸方向上的空白區域均分,使得children之間的空白區域相等,包括首尾空白區域
textDirection屬性值爲rtl時右圖
都以 mainAxisAlignment: MainAxisAlignment.start
爲例
start
將子元素在交叉軸上起點對齊。設置verticalDirection爲VerticalDirection.up右圖。
end
將子元素在交叉軸上末尾對齊。設置verticalDirection爲VerticalDirection.up右圖。
center
將子元素在交叉軸上居中對齊。設置verticalDirection無改變。
strentch
將子元素在交叉軸上拉伸
baseline
基準線對其適用於文字,咱們首先建立文字的start。使用baseline。必須設置textBaseline屬性。不一樣文字的文字基準線。主要是字母文字(如:英語)和表意文字(如:漢語)的基準線是不一樣的。必須告訴Flutter你使用的文字。
Row(
crossAxisAlignment: CrossAxisAlignment.start,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
'Flutter',
style: TextStyle(
color: Colors.yellow,
fontSize: 30.0
),
),
Text(
'Flutter',
style: TextStyle(
color: Colors.blue,
fontSize: 20.0
),
),
],
);
複製代碼
設置對齊方式爲基準線
mainAxisSize只有兩個值一個是min一個是max。默認是max。
當值爲min時候。主軸縮緊,變爲最小。
如圖:即便這時mainAxisAlignment:爲MainAxisAlignment.spaceAround。主軸的長度仍爲最小狀態
垂直佈局與水平佈局的屬性和方法一致,惟一須要注意的是textDirection(水平排列方式)和verticalDirection(垂直排列方式)
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "垂直佈局",
home: Scaffold(
appBar: AppBar(title: Text("垂直佈局")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Text("1",style: TextStyle(fontSize: 100),),
Expanded(
child: Text("2"),
),
Text("33333"),
Text("4")
],
),
)));
}
}
複製代碼
爲了便於觀看咱們將1擴大
咱們查看如下源碼便於理解
能夠看到Row
和Column
都繼承與Flex
。
class Row extends Flex class Column extends Flex 複製代碼
而Row
的構造函數可選命名參數(即{}
包裹的參數)有8個。
傳入父級的super
構造函數卻有9個,多出了direction: Axis.horizontal
。
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>[],
}) : super(
children: children,
key: key,
direction: Axis.horizontal,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
}
複製代碼
再Flex中屬性direction
實際上時主軸的方向。且被@required
標註。它是必選的。
Flex({
Key key,
@required this.direction,
this.mainAxisAlignment = MainAxisAlignment.start,
this.mainAxisSize = MainAxisSize.max,
this.crossAxisAlignment = CrossAxisAlignment.center,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
this.textBaseline,
List<Widget> children = const <Widget>[],
}) : assert(direction != null),
assert(mainAxisAlignment != null),
assert(mainAxisSize != null),
assert(crossAxisAlignment != null),
assert(verticalDirection != null),
assert(crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null),
super(key: key, children: children);
複製代碼
上面的例子咱們遇到從未見過的一個widget。很容易就能夠看出。Expanded會忽略子元素的大小並強制自動擴展使主軸填充父級可用的空白區域。Expanded必須是Row、Column、Flex的children。
下面兩個例子便於更好理解:
//當子元素只有一個Expanded時
class LyoutExpanded extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Expanded"),
),
body: Center(
child: Container(
child: Column(
children: <Widget>[
Expanded(
child: Container(
color: Colors.blue,
width: 100,//這個被忽略掉了
),
),
],
),
),
),
);
}
}
複製代碼
class LyoutExpanded extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Expanded"),
),
body: Center(
child: Container(
height: 50,//填充使主軸擴展至父級高度
child: Column(
children: <Widget>[
Expanded(
child: Container(
color: Colors.blue,
height: 100,//
width: 100,
),
),
],
),
),
),
);
}
}
複製代碼
Expanden還有一個屬性爲flex
默認值爲1。表明權重。當子元素有多個Expanden。按照權重值佔據父級的高度(row時爲水平寬度)
<Widget>[
Expanded(
flex: 2,
child: Container(
color: Colors.blue,
width: 100,
),
),
Expanded(
child: Container(
color: Colors.red,
width: 100,
),
),
Expanded(
child: Container(
color: Colors.yellow,
width: 100,
),
),
],
複製代碼
咱們能夠看到藍色的塊時紅和黃的二倍。
Flexible組件可使Row、Column、Flex等子組件在主軸方向有填充可用空間的能力(例如,Row在水平方向,Column在垂直方向),可是它與Expanded組件不一樣,它不強制子組件填充可用空間。一樣Flexible組件必須是Row、Column、Flex等組件的children。
Flexiible 有屬性fit
當屬性值FlexFit.tight時。Flexible和Expanded沒有區別。
從源碼能夠看出Expanded繼承與Flexible
且引用 上級構造函數傳入fit的值爲FlexFit.tight。
class Expanded extends Flexible //省區其餘源碼 const Expanded({
Key key,
int flex = 1,
@required Widget child,
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}
複製代碼
在Column的children中傳入。屬性fit:爲FlexFit.tight,按照權值填充空白區域。
<Widget>[
Flexible(
fit: FlexFit.tight,
child: Container(
color: Colors.blue,
height: 100,
width: 100,
),
),
Flexible(
fit: FlexFit.tight,
flex: 2,
child: Container(
color: Colors.yellow,
height: 100,
width: 100,
),
),
],
複製代碼
一旦fit的值爲FlexFit.loose(默認值)先按flex的值分主軸肯定佔主軸的大小,再按child調整元素的高度。不強制拉伸
例如第一個爲tight強制擴展。第二個爲loose不強制擴展。
<Widget>[
Flexible(
fit: FlexFit.tight,
child: Container(
color: Colors.blue,
height: 100,
width: 100,
),
),
Flexible(
fit: FlexFit.loose,
flex: 2,
child: Container(
color: Colors.yellow,
height: 100,
width: 100,
),
),
],
複製代碼
由於第二個爲loose因此黃顏色的Container高度爲100。而藍色Container的 fit值爲 tight,他的大小和上一個例子一致。
Spacer建立一個能夠調整的空白區域,可用於調整Flex容器(Row或者Colum)中widget之間的間距。
一旦children裏面包含了Spacer。mainAxisAlignment
的屬性值將起不到做用。Spacer已經佔據了全部額外的空間,所以沒有剩餘的空間能夠從新分配。
Row(
mainAxisAlignment: MainAxisAlignment.end,//起不到做用
children: <Widget>[
Container(
height: 100,
width: 50,
color: Colors.redAccent,
),
Spacer(),
Container(
height: 50,
width: 50,
color: Colors.blueAccent,
),
Container(
color: Colors.black,
height: 75,
width: 75,
)
],
),
複製代碼
絕大多數flutter的widget是盒子。能夠將他們疊放,堆疊,嵌套。
咱們能夠層級嵌套盒子,可是若是一個盒子不適合(大小不適合)另外一個盒子。該如何解決?
爲了解決這個問題Flutter提供了FittedBox,這個和移動端的ImageView相似。
它實現的功能是使子元素縮放(fit)或者調整位置(alignment)
爲了便於理解咱們能夠找一個圖片進行測試
class Lyoutfitdemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("data")),
body: Container(
height: 250,
width: 250,
color: Colors.indigo[200],
child: FittedBox(
child: Image.asset('images/fittedbox.png')),
),
);
}
}
複製代碼
5.BoxFit.fitWidth
備註:不用建立FittedBox,Image含有屬性fit。值效果和FittedBox一致。
Stack與web絕對定位佈局模型相似。Stack不是按行或者列來佈局的,而是按照特定順序和位置堆疊。可使用 Positioned
和Align
做爲Stack的的定位。
示例:使用層疊佈局實現一個 圖片漸變效果
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "漸變狀態欄",
home: Scaffold(
body: Container(
height: 400,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Image.asset(
'images/Stack.png',
fit: BoxFit.cover,
),
const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment(0.0, -1.0),
end: Alignment(0.0, -0.4),
colors: <Color>[Color(0x90000000), Color(0x00000000)],
),
),
),
],
),
),
),
);
}
}
複製代碼
圖片漸變效果
這是兩個控件堆疊在一塊兒的例子。固然子部件能夠出如今父級內的任何位置。經過兩種widget實現對位置的控制。
Align爲對齊部件,設置child的在父級的(如container、stack等)對齊方式,並根據child的尺寸調整自身的尺寸。
本文的父級容器選用stack
其中有這幾種屬性
topLeft(左上),topCenter(頂部中央),topRight(右上),centerLeft,center,centerRight,bottomLeft,bottomCenter,bottomRight
class LayoutAlignDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("align部件"),
),
body: Stack(
children: <Widget>[
Align(
alignment: Alignment.topLeft,
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
],
));
}
}
複製代碼
lerp方法有三個參數,前兩個參數爲Alignment類型,第三個參數爲小數。
當t爲0時,這個方法返回的值爲a。
當t爲1時,返回b的值。
當t爲0.5時,這個widget位置就位於a和b指定的位置中間。
因此這個t爲一個偏移量。指定爲a到b之間的偏移。
//在Stack children中添加一個align
Align(
alignment: Alignment.lerp(Alignment.bottomCenter, Alignment.center,0.5),//位於Alignment.bottomCenter和Alignment.center的中央
child: Container(
width: 100,
height: 100,
color: Colors.yellow,
),
),
複製代碼
上面介紹了相對於對其方式a和b的偏移對其。
FractionalOffset 是另一個偏移方法,它是相對於父部件左上角的偏移。
建立偏移量FractionalOffset (dx,dy)。dx和dy的取值都是0~1。左上的位置爲dx和dy都爲0。
Align(
alignment: FractionalOffset(0, 0.5),
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
複製代碼
另外在源碼中:
class FractionalOffset extends Alignment 複製代碼
FractionalOffset
繼承於Alignment因此Alignment的屬性均可以使用,這樣咱們要使得widget位於左上也能夠用使用FractionalOffset.topLeft
class LayoutAlignDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("align部件"),
),
body: Stack(
children: <Widget>[
Align(
alignment: FractionalOffset(0, 0),
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
Align(
alignment: FractionalOffset(0, 0.5),
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
Align(
alignment: FractionalOffset(0, 1),
child: Container(
width: 100,
height: 100,
color: Colors.red,
),
),
Align(
alignment: Alignment.lerp(Alignment.bottomCenter, Alignment.center,0.5),
child: Container(
width: 100,
height: 100,
color: Colors.yellow,
),
),
Align(
alignment: FractionalOffset.topRight,
child: Container(
width: 100,
height: 100,
color: Colors.yellow,
),
),
],
));
}
}
複製代碼
與之相相似的還有Alignment(dx, dy),以父級容器的中心爲座標系原點。dx的取值範圍爲-1~1,dy的取值範圍爲-1~1。設置子元素的位置。
Positioned 部件能夠控制Stack
中子元素的位置。Positioned 與Align不一樣的是Position必須是Stack的Children。
Position有top
、bottom
、left
、right
、height
,width
。
top
、bottom
、left
、right
這些量都是子元素邊界與父級某一邊界的距離。
也就是說當一個高度和長度固定容器,一旦咱們肯定left或right的一個量和bottom或top的一個量,其位置就能夠肯定。
部分代碼
Stack(
children: <Widget>[
Positioned(
top: 100,
right: 100,
width: 100,
height: 100,
child: Container(
color: Colors.red,
),
),
],
)
複製代碼
源碼分析
const Positioned({
Key key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
@required Widget child,
}) : assert(left == null || right == null || width == null),
assert(top == null || bottom == null || height == null),
super(key: key, child: child);
複製代碼
在水平方向上若是assert(false)會拋出錯誤
而括號裏的值爲left == null || right == null || width == null
left ,right ,width 至少有一個值爲null這個程序纔不會報錯。
也就是說當width未指定(爲空)使left和right是能夠同時存在的。這時的容器的寬度會以據邊界的距離(left和right)自動調整。
Stack(
children: <Widget>[
Positioned(
top: 100,
right: 100,
left: 100,
// width: 100,
height: 100,
child: Container(
color: Colors.red,
),
),
],
)
複製代碼
IndexedStack繼承於Stack
class IndexedStack extends Stack 複製代碼
他和Stack不一樣的是有一個index的屬性,表示只顯示第幾個元素。
IndexedStack(
index: 1,//只顯示第二個
children: <Widget>[
Positioned(
top: 100,
right: 100,
left: 100,
height: 100,
child: Container(
color: Colors.red,
),
),
Positioned(
top: 500,
right: 100,
left: 100,
height: 100,
child: Container(
color: Colors.red,
),
),
],
)
複製代碼
Container能夠建立一個矩形元素。能夠用BoxDecoration進行裝飾。背景,邊框,陰影。
class LyoutContainerdemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Container容器"),
),
body: Container(
height: 200,
width: 200,
child: Text("這是一個Container容器"),
decoration: BoxDecoration(
color: Colors.red[200],
// shape: BoxShape.circle, //形狀
border: Border.all(width: 10.0),
boxShadow: [
BoxShadow(
offset: Offset(0.0, 100.0), //模糊偏移量
color: Color.fromRGBO(16, 20, 188, 1.0), //顏色
blurRadius: 10, //模糊
spreadRadius: -9.0, //在應用模糊以前陰影的膨脹量
),
],
),
),
);
}
}
複製代碼
爲了演示功能,這個圖片效果作的比較誇張。
附上一個好看的效果
類似於Css的盒子佈局,咱們也能夠經過Margin給盒子設置外邊距。
Container(
margin: EdgeInsets.all(50.0),//外邊距50.0
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.red[600],
border: Border.all(width: 2.0),
boxShadow: [
BoxShadow(
offset: Offset(2.0, 9.0), //偏移量
color: Colors.red[200], //顏色
blurRadius: 10, //模糊
spreadRadius: -1.0, //在應用模糊以前陰影的膨脹量
),
],
),
),
複製代碼
用於處理容器與子元素之間的距離。與Padding對應的是margin屬性。margin用於處理與其餘組件之間的距離。Padding部件和容器內的pading屬性的效果其實是一致的,當同時出現,真實的padding將是二者相加。
class LayoutPaddingDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Padding 佈局"),
),
body: Container(
padding: EdgeInsets.all(20.0),//#1這兩處的屬性效果是一致的
decoration: BoxDecoration(
color: Colors.yellow,
border: Border.all(color: Colors.white, width: 8.0)),//白色邊框
child: Padding(
child: Container(
decoration: BoxDecoration(
color: Colors.red,
border: Border.all(color: Colors.white, width: 8.0)),//白色邊框
),
padding: EdgeInsets.all(10.0),//#1這兩處的屬性效果是一致的
),
),
);
}
}
複製代碼
Flutter中涉及到佈局的Widget有30多種,同樣的效果的ui,實現的途徑有不少中。本篇就重點涉及幾個經常使用的部件。