目錄
- Column Widget(垂直布局)
- Row Widget(水平布局)
- Flex Widget and Expanded(彈性布局及擴展)
- Wrap Widget(流式布局)
- Flow Widget(自定義流式布局)
- Stack Widget and Positioned(層疊布局及定位)
Column Widget(垂直布局)
子 widget 按照垂直方向排列冲杀,繼承自 flex
Column({
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.vertical,
mainAxisAlignment: mainAxisAlignment,
mainAxisSize: mainAxisSize,
crossAxisAlignment: crossAxisAlignment,
textDirection: textDirection,
verticalDirection: verticalDirection,
textBaseline: textBaseline,
);
- key:當前元素的唯一標識符(類似于 Android 中的 id)
- mainAxisAlignment :表示子 widget 在 Column 空間內(nèi)的對齊方式。當值為 mainAxisSize = min 時無意義睹酌,因為此時 Column 的寬度 = 所有子 widget 的寬度之和权谁。當 mainAxisSize = max 時有意義,MainAxisAlignment.start表示沿textDirection的初始方向?qū)R憋沿,如textDirection取值為TextDirection.ltr時旺芽,則MainAxisAlignment.start表示左對齊,textDirection取值為TextDirection.rtl時表示從右對齊辐啄。而MainAxisAlignment.end和MainAxisAlignment.start正好相反采章;MainAxisAlignment.center表示居中對齊。textDirection是mainAxisAlignment的參考系
- mainAxisSize :表示 Column 在水平方向占用的空間壶辜。max 表示最大寬度悯舟,min 表示最小寬度,也就是所有子 widget 的寬度之和
- crossAxisAlignment:表示子 widgets 在縱軸方向的對齊方式砸民,Column 的高度等于子 widgets 中高度最高子元素的高度抵怎。它的取值和MainAxisAlignment一樣(包含start、end岭参、 center三個值)反惕,verticalDirection 是 crossAxisAlignment 的參考系
- textDirection:表示水平方向子 widget 的布局順序,默認為由左向右
- verticalDirection:表示 Column 的縱軸對齊方式演侯,默認為 VerticalDirection.down 從上到下
- textBaseline:文本繪制基線(alphabetic/ideographic)
- children:子 widget 集合
/**
* @des Column Widget
* @author liyongli 20190422
* */
class ColumnWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _ColumnState();
}
/**
* @des Column Widget State
* @author liyongli 20190422
* */
class _ColumnState extends State<ColumnWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Row Widget"),
),
body: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
verticalDirection: VerticalDirection.up,
children: <Widget>[
new Text("我是一只小小鳥 ",
style: new TextStyle(
color: Color(0xff2196F3),
height: 3.0
)),
new Text("飛著飛著",
style: new TextStyle(
color: Color(0xff2196F3),
height: 2.0
)),
new Text("我更有勁了",
style: new TextStyle(
color: Color(0xff2196F3),
height: 4.0
))
],
),
),
);
}
}
Row Widget(水平布局)
子 widget 按照水平方向排列姿染,繼承自 flex
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,
);
- key:當前元素的唯一標識符(類似于 Android 中的 id)
- mainAxisAlignment :表示子 widget 在 row 空間內(nèi)的對齊方式。當值為 mainAxisSize = min 時無意義秒际,因為此時 row 的寬度 = 所有子 widget 的寬度之和悬赏。當 mainAxisSize = max 時有意義狡汉,MainAxisAlignment.start表示沿textDirection的初始方向?qū)R,如textDirection取值為TextDirection.ltr時闽颇,則MainAxisAlignment.start表示左對齊轴猎,textDirection取值為TextDirection.rtl時表示從右對齊。而MainAxisAlignment.end和MainAxisAlignment.start正好相反进萄;MainAxisAlignment.center表示居中對齊。textDirection是mainAxisAlignment的參考系
- mainAxisSize :表示 row 在水平方向占用的空間锐峭。max 表示最大寬度中鼠,min 表示最小寬度,也就是所有子 widget 的寬度之和
- crossAxisAlignment:表示子 widgets 在縱軸方向的對齊方式沿癞,row 的高度等于子 widgets 中高度最高子元素的高度援雇。它的取值和MainAxisAlignment一樣(包含start狰腌、end熬词、 center三個值),verticalDirection 是 crossAxisAlignment 的參考系
- textDirection:表示水平方向子 widget 的布局順序掠拳,默認為由左向右
- verticalDirection:表示 row 的縱軸對齊方式蚕涤,默認為 VerticalDirection.down 從上到下
- textBaseline:文本繪制基線(alphabetic/ideographic)
- children:子 widget 集合
import 'package:flutter/material.dart';
/**
* @des Row Widget
* @author liyongli 20190422
* */
class RowWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _RowState();
}
/**
* @des Row Widget State
* @author liyongli 20190422
* */
class _RowState extends State<RowWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Row Widget"),
),
body: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
verticalDirection: VerticalDirection.up,
children: <Widget>[
new Text("我是一只小小鳥 ",
style: new TextStyle(
color: Color(0xff2196F3),
height: 3.0
)),
new Text(" 翅膀硬了",
style: new TextStyle(
color: Color(0xff2196F3),
height: 2.0
)),
new Text(" 不停的飛",
style: new TextStyle(
color: Color(0xff2196F3),
height: 4.0
))
],
),
),
);
}
}
Flex Widget and Expanded(彈性布局及擴展)
Flex Widget(彈性布局)
flex 可以按水平或垂直方向排列子 widget筐赔,并且允許子 widget 按照比例分配父 widget 的空間,row 和 column 均繼承自 flex
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>[],
})
- key:當前元素的唯一標識符(類似于 Android 中的 id)
- direction:彈性布局的方向, Row默認為水平方向揖铜,Column默認為垂直方向
- mainAxisAlignment :表示子 widget 在 flex 空間內(nèi)的對齊方式茴丰。當值為 mainAxisSize = min 時無意義,因為此時 row 的寬度 = 所有子 widget 的寬度之和天吓。當 mainAxisSize = max 時有意義贿肩,MainAxisAlignment.start表示沿textDirection的初始方向?qū)R,如textDirection取值為TextDirection.ltr時龄寞,則MainAxisAlignment.start表示左對齊汰规,textDirection取值為TextDirection.rtl時表示從右對齊。而MainAxisAlignment.end和MainAxisAlignment.start正好相反物邑;MainAxisAlignment.center表示居中對齊溜哮。textDirection是mainAxisAlignment的參考系
- mainAxisSize :表示 flex 在水平方向占用的空間。max 表示最大寬度拂封,min 表示最小寬度茬射,也就是所有子 widget 的寬度之和
- crossAxisAlignment:表示子 widgets 在縱軸方向的對齊方式,flex 的高度等于子 widgets 中高度最高子元素的高度冒签。它的取值和MainAxisAlignment一樣(包含start在抛、end、 center三個值)萧恕,verticalDirection 是 crossAxisAlignment 的參考系
- textDirection:表示水平方向子 widget 的布局順序刚梭,默認為由左向右
- verticalDirection:表示 flex 的縱軸對齊方式肠阱,默認為 VerticalDirection.down 從上到下
- textBaseline:文本繪制基線(alphabetic/ideographic)
- children:子 widget 集合
Expanded(擴展)
可以按照設(shè)定的比例值擴展/擴大在 row、column朴读、flex 布局中 widget 的所用空間
Expanded({
flex:1, // 當 flex = 0 時為占用空間不擴展
child: Container(
height: 30.0
),
})
水平方向擴展
/**
* @des Flex Widget
* @author liyongli 20190422
* */
class FlexWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _FlexState();
}
/**
* @des Flex Widget State
* @author liyongli 20190422
* */
class _FlexState extends State<FlexWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Row Widget"),
),
body: new Column(
children: <Widget>[
new Flex(
direction: Axis.horizontal,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Color(0xff333333),
height: 20.0,
),
),
Expanded(
flex: 2,
child: Container(
color: Color(0xff999999),
height: 20.0,
),
),
Expanded(
flex: 2,
child: Container(
color: Color(0xff666666),
height: 20.0,
),
)
],
)
],
),
),
);
}
}
垂直方向擴展
/**
* @des Flex Widget
* @author liyongli 20190422
* */
class FlexWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _FlexState();
}
/**
* @des Flex Widget State
* @author liyongli 20190422
* */
class _FlexState extends State<FlexWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Flex Widget"),
),
body: new Center(
child: new SizedBox(
width: 20,
child: new Flex(
direction: Axis.vertical,
children: <Widget>[
// 第一部分占五分之一
Expanded(
flex: 1,
child: Container(
color: Color(0xff333333),
height: 20,
),
),
// 第二部分占五分之二
Expanded(
flex: 2,
child: Container(
color: Color(0xff999999),
height: 20,
),
),
// 第三部分占五分之二
Expanded(
flex: 2,
child: Container(
color: Color(0xff666666),
height: 20,
),
),
],
),
)
)
),
);
}
}
Wrap Widget(流式布局)
若布局中包含的 widget 超出屏幕范圍屹徘,且需要自動折行展示,那么你需要使用流式布局 wrap 來實現(xiàn)衅金,wrap 的構(gòu)成與 flex + row + column 相似
Wrap({
Key key,
this.direction = Axis.horizontal,
this.alignment = WrapAlignment.start,
this.spacing = 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>[],
}) : super(key: key, children: children);
(key噪伊,alignment,crossAxisAlignment氮唯,textDirection鉴吹,verticalDirection,children 參數(shù)意義與 flex / row / column 相同)
- spacing:行間距
- runAlignment:列間距
- runSpacing:縱軸方向上對齊方式
/**
* @des Wrap Widget
* @author liyongli 20190423
* */
class WrapWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _WrapState();
}
/**
* @des Wrap Widget State
* @author liyongli 20190423
* */
class _WrapState extends State<WrapWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Wrap Widget"),
),
body: new Center(
child: new Wrap(
spacing: 20.0, // 主軸(水平)方向間距
runSpacing: 5.0, // 縱軸(垂直)方向間距
alignment: WrapAlignment.center, //沿主軸方向居中
children: <Widget>[
new RaisedButton(
child: new Text("A"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("B"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("C"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("D"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
new RaisedButton(
child: new Text("E"),
color: Colors.blue ,
textColor: Colors.white,
onPressed: _BtnClick,
),
],
)
)
),
);
}
// 按鈕點擊監(jiān)聽
void _BtnClick(){
print("不設(shè)置點擊事件按鈕會是灰色的惩琉!");
}
}
Flow Widget(自定義流式布局)
可靈活實現(xiàn)自定義需求布局豆励,且性能較好,但是使用方式復雜
flow 官方介紹是一個對 child 尺寸以及位置調(diào)整非常高效的控件瞒渠,主要是得益于其FlowDelegate良蒸。另外 flow 在用轉(zhuǎn)換矩陣(transformation matrices)對child進行位置調(diào)整的時候進行了優(yōu)化
Flow之所以高效,是因為其在定位過后伍玖,如果使用FlowDelegate中的paintChildren改變child的尺寸或者位置嫩痰,只是重繪,并沒有實際調(diào)整其位置
Flow({
Key key,
@required this.delegate,
List<Widget> children = const <Widget>[],
}) : assert(delegate != null),
super(key: key, children: RepaintBoundary.wrapAll(children));
- key:當前元素的唯一標識符(類似于 Android 中的 id)
- delegate:影響 flow 具體布局的 flowDelegate
- children:子 widget 集合
/**
* @des Flow Widget
* @author liyongli 20190423
* */
class FlowWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _FlowState();
}
/**
* @des Flow Widget State
* @author liyongli 20190423
* */
class _FlowState extends State<FlowWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Flow Widget"),
),
body: new Flow(
delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
children: <Widget>[
new Container(width: 80.0, height:80.0, color: Colors.blue,),
new Container(width: 80.0, height:80.0, color: Colors.black,),
new Container(width: 80.0, height:80.0, color: Colors.amber,),
new Container(width: 80.0, height:80.0, color: Colors.brown,),
new Container(width: 80.0, height:80.0, color: Colors.cyanAccent,),
new Container(width: 80.0, height:80.0, color: Colors.deepOrange,),
],
),
),
);
}
}
/**
* @des Flow Widget Delegate
* @author liyongli 20190423
* */
class TestFlowDelegate extends FlowDelegate {
EdgeInsets margin = EdgeInsets.zero;
TestFlowDelegate({this.margin});
@override
void paintChildren(FlowPaintingContext context) {
var x = margin.left;
var y = margin.top;
//計算所有子 widget 位置
for (int i = 0; i < context.childCount; i++) {
var w = context.getChildSize(i).width + x + margin.right;
if (w < context.size.width) {
context.paintChild(i, transform: new Matrix4.translationValues(x, y, 0.0));
x = w + margin.left;
} else {
x = margin.left;
y += context.getChildSize(i).height + margin.top + margin.bottom;
context.paintChild(i, transform: new Matrix4.translationValues( x, y, 0.0));
x += context.getChildSize(i).width + margin.left + margin.right;
}
}
}
getSize(BoxConstraints constraints){
return Size(double.infinity,200.0);
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return oldDelegate != this;
}
}
Stack Widget and Positioned(層疊布局及定位)
stack 與 Android 中 Frame私沮、Web 中絕對定位類似始赎,子 widget 根據(jù)父 widget 的四個頂點確定位置。stack 布局允許子 widget 堆疊繪制
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
- key:當前元素的唯一標識符(類似于 Android 中的 id)
- alignment:如果子 widget 沒有設(shè)置定位(無 positioned)或只指定了部分定位仔燕,則此參數(shù)為子 widget 的定位標準造垛。
- textDirection:用于決定 alignment 的參考標準,與 row 布局中參數(shù)功能一致
- fit:如果子 widget 沒有定位晰搀,則此參數(shù)將指定子 widget 以怎樣的方式適應 statck 的大小五辽。StackFit.loose = 使用子 widget 的大小,StackFit.expand = 擴展至 stack 大小
- overflow:如果子 widget 超出了 stack 的空間外恕,則此參數(shù)將指定如何顯示杆逗。Overflow.clip = 超出部分隱藏 / 裁剪,Overflow.visible = 產(chǎn)出部分不會隱藏 / 裁剪
- children:子 widget 集合
Positioned(定位)
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);
- left鳞疲、top 罪郊、right、 bottom:代表子 widget 距 statck 左尚洽、上悔橄、右、下四邊的距離
- width、height:設(shè)置定位元素的寬癣疟、高(width挣柬、height 需配合 left、top 睛挚、right邪蛔、 bottom 并結(jié)合實際情況使用,如水平方向時扎狱,只可以設(shè)置 left侧到、right、width 三個參數(shù)中的二個淤击,若同時設(shè)置三個參數(shù)會產(chǎn)生錯誤)
/**
* @des Stack Widget
* @author liyongli 20190423
* */
class StackWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _StackState();
}
/**
* @des Stack Widget State
* @author liyongli 20190423
* */
class _StackState extends State<StackWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Stack Widget"),
),
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment:Alignment.center , //指定無定位或部分定位的子 widget 的對齊方式
children: <Widget>[
Container(
child: Text("我是一只小小鳥", style: new TextStyle(color: Colors.white),),
color: Colors.blue,
),
Positioned(
left: 18.0,
child: Text("想飛飛飛"),
),
Positioned(
top: 18.0,
child: Text("飛的挺高"),
)
],
),
),
),
);
}
}
- 我是一只小小鳥:沒有指定定位(無 positioned)床牧,alignment = Alignment.center,所以居中顯示
- 想飛飛飛:指定了 left 遭贸,屬于部分定位,只指定了水平定位心软,無垂直定位壕吹,所以垂直對齊的方式會按 alignment 的賦值參數(shù),也就是垂直居中顯示
- 飛的挺高:指定了 top删铃,屬于部分定位耳贬,只制定了垂直定位,無水平定位猎唁,所以水平方向會按照 alignment 的賦值參數(shù)咒劲,也就是水平居中顯示
此時,在原基礎(chǔ)上給 stack 設(shè)置 fit = StackFit.expand (子 widget 沒有指定定位時诫隅,此參數(shù)將指定子 widget 以怎樣的方式適應 stack)
/**
* @des Stack Widget
* @author liyongli 20190423
* */
class StackWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() => new _StackState();
}
/**
* @des Stack Widget State
* @author liyongli 20190423
* */
class _StackState extends State<StackWidget>{
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Stack Widget"),
),
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
alignment:Alignment.center , //指定未定位或部分定位widget的對齊方式
fit: StackFit.expand, // expand = 未定位的子 widget 占滿 stack 的可用空間
children: <Widget>[
Positioned(
left: 18.0,
child: Text("想飛飛飛"),
),
Container(
child: Text("我是一只小小鳥", style: new TextStyle(color: Colors.white),),
color: Colors.blue,
),
Positioned(
top: 18.0,
child: Text("飛的挺高"),
)
],
),
),
),
);
}
}
- 我是一只小小鳥:由于它無定位腐魂,所以它的繪制方式遵循 fit 指定的值,也就是占滿 stack
- 想飛飛飛:被遮蓋 / 隱藏逐纬,由于 stack 布局可堆疊的特性蛔屹,它已被第二個子 widget 遮蓋
- 飛的挺高:正常顯示,因為它最后繪制豁生,所以不會被第二個子 widget 遮蓋
本篇到此完結(jié)兔毒,更多 Flutter 跨平臺移動端開發(fā) 原創(chuàng)內(nèi)容持續(xù)更新中~
期待您 關(guān)注 / 點贊 / 收藏 向著 大前端工程師 晉級!