網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
簡(jiǎn)介
我們?cè)陂_(kāi)發(fā)web應(yīng)用的時(shí)候,有時(shí)候?yàn)榱诉m應(yīng)瀏覽器大小的調(diào)整,需要?jiǎng)討B(tài)對(duì)頁(yè)面的組件進(jìn)行位置的調(diào)整。這時(shí)候就會(huì)用到flow layout,也就是流式布局。
同樣的,在flutter中也有流式布局,這個(gè)流式布局的名字叫做Flow。事實(shí)上,在flutter中,F(xiàn)low通常是和FlowDelegate一起使用的,F(xiàn)lowDelegate用來(lái)設(shè)置Flow子組件的大小和位置,通過(guò)使用FlowDelegate.paintChildre可以更加高效的進(jìn)行子widget的重繪操作。今天我們來(lái)詳細(xì)講解flutter中flow的使用。
Flow和FlowDelegate
先來(lái)看下Flow的定義:
class Flow extends MultiChildRenderObjectWidget
Flow繼承自MultiChildRenderObjectWidget,說(shuō)它里面可以包含多個(gè)子widget。
再來(lái)看下它的構(gòu)造函數(shù):
Flow({
Key? key,
required this.delegate,
List<Widget> children = const <Widget>[],
this.clipBehavior = Clip.hardEdge,
}) : assert(delegate != null),
assert(clipBehavior != null),
super(key: key, children: RepaintBoundary.wrapAll(children));
可以看到Flow中主要有三個(gè)屬性,分別是delegate,children和clipBehavior。
children很好理解了,它就是Flow中的子元素。
clipBehavior是一個(gè)Clip類型的變量,表示的是如何對(duì)widget進(jìn)行裁剪。這里的默認(rèn)值是none。
最后一個(gè)非常重要的屬性就是FlowDelegate,FlowDelegate主要用來(lái)控制Flow中子widget的位置變換。所以,當(dāng)我們?cè)贔low中定義好子widget之后,剩下的就是定義FlowDelegate來(lái)控制如何展示這些子widget。
FlowDelegate是一個(gè)抽象類,所以我們?cè)谑褂玫臅r(shí)候,需要繼承它。
FlowDelegate有幾個(gè)非常重要的方法:
Size getSize(BoxConstraints constraints) => constraints.biggest;
這個(gè)方法用來(lái)定義Flow的size,對(duì)于Flow來(lái)說(shuō),它的size是和子widget的size是獨(dú)立的,F(xiàn)low的大小通過(guò)getSize方法來(lái)定義。
接下來(lái)是getConstraintsForChild方法:
BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints;
getConstraintsForChild用來(lái)控制子widget的Constraints。
paintChildren用來(lái)控制如何繪制子widget,也是我們必須要實(shí)現(xiàn)的方法:
void paintChildren(FlowPaintingContext context);
FlowDelegate還有兩個(gè)方法,分別用來(lái)判斷是否需要Relayout和Repaint,這兩個(gè)方法的參數(shù)都是FlowDelegate:
bool shouldRelayout(covariant FlowDelegate oldDelegate) => false;
bool shouldRepaint(covariant FlowDelegate oldDelegate);
Flow的應(yīng)用
有了上面的介紹,我們基本上已經(jīng)了解如何構(gòu)建Flow了,接下來(lái)我們通過(guò)一個(gè)具體的例子來(lái)加深對(duì)Flow的理解。
在本例中,我們主要是使用Flow來(lái)排列幾個(gè)圖標(biāo)。
首先我們定義一個(gè)圖標(biāo)的數(shù)組:
final List<IconData> buttonItems = <IconData>[
Icons.home,
Icons.ac_unit,
Icons.adb,
Icons.airplanemode_active,
Icons.account_box_rounded,
];
然后通過(guò)每個(gè)圖標(biāo)對(duì)應(yīng)的IconData來(lái)構(gòu)建一個(gè)IconButton的widget:
Widget flowButtonItem(IconData icon) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: IconButton(
icon: Icon(icon,
size: 50,
color: Colors.blue
),
onPressed: () {
buttonAnimation.status == AnimationStatus.completed
? buttonAnimation.reverse()
: buttonAnimation.forward();
},
)
);
}
這里我們使用的是IconButton,為了在不同IconButton之間留一些空間,我們將IconButton封裝在Padding中。
在onPressed方法中,我們希望能夠處理一些動(dòng)畫(huà)效果。這里的buttonAnimation是一個(gè)AnimationController對(duì)象:
AnimationController buttonAnimation = AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
);
有了flowButtonItem之后,我們就可以構(gòu)建Flow了:
Widget build(BuildContext context) {
return Flow(
delegate: FlowButtonDelegate(buttonAnimation: buttonAnimation),
children:
buttonItems.map<Widget>((IconData icon) => flowButtonItem(icon)).toList(),
);
}
Flow的child就是我們剛剛創(chuàng)建的flowButtonItem,F(xiàn)lowButtonDelegate是我們需要新建的類,因?yàn)橹霸跇?gòu)建flowButtonItem的時(shí)候,我們希望進(jìn)行一些動(dòng)畫(huà)的繪制,而FlowDelegate又是真正用來(lái)控制子Widget繪制的類,所以我們需要將buttonAnimation作為參數(shù)傳遞給FlowButtonDelegate。
下面是FlowButtonDelegate的定義:
class FlowButtonDelegate extends FlowDelegate {
FlowButtonDelegate({required this.buttonAnimation})
: super(repaint: buttonAnimation);
final Animation<double> buttonAnimation;
@override
bool shouldRepaint(FlowButtonDelegate oldDelegate) {
return buttonAnimation != oldDelegate.buttonAnimation;
}
@override
void paintChildren(FlowPaintingContext context) {
double dy = 0.0;
for (int i = 0; i < context.childCount; ++i) {
dy = context.getChildSize(i)!.height * i;
context.paintChild(
i,
transform: Matrix4.translationValues(
0,
dy * buttonAnimation.value,
0,
),
);
}
}
FlowButtonDelegate繼承自FlowDelegate,并且傳入了buttonAnimation對(duì)象。
這里我們根據(jù)buttonAnimation是否發(fā)生變化來(lái)決定是否進(jìn)行Repaint。
如果需要進(jìn)行Repaint,那么就要調(diào)用paintChildren的方法。
在paintChildren中,我們根據(jù)child自身的height和buttonAnimation的值來(lái)進(jìn)行動(dòng)畫(huà)的繪制。
那么buttonAnimation的值是如何變化的呢?這就要回顧之前我們創(chuàng)建flowButtonItems的onPress方法了。
在onPress方法中,我們調(diào)用了buttonAnimation.reverse或者buttonAnimation.forward這兩個(gè)方法來(lái)修改buttonAnimation的值。
運(yùn)行之后的效果如下:
初始狀態(tài)下,所有的組件都是在一起的。
當(dāng)我們點(diǎn)擊上面的圖標(biāo)的時(shí)候,我們可以得到下面的界面:
圖標(biāo)在動(dòng)畫(huà)中展開(kāi)了。
總結(jié)
Flow是一種比較復(fù)雜的layout組件,如果和動(dòng)畫(huà)進(jìn)行結(jié)合使用,可以得到非常完美的效果。
本文的例子:github.com/ddean2009/l…
原文鏈接:https://juejin.cn/post/7146098348250890253
相關(guān)推薦
- 2022-04-19 Golang語(yǔ)言的多種變量聲明方式與使用場(chǎng)景詳解_Golang
- 2023-02-14 Android?SharedPreference存儲(chǔ)文件三步走_(dá)Android
- 2024-04-05 docker部署mongodb
- 2022-06-22 Git配置用戶簽名方式的拓展示例實(shí)現(xiàn)全面講解_其它綜合
- 2022-08-05 C語(yǔ)言簡(jiǎn)明介紹常見(jiàn)關(guān)鍵字的用法_C 語(yǔ)言
- 2022-10-05 圖文詳解牛頓迭代算法原理及Python實(shí)現(xiàn)_python
- 2022-05-06 python?selenium中Excel數(shù)據(jù)維護(hù)指南_python
- 2024-03-16 nginx 報(bào) unknown directive “server“ 詭異問(wèn)題處理
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支