8天让iOS开发者上手Flutter之五

上篇文章,咱们已经完成了通信录的列表。这篇文章介绍完成通信录右侧的索引条的功能。markdown

显示索引条

以前咱们已经作过了个人页面的布局,个人页面上有一个列表和一个拍照按钮,和咱们今天要实现的索引条布局十分相似。个人页面的布局以下: image.png 通信录界面的布局,和个人页面的布局都是使用一个Stack包含列表和其余子视图来实现。索引条是紧贴屏幕右侧,而后里面的子视图是由上至下的。因此天然的会想到使用一个Positioned包含Column来实现。Positioned和Stack的组合咱们以前讲过,这两个组合起来使用,就和咱们iOS的约束布局相似,能够设置上左宽高等等。Column就更不用多说,咱们已经使用过不少次了。因此代码以下图所示: image.png网络

而后优化一下索引条的位置,高度咱们设置为屏幕高度的一半,那么上下的间距就不能设置为0了,设置距离上间距为屏幕的1/8看起来比较合适。

image.png

抽取IndexBar

写到这里咱们会发现,这个索引条还有不少的功能须要咱们来实现,仍是有点复杂的,若是代码都写在friends_page.dart里会有点冗余,咱们彻底能够将这个索引条做为一个独立的Widget来实现。新建index_bar.dart文件,代码以下: image.png闭包

实现IndexBar的点击切换状态

当没有触摸到IndexBar的时候,默认是不展现背景色的,文字也是黑色的。当咱们开始点击IndexBar的时候,显示出背景色,而后文字也变成了白色。 实现这个功能,主要是要对GestureDetector的两个方法有所了解。onVerticalDragDown方法会在手指触摸IndexBar的时候就会被调用,onVerticalDragEnd会在手指松开屏幕的时候调用。利用这两个方法就能够实现需求。代码以下: image.png 由于要对文字的颜色进行修改,因此初始化Text的时候,就须要使用变量_textColor; image.png函数

获取当前选中的下标

一样是对GestureDetector的一个手势方法的使用,onVerticalDragUpdate这个方法的调用时机,在手指移动的时候会不停的调用这个方法。这个方法有一个DragUpdateDetails参数,它包含了手指所在的坐标信息。不过是相对于整个屏幕的坐标,能够将它转化为相对于IndexBar的坐标,而后经过计算能够获得咱们当前选中的是哪一个下标。代码以下: image.png 方法中的~/是flutter特有的运算符,意思是除后取整。而clamp()是对边界状况的处理,意思调用该函数的结果在它的两个参数之间。布局

回调选中的下标

这里的回调,和OC里面的block,Swift里面的闭包都是一个意思。flutter里面带有下划线的变量是私有的,外部没法访问的。因此对外暴露的参数,不能写在_IndexBarState类里面,须要写在IndexBar类里面。声明一个闭包(或者叫block)属性,做为必传参数在初始化的时候传入。 image.png 这样,在friends_page.dart文件中初始化IndexBar的时候,就须要传入一个闭包。而后IndexBar内部在onVerticalDragUpdate的时候,调用这个闭包,就能够将当前选中的下标回调给外部了。 image.png image.png 这个时候,会发现一个小问题,就是点击IndexBar的时候,回调没有执行,只有在点击并手指挪动的时候才会执行。因此须要在onVerticalDragDown方法里面也调用一次闭包。这时候若是直接将onVerticalDragUpdate方法里面的代码复制到onVerticalDragDown方法里面确实没有问题,可是会明显的看到重复的代码太多了。 image.png 因此能够抽取一个方法,将重复的代码放到一块。 image.png 而后调用的时候就简单多了。 image.png性能

优化回调执行的频率

已经成功的实现了回调,可是从打印的结果来看,会发现一样一个下标会被回调许屡次。这样咱们滚动好友列表的时候会形成没必要要的性能消耗。明明只须要滚动一次,结果却滚动了无数次到同一个位置。因此这里咱们须要优化一下,一个很天然的想法就是记录一个_currentIndexLetter,每次执行回调的时候,判断回调的首字母是否和_currentIndexLetter是否不一样,若是是同样的就没有必要回调了,只有不一样的时候,才执行回调。 image.png 代码以下: image.png image.png 这样回调的频率就正常了。优化

滚动好友列表ListView

可滚动的widget都有一个controller属性,用于控制滚动条的行为。controller属性是一个ScrollController对象。可使用它来实现指定滚动到某个位置,实现回到顶部等功能。 滚动好友列表须要一个新的对象ScrollController实例,将它设置给ListView的controller属性,而后就能够经过使用ScrollController实例来操做ListView的滚动。 image.png image.png 这里暂时先将滚动的偏移设置为固定值250,试试看效果。能够看到当咱们点击IndexBar的时候,ListView就会滚动到偏移为250的地方。接下来就是处理滚动的实际偏移值了。spa

滚动的实际偏移,是根据咱们的数据源来计算的。由于咱们的cell的高度是肯定的,不显示组头的cell高度是54,有组头的cell高度是54 + 30 = 84。使用首字母做为key,计算出对应的偏移为offset,而后使用Map(相似iOS中字典)记录下来。因为第一个是否是字母,而是搜索符号,而它对应的偏移也是固定的0。因此能够在初始化Map的时候就指定好。而其余的高度咱们在initState方法中计算。代码以下: image.png3d

有了这个Map以后,咱们在IndexBar的回调方法中,就能够根据IndexBar回调给咱们的首字母获得对应的偏移值了。代码以下: image.pngcode

到这里,咱们的IndexBar基本上就实现了滚动ListView的功能。可是滚动几回以后就会发现一个小问题。。。滚动到底部的几个组头的时候,会出现ListView先将组头滚动到指定位置,而后又滚回底部的状况。缘由很好理解,后面的组头内容不够显示一整个屏幕了。因此咱们这里须要作下处理。这里主要是对ListView的滚动的监听,若是是在iOS中咱们会想去获取滚动视图的contentSize而后减去UITableView的高度,就是UITableView的最大的滚动范围。而在Flutter中,这些都不须要咱们计算了。

若是须要获取到ListView的一些滚动相关的信息,能够将它包裹在NotificationListener里面,它有一个onNotification属性,是一个闭包,能够回调给咱们一些滚动的相关信息。包含在闭包参数ScrollNotification note里面。准确来说滚动相关的信息包含在ScrollNotification的属性metrics里面。它包含当前滚动偏移值,能滚动的最大范围(这就是咱们iOS中contentSize的高减去UITableView的高)等等信息。完整代码以下: image.png_maxScrollExtent定义为一个属性就行了。须要注意的是并不能给初始值为0,不然没有滚动ListView以前,使用IndexBar就没法滚动ListView了。 image.png 至此,IndexBar滚动ListView的功能就实现了。

显示指示器

终于来到了最后一步,显示咱们IndexBar的指示器。首先考虑的就是布局。最初的IndexBar只有右侧的下标一列。如今咱们左侧须要一块容器用来显示咱们的指示器,因此IndexBar的根视图应该考虑改成Row。指示器背景的不规则图形可使用一张图片展现,图片已经准备好了。中间的文字,使用Text就够了。先看下大概布局代码: image.png

若是以为位置不是很合适,能够修改一下各自的宽度。而后是对指示器的显示与隐藏作控制,指示器的显示与隐藏的控制,应该说跟背景色的显示隐藏是相似的。都是在手势的那两个方法里面实现控制。使用一个bool变量来控制指示器的显示与隐藏,在手势的触摸方法和离开方法里面操做这个bool变量,而后setState()就能够实现了指示器的显示与隐藏了。而后是关于指示器的显示文本的。这个文本就是咱们的_currentIndexLetter,直接使用就行了。最后是关于如何控制整个IndexBar的上下位移的。经过对Alignment的使用,发现能够控制IndexBar的上下位移。经过不断的修改Alignment的y值会找到一个合适的y值指向第一个放大镜,那么-y就指向最后一个字母Z。我这里试了几回发现y=-1.13的时候,指示器刚恰好指向第一个放大镜的位置。那么如今的问题就是将1.13 * 2 = 2.26分红_index_words.length - 1份,而后根据选择的下标,取得对应的Alignment的y值。当咱们选择第一个的时候下标为0,y值应该为-1.13,当咱们选择最后一个的时候下标为_index_words.length - 1,y值应该为1.13。根据这些信息就能够找到计算y值的公式。最终的代码以下:
新增两个变量_showIndicator_indicatorAlignmentYimage.png 使用这两个变量还有_currentIndexLetter image.png

到这里,咱们就终于实现了通信录的IndexBar的封装。下一节会介绍一些网络请求了...

相关文章
相关标签/搜索