您好,登錄后才能下訂單哦!
這篇文章主要介紹了Flutter瀑布流仿寫(xiě)原生的復(fù)用機(jī)制有什么用,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
iOS與android在實(shí)現(xiàn)列表界面的時(shí)候是有重用機(jī)制的,目的就是減少內(nèi)存開(kāi)銷,用時(shí)間換空間。個(gè)人感覺(jué)flutter并沒(méi)有特別強(qiáng)調(diào)復(fù)用,關(guān)于listView.builder 的“復(fù)用”個(gè)人感覺(jué)應(yīng)該是銷毀跟重建的過(guò)程,所以這里用flutter實(shí)現(xiàn)了簡(jiǎn)單的復(fù)用機(jī)制。代碼拙劣,大神勿噴,共同進(jìn)步
右側(cè)是簡(jiǎn)單實(shí)現(xiàn)瀑布流界面,里面顯示的是一共有39個(gè)Widget。左側(cè)是控制臺(tái)打印一共創(chuàng)建的12個(gè)Widget,所以這里就簡(jiǎn)單的實(shí)現(xiàn)了Widget復(fù)用。
這里先簡(jiǎn)單的說(shuō)一下實(shí)現(xiàn)思路。
在渲染界面前,通過(guò)計(jì)算得出全部的Widget的位置坐標(biāo)。
首次渲染創(chuàng)建一屏可視瀑布流Widget.
監(jiān)聽(tīng)滑動(dòng),判斷當(dāng)前頁(yè)面滾動(dòng)方向展示的瀑布流Widget,先去緩存池里拿,如果沒(méi)有就直接創(chuàng)建,添加到組件中進(jìn)行渲染。如果緩存池里有,修改Widget的相對(duì)布局位置。
tip: WaterfallFlow.dart 瀑布流主頁(yè)面;WaterfallFlowItem.dart 瀑布流單元item
效果展示:
WaterfallFlowItem.dart 瀑布流item文件
class WaterfallFlowItem extends StatefulWidget{ Frame? _frame; WaterfallFlowItemState? _waterfallFlowItemState; WaterfallFlowItem({required Frame frame}){ _frame = frame; } Frame getFrame(){ return _frame!; } void setFrame({required Frame frame}) { _frame = frame; _waterfallFlowItemState!.setFrame(frame: frame); } @override State<StatefulWidget> createState() { _waterfallFlowItemState = new WaterfallFlowItemState(frame: _frame!); return _waterfallFlowItemState!; } } class WaterfallFlowItemState extends State<WaterfallFlowItem> with AutomaticKeepAliveClientMixin { Frame? _frame; WaterfallFlowItemState({required Frame frame}){ _frame = frame; } void setFrame({required Frame frame}) { setState(() { _frame = frame; }); } @override Widget build(BuildContext context) { return new Positioned( top: _frame!.top, left: _frame!.left, child: GestureDetector( child: new Container( color: _frame!.index == 12 ? Colors.red : Color.fromARGB(255, 220, 220, 220), width: _frame!.width, height: _frame!.heigth, child: new Text(_frame!.index.toString()), ), onTap: (){ }, ) ); } @override // TODO: implement wantKeepAlive bool get wantKeepAlive => true; }
WaterfallFlow.dart 主界面文件
builder 實(shí)現(xiàn)
@override Widget build(BuildContext context) { return new Container( //去掉scrollView頂部空白間隙 child: MediaQuery.removePadding( context: context, removeTop: true, child: Scrollbar( //isAlwaysShown: true, //showTrackOnHover: true, //scrollView child: new SingleChildScrollView( controller: _scrollController, child: new Container( width: MediaQuery.of(context).size.width, //最大高度 height: _maxHeight, color: Colors.white, child: new Stack( //幀布局下的瀑布流單元格item集合 children: _listWidget, ), ), ), ) ), ); }
聲明的屬性
//瀑布流間隔 double sep = 5; //瀑布流寬度 double? _width; //最大高度 double _maxHeight = 0; //左側(cè)最大高度 double leftHeight = 0; //右側(cè)最大高度 double rightHeight = 0; //主界面高度 double _mineContentHeight = 0; //瀑布流item緩存池 List<WaterfallFlowItem> _bufferPoolWidget = []; //當(dāng)前顯示的瀑布流item List<WaterfallFlowItem> _listWidget = []; //當(dāng)前組渲染frame對(duì)象保存 List<Frame> _fList = []; //總frame集合 List<Frame> _frameList = []; //數(shù)據(jù)源這里只保存高度 List<double> _list = [ 100,150,45,11,140,89,212,21,434,545,100,150,45,11,140,89,212,21,434,545, 100,150,45,11,140,89,212,21,434,545,100,150,45,11,140,89,212,21,434,545]; //滑動(dòng)監(jiān)聽(tīng) ScrollController _scrollController = new ScrollController(); //滑動(dòng)偏移量 double _scrollOff = 0;
計(jì)算主窗口scrollView 高度
//獲取最大高度,并計(jì)算出全部的瀑布流位置 void getMaxHeight(){ List<Frame> fList = []; double width = (_width! - sep * 3) / 2.0; double maxHeight = _maxHeight; for(int i = _frameList.length;i < _list.length;i++){ double height = _list[i]; bool isLeft = (leftHeight <= rightHeight); double left = isLeft ? sep : (width + sep * 2); maxHeight = isLeft ? leftHeight : rightHeight; Frame frame = Frame(leftP: left, topP: maxHeight, widthP: width, heigthP: height,indexP: i); if(isLeft == true) { leftHeight += (height + sep); } else { rightHeight += (height + sep); } fList.add(frame); } _maxHeight = max(leftHeight, rightHeight); _frameList.addAll(fList); //刷新 setState(() {}); }
Frame 位置信息類
class Frame{ double left = 0;//左 double top = 0;//右 double width = 0;//寬度 double heigth = 0;//高度 int index = 0;//索引 Frame({required leftP ,required topP, required widthP, required heigthP, required indexP}){ left = leftP * 1.0; top = topP * 1.0; width = widthP * 1.0; heigth = heigthP * 1.0; index = indexP; } }
生成瀑布流Widget單元item
//重用池里生成item _takeReuseFlowItem(Frame f,dynamic block){ WaterfallFlowItem? waterfallFlowItem; //是否重用,是,直接修改frame;否,重新渲染。 bool isReUse = false; //有,從緩存池里取(緩存中的已在結(jié)構(gòu)樹(shù)里,可以修改幀布局位置) if(_bufferPoolWidget.length > 0){ waterfallFlowItem = _bufferPoolWidget.last; waterfallFlowItem.setFrame(frame: f); _bufferPoolWidget.removeLast(); isReUse = true; } //沒(méi)有,直接創(chuàng)建(不緩存中的,需要調(diào)用setState方法渲染) if(waterfallFlowItem == null) { waterfallFlowItem = new WaterfallFlowItem(frame: f,); isReUse = false; } block(waterfallFlowItem,isReUse); }
創(chuàng)建首屏全部可視瀑布流Widget單元組件
//渲染瀑布流item createWaterfallFlow(int index){ getMaxHeight(); //這里加點(diǎn)延遲,保證獲取最大高度完成(不太嚴(yán)謹(jǐn),大神有好方法請(qǐng)賜教[抱拳]) Future.delayed(Duration(milliseconds: 100),(){ _mineContentHeight = context.size!.height; for(var i = 0;i < _frameList.length;i++){ Frame f = _frameList[i]; //判斷可視化邏輯 if(f.top <= _mineContentHeight + _scrollOff) { _takeReuseFlowItem(f,(WaterfallFlowItem waterfallFlowItem,bool isReuse){ _listWidget.add(waterfallFlowItem); }); } } setState(() { }); }); }
滑動(dòng)過(guò)程中進(jìn)行重用渲染
//獲取上滑狀態(tài)當(dāng)前顯示的下一個(gè)item位置 Frame? _getUpNeedShowFrame(){ Frame? f; WaterfallFlowItem? lastWaterfallFlowItem = _listWidget.last; if(lastWaterfallFlowItem.getFrame().index + 1 < _frameList.length) { f = _frameList[lastWaterfallFlowItem.getFrame().index + 1]; } return f; } //獲取下滑狀態(tài)當(dāng)前顯示的上一個(gè)item位置 Frame? _getDownNeedShowFrame(){ Frame? f; WaterfallFlowItem? lastWaterfallFlowItem = _listWidget[0]; if(lastWaterfallFlowItem.getFrame().index - 1 >= 0) { f = _frameList[lastWaterfallFlowItem.getFrame().index - 1]; } return f; } //超出界面可視范圍的瀑布流加入緩存池 void addFlowItemAddToBufferPool(){ List<WaterfallFlowItem> list = []; for(int i = 0; i < _listWidget.length;i++){ WaterfallFlowItem? waterfallFlowItem = _listWidget[i]; Frame? frame = waterfallFlowItem.getFrame(); if((frame.top + frame.heigth) < _scrollOff || frame.top > _mineContentHeight + _scrollOff) { _bufferPoolWidget.add(waterfallFlowItem); list.add(waterfallFlowItem); } } if(list.length != 0) { for(int i= 0;i < list.length;i++){ WaterfallFlowItem? waterfallFlowItem = list[i]; if(_listWidget.contains(waterfallFlowItem)){ _listWidget.remove(waterfallFlowItem); } } } //從緩存池里獲取item //上滑狀態(tài) Frame? upNextFrame = _getUpNeedShowFrame(); if(upNextFrame != null) { //debugPrint('我是在復(fù)用 ${upNextFrame.index} ,${upNextFrame.top},${_mineContentHeight + _scrollOff}'); if(upNextFrame.top <= _mineContentHeight + _scrollOff) { debugPrint('我在上滑重置第${upNextFrame.index}個(gè)frame'); _takeReuseFlowItem(upNextFrame,(WaterfallFlowItem waterfallFlowItem,bool isReuse){ _listWidget.add(waterfallFlowItem); if(!isReuse){ debugPrint('我不是復(fù)用'); setState(() {}); } else { debugPrint('我是復(fù)用'); waterfallFlowItem.setFrame(frame: upNextFrame); } }); } } //下滑狀態(tài) Frame? downNextFrame = _getDownNeedShowFrame(); if(downNextFrame != null) { //debugPrint('我是在復(fù)用 ${downNextFrame.index} ,${downNextFrame.top},${_mineContentHeight + _scrollOff}'); if(downNextFrame.top + downNextFrame.heigth > _scrollOff && downNextFrame.top + downNextFrame.heigth < _mineContentHeight + _scrollOff) { debugPrint('我在下滑重置第${downNextFrame.index}個(gè)frame'); _takeReuseFlowItem(downNextFrame,(WaterfallFlowItem waterfallFlowItem,bool isReuse){ _listWidget.insert(0, waterfallFlowItem); if(!isReuse){ debugPrint('我不是復(fù)用'); setState(() {}); } else { debugPrint('我是復(fù)用'); waterfallFlowItem.setFrame(frame: downNextFrame); } }); } } }
滾動(dòng)監(jiān)聽(tīng)
_scrollController.addListener(() { _scrollOff = _scrollController.offset; //加入緩存池,并進(jìn)行復(fù)用 addFlowItemAddToBufferPool(); debugPrint('總共:${_listWidget.length + _bufferPoolWidget.length} 個(gè)'); });
基本上flutter的瀑布流復(fù)用邏輯就完成了,代碼拙劣,里面有些地方需要優(yōu)化,比如:快速滑動(dòng)防護(hù),item的內(nèi)容渲染。flutter對(duì)于界面渲染已經(jīng)很極致了,重寫(xiě)復(fù)用有點(diǎn)倒退的趕腳。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Flutter瀑布流仿寫(xiě)原生的復(fù)用機(jī)制有什么用”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來(lái)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。