Flutter仿写一个iOS风格的通讯录
此文章主要介紹怎么使用Flutter的Cupertino風(fēng)格控件,寫一個(gè)iOS風(fēng)格的通訊錄,還有在此過程中遇到的問題及解決辦法。
大家在用Flutter寫App的時(shí)候,一般都會(huì)使用material風(fēng)格的控件,因?yàn)閙aterial風(fēng)格的控件比較豐富,但是,他在iOS上就會(huì)顯得Android氣息比較重,不太適合,所以本文章將通過用仿寫iOS通訊錄,系統(tǒng)地介紹Cupertino控件,及系統(tǒng)的一些底層控件和怎么自己定義優(yōu)美的適合自己的控件。
由于使用的聯(lián)系人三方包的限制,有些功能未能實(shí)現(xiàn),我會(huì)持續(xù)關(guān)注這個(gè)聯(lián)系人插件的更新,及時(shí)加上新功能。
Github地址
首頁
主要用到的控件及問題
CupertinoPageScaffold
一個(gè)iOS風(fēng)格Scaffold,可以添加NavigationBar。
NestedScrollView
實(shí)現(xiàn)浮動(dòng)的NavigationBar和SearchBar。
NestedScrollView我用的自己重寫過的,主要是因?yàn)樵创a中的有兩個(gè)問題。
1、當(dāng)列表滑動(dòng)到底部,然后繼續(xù)滑動(dòng),然后停止,松手,這時(shí)候可列表會(huì)重新滾動(dòng)到底部,但是源碼沒有處理當(dāng)速度等于0的時(shí)候的情況,所以當(dāng)松手的時(shí)候,列表會(huì)回彈回去,回彈距離小于maxScrollExtent。
源碼如下:
@protected ScrollActivity createInnerBallisticScrollActivity(_NestedScrollPosition position, double velocity) {return position.createBallisticScrollActivity(position.physics.createBallisticSimulation(velocity == 0 ? position as ScrollMetrics : _getMetrics(position, velocity),velocity,),mode: _NestedBallisticScrollActivityMode.inner,); }這里當(dāng)velocity == 0的時(shí)候,直接把innerPosition賦值給了createBallisticSimulation方法的position參數(shù),我們繼續(xù)往下看。
ScrollActivity createBallisticScrollActivity(Simulation simulation, {@required _NestedBallisticScrollActivityMode mode,_NestedScrollMetrics metrics, }) {if (simulation == null) return IdleScrollActivity(this);assert(mode != null);switch (mode) {case _NestedBallisticScrollActivityMode.outer:assert(metrics != null);if (metrics.minRange == metrics.maxRange) return IdleScrollActivity(this);return _NestedOuterBallisticScrollActivity(coordinator,this,metrics,simulation,context.vsync,);case _NestedBallisticScrollActivityMode.inner:return _NestedInnerBallisticScrollActivity(coordinator,this,simulation,context.vsync,);case _NestedBallisticScrollActivityMode.independent:return BallisticScrollActivity(this, simulation, context.vsync);}return null; }這里velocity == 0的時(shí)候,執(zhí)行的是
case _NestedBallisticScrollActivityMode.inner:return _NestedInnerBallisticScrollActivity(coordinator,this,simulation,context.vsync,);這時(shí)候的simulation就是上面通過innerPosition得到的,然后傳給了_NestedInnerBallisticScrollActivity,我們?cè)诶^續(xù)往下看,
class _NestedInnerBallisticScrollActivity extends BallisticScrollActivity {_NestedInnerBallisticScrollActivity(this.coordinator,_NestedScrollPosition position,Simulation simulation,TickerProvider vsync,) : super(position, simulation, vsync);final _NestedScrollCoordinator coordinator;@override_NestedScrollPosition get delegate => super.delegate as _NestedScrollPosition;@overridevoid resetActivity() {delegate.beginActivity(coordinator.createInnerBallisticScrollActivity(delegate,velocity,));}@overridevoid applyNewDimensions() {delegate.beginActivity(coordinator.createInnerBallisticScrollActivity(delegate,velocity,));}@overridebool applyMoveTo(double value) {return super.applyMoveTo(coordinator.nestOffset(value, delegate));} }我們發(fā)現(xiàn)這里執(zhí)行的操作并不是我們想要的,當(dāng)velocity == 0,滑動(dòng)距離大于maxScrollExtent的時(shí)候,我們只想滾動(dòng)到列表的最底部,所以我們改一下這里的實(shí)現(xiàn)。此處有兩種實(shí)現(xiàn)方式:
第一種方式:改_getMetrics方法
// This handles going forward (fling up) and inner list is // underscrolled, OR, going backward (fling down) and inner list is // scrolled past zero. We want to skip the pixels we don't need to grow // or shrink over. if (velocity > 0.0) {// shrinkingextra = _outerPosition.minScrollExtent - _outerPosition.pixels; } else if (velocity < 0.0) {// growingextra = _outerPosition.pixels - (_outerPosition.maxScrollExtent - _outerPosition.minScrollExtent); } else {extra = 0.0; } assert(extra <= 0.0); minRange = _outerPosition.minScrollExtent; maxRange = _outerPosition.maxScrollExtent + extra; assert(minRange <= maxRange); correctionOffset = 0.0;這里加上velocity == 0的判斷。
第二種方式:修改createInnerBallisticScrollActivity方法,加上velocity == 0的判斷。
@protected ScrollActivity createInnerBallisticScrollActivity(_NestedScrollPosition position, double velocity) {return position.createBallisticScrollActivity(position.physics.createBallisticSimulation(velocity == 0 ? position as ScrollMetrics : _getMetrics(position, velocity),velocity,),mode: velocity == 0 ? _NestedBallisticScrollActivityMode.independent : _NestedBallisticScrollActivityMode.inner,); }2、當(dāng)我們手動(dòng)調(diào)用position.moveTo方法滾動(dòng)到最底部的時(shí)候,獲取到的maxScrollExtent并不是實(shí)際innerPosition的maxScrollExtent,而應(yīng)該是maxScrollExtent - outerPosition.maxScrollExtent + outerPosition.pixels。
接下來我們分析源碼看看哪里出了問題。
首先,我們看看與之有直接關(guān)聯(lián)的maxScrollExtent方法。
我們看到只是單純的返_maxScrollExtent,那我們看看_maxScrollExtent是在哪里賦值的,經(jīng)過查看源碼得知,_maxScrollExtent賦值的地方主要在下面這個(gè)方法里:
@override bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {assert(minScrollExtent != null);assert(maxScrollExtent != null);if (!nearEqual(_minScrollExtent, minScrollExtent, Tolerance.defaultTolerance.distance) ||!nearEqual(_maxScrollExtent, maxScrollExtent, Tolerance.defaultTolerance.distance) ||_didChangeViewportDimensionOrReceiveCorrection) {assert(minScrollExtent != null);assert(maxScrollExtent != null);assert(minScrollExtent <= maxScrollExtent);_minScrollExtent = minScrollExtent;_maxScrollExtent = maxScrollExtent;_haveDimensions = true;applyNewDimensions();_didChangeViewportDimensionOrReceiveCorrection = false;}return true; }所以我們重寫這個(gè)方法,修改如下:
@override bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {assert(minScrollExtent != null);assert(maxScrollExtent != null);var outerPosition = coordinator._outerPosition;var outerMaxScrollExtent = outerPosition.maxScrollExtent;var outerPixels = outerPosition.pixels;if (outerMaxScrollExtent != null && outerPixels != null) {maxScrollExtent -= outerMaxScrollExtent - outerPixels;maxScrollExtent = math.max(minScrollExtent, maxScrollExtent);}return super.applyContentDimensions(minScrollExtent, maxScrollExtent); }這樣我們成功解決了上面提到的兩個(gè)問題。
CustomScrollView
實(shí)現(xiàn)浮動(dòng)的Index。
SliverPersistentHeader
實(shí)現(xiàn)Index固定在頭部。
CupertinoSliverRefreshIndicator
實(shí)現(xiàn)下拉刷新。
群組
新建聯(lián)系人頁面
編輯頭像
聯(lián)系人詳情
選擇標(biāo)簽
至此,基本完成。
總結(jié)
以上是生活随笔為你收集整理的Flutter仿写一个iOS风格的通讯录的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第1章-确定superboot210如何
- 下一篇: linux下GPRS模块ppp拨号上网