前言

在像通讯录,联系人列表,城市选择列表等数据量比较多的长列表页面中,我们经常会留意到产品设计会在页面的右侧区域提供一个竖向的字母索引列表,供用户点击选择快速定位到长列表中的指定索引位置,以便于用户快速定位到自己要筛选的数据,从而提升用户体验,今天我们就以城市列表为例,来剖析一下,这样的体验效果如果用Flutter来实现

技术实现分析

  • 页面城市列表布局采用ListView嵌套ListView,其中外层ListView负责显示当前城市的分组字母信息,内层Listview负责显示当前字母索引分组下的所有城市
  • 右侧竖向字母索引列表采用ListView展示,当点击右侧字母索引的某一个时,动态的计算出,当前字母索引在城里列表的角标索引值,然后计算得出被点击的字符索引在城里列表里的高度值,通过ScrollController让城里列表自动定位到计算出的高度位置,从而实现,点击字母所以动态定位城市列表位置的联动效果

效果如下

我先把上述效果的相关代码贴出来供大家参考,然后再把代码里涉及到计算位置的核心代码做下详细的讲解,代码里涉及到网络请求,以及网络工具类的封装这里就不再赘述了,读者可以查看我往期博客关于Flutter入门进阶之旅(十七)Flutter dio网络请求里的具体讲解,或者从github上直接找到源代码阅读https://github.com/xiedong11/flutter_app

核心代码

/*** @desc   选择城市地区联动索引页* @author xiedong* @date   2020-04-30.*/class PhoneCountryCodePage extends StatefulWidget {@overrideState<StatefulWidget> createState() => PageState();
}class PageState extends State<PhoneCountryCodePage> {var GET_PHONE_COUNTRY_CODE_URL ="https://raw.githubusercontent.com/xiedong11/flutter_app/master/static/phoneCode.json";List<String> letters = [];List<PhoneCountryCodeData> data;ScrollController _scrollController = ScrollController();int _currentIndex = 0;@overridevoid initState() {super.initState();getPhoneCodeDataList();}getPhoneCodeDataList() async {var response = await DioUtils.getInstance().get(GET_PHONE_COUNTRY_CODE_URL);var resultEntity = new PhoneCountryCodeEntity.fromJson(json.decode(response));if(resultEntity.code==200){this.setState(() {data = resultEntity.data;for (int i = 0; i < data.length; i++) {letters.add(data[i].name.toUpperCase());}});}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("城市地区选择"),centerTitle: true,),body: Stack(children: <Widget>[data == null || data.length == 0? Text(""): Padding(padding: EdgeInsets.only(left: 20),child: ListView.builder(controller: _scrollController,itemCount: data.length,itemBuilder: (BuildContext context, int index) {return Column(crossAxisAlignment: CrossAxisAlignment.start,children: <Widget>[PhoneCodeIndexName(data[index].name.toUpperCase()),ListView.builder(itemBuilder:(BuildContext context, int index2) {return Container(height: 46,child: GestureDetector(
//                      behavior: HitTestBehavior.translucent,child: Padding(padding:EdgeInsets.symmetric(vertical: 10),child: Row(children: <Widget>[Text("${data[index].listData[index2].name}",style: TextStyle(fontSize: 16,color: Color(0xff434343))),Margin(width: 10),Text("+${data[index].listData[index2].code}",style: TextStyle(fontSize: 16,color: Color(0xffD6D6D6)),)],),),onTap: () {Navigator.of(context).pop(data[index].listData[index2].code);},),);},itemCount: data[index].listData.length,shrinkWrap: true,physics:NeverScrollableScrollPhysics()) //禁用滑动事件),],);}),),Align(alignment: new FractionalOffset(1.0, 0.5),child: SizedBox(width: 25,child: Padding(padding: EdgeInsets.only(top: 20),child: ListView.builder(itemCount: letters.length,itemBuilder: (BuildContext context, int index) {return GestureDetector(child: Text(letters[index],style: TextStyle(color: Colors.black),),onTap: () {setState(() {_currentIndex = index;});var height = index * 45.0;for (int i = 0; i < index; i++) {height += data[i].listData.length * 46.0;}_scrollController.jumpTo(height);},);},),),),)],),);}
}class PhoneCodeIndexName extends StatelessWidget {String indexName;PhoneCodeIndexName(this.indexName);Widget build(BuildContext context) {return Container(height: 45,child: Padding(child: Text(indexName,style: TextStyle(fontSize: 20, color: Color(0xff434343))),padding: EdgeInsets.symmetric(vertical: 10),),);}
}

Json数据映射实体类

class PhoneCountryCodeEntity {int code;List<PhoneCountryCodeData> data;String message;PhoneCountryCodeEntity({this.code, this.data, this.message});PhoneCountryCodeEntity.fromJson(Map<String, dynamic> json) {code = json['code'];if (json['data'] != null) {data = new List<PhoneCountryCodeData>();(json['data'] as List).forEach((v) {data.add(new PhoneCountryCodeData.fromJson(v));});}message = json['message'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();data['code'] = this.code;if (this.data != null) {data['data'] = this.data.map((v) => v.toJson()).toList();}data['message'] = this.message;return data;}
}class PhoneCountryCodeData {List<PhoneCountryCodeDataListdata> listData;String name;PhoneCountryCodeData({this.listData, this.name});PhoneCountryCodeData.fromJson(Map<String, dynamic> json) {if (json['listData'] != null) {listData = new List<PhoneCountryCodeDataListdata>();(json['listData'] as List).forEach((v) {listData.add(new PhoneCountryCodeDataListdata.fromJson(v));});}name = json['name'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();if (this.listData != null) {data['listData'] = this.listData.map((v) => v.toJson()).toList();}data['name'] = this.name;return data;}
}class PhoneCountryCodeDataListdata {String code;String name;int id;String groupCode;PhoneCountryCodeDataListdata({this.code, this.name, this.id, this.groupCode});PhoneCountryCodeDataListdata.fromJson(Map<String, dynamic> json) {code = json['code'];name = json['name'];id = json['id'];groupCode = json['groupCode'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();data['code'] = this.code;data['name'] = this.name;data['id'] = this.id;data['groupCode'] = this.groupCode;return data;}
}

博客中Json数据的测试地址为:https://raw.githubusercontent.com/xiedong11/flutter_app/master/static/phoneCode.json 读者测试代码时如果自己的json数据格式跟我的不一样,应该对应自己的json数据格式去解析对应的实体类,确保数据能正确的绑定到视图上。

难点分析

文章开头当点击右侧字母索引的某一个时,动态的计算出当前字母索引在城里列表的角标索引值,然后计算得出被点击的字符索引在城里列表里的高度值,通过ScrollController让城里列表自动定位到计算出的高度位置,从而实现,点击字母所以动态定位城市列表位置的联动效果。

我们通过分析上图可以得出,当我们点击需要定位到的指定位置时,我们只需要计算出当前被点击的字母索引之前的所有item的累加高度值,然后通过ListView中的ScrollController.jumpTo(double value)滑动到指定的高度值,就完成了这整个联动效果。

这里,我们通过遍历我们从网络上获取数据值,很容易计算出需要滑动的高度累加值

核心代码如下

       var height = index * 45.0;  //45.0 字母分组的高度for (int i = 0; i < index; i++) {height += data[i].listData.length * 46.0;   //46.0 每个Item的高度}_scrollController.jumpTo(height);

至此整个实现效果已经完成,完整的项目跟代码请参阅:Flutter进阶之旅专栏

Flutter 城市/通讯录列表字母索引联动效果实现相关推荐

  1. Android 通过ListView实现联系人列表字母索引

    转载:Android自定义View--实现联系人列表字母索引_Code-Porter的博客-CSDN博客_android 字母索引 学习大神的思路和逻辑,很清晰. 这篇博文也不错:安卓仿手机联系人右侧 ...

  2. 实现联系人列表字母索引

    相信大家对这个列表字母索引已经不陌生了,在很多app中也随处可见,像没团的城市地址选择,微信联系人列表,手机通讯录-等等.既然是个这么nb这么实用的功能我们怎么能不Get到来呢,下面就让我们一起造一个 ...

  3. Android自定义View——实现联系人列表字母索引

    相信大家对这个列表字母索引已经不陌生了,在很多app中也随处可见,像没团的城市地址选择,微信联系人列表,手机通讯录-等等.既然是个这么nb这么实用的功能我们怎么能不Get到来呢,下面就让我们一起造一个 ...

  4. android 通讯录 首字母索引,android仿微信通讯录搜索(匹配拼音,字母,索引位置标记颜色)...

    前言: 仿微信通讯录搜索功能,通过汉字或拼音首字母找到匹配的联系人并显示匹配的位置 一:先看效果图 字母索引 搜索匹配 二:功能分析 1:汉字转拼音 通讯录汉字转拼音(首个字符当考虑姓氏多音字), 现 ...

  5. html列表滑动字母索引,js实现做通讯录的索引滑动显示效果和滑动显示锚点效果...

    只做实现..完全没考虑性能优化.所以我实现了以后特别卡. 第一个是在通讯录右边的索引条上进行滑动,滑动到相应字母就跳转到相应字母的锚点上. 思路:监听touchmove事件,获取clientX和cli ...

  6. 微信小程序点击--实现带字母索引的城市列表

    哈哈 先上图看看是不是这种效果   因为比较穷 所以录制的gif格式图片有水印 之前在网上搜索了好多这种格式的,感觉代码都好多  如果只是点击然后跳转到相应的位置是比较简单的 主要用到的知识就是 sc ...

  7. 微信小程序手把手教你实现带字母索引的城市选择列表

    微信小程序手把手教你实现带字母索引的城市选择列表 前言 需求分析 左边可滑动列表 滑动列表UI实现 item点击事件 右边带字母的索引条 索引条从上到下分别是定位和26个大写字母 索引条响应触摸和点击 ...

  8. Windows Phone 7 LongListSelector控件实现分类列表和字母索引

    在wp7手机里面的联系人列表和程序里面里面我们可以看到一个根据字母索引来定位联系人或者应用程序的控件,那么这个控件就是LongListSelector控件了. LongListSelector是一种比 ...

  9. android 字母搜索栏,android仿微信通讯录搜索示例(匹配拼音,字母,索引位置)

    前言: 仿微信通讯录搜索功能,通过汉字或拼音首字母找到匹配的联系人并显示匹配的位置 一:先看效果图 字母索引 搜索匹配 二:功能分析 1:汉字转拼音 通讯录汉字转拼音(首个字符当考虑姓氏多音字), 现 ...

  10. 联系人列表字母排序索引(一)

    很久没有写博客了,对自己又放松了很多.这篇博客本来早就要写了,迟迟拖到现在.今天这里要说的是,联系人列表,分组带索引,即联系人按字母顺序排列并分组,右边还有索引条.先看下效果: 就是这么一个效果,想必 ...

最新文章

  1. 一款由css3和jquery实现的响应式设计导航
  2. python元组(tuple)使用示例+常用方法+列表(list)和元组(tuple)的异同?
  3. 9.DRN: A Deep Reinforcement Learning Framework for News Recommendation论文详解
  4. 集群节点Elasticsearch升级
  5. 用北斗和阿里云毫米级监控山体滑坡 他还只是铁路工人
  6. 小甲鱼 OllyDbg 教程系列 (十六) : 简单病毒的逆向分析
  7. 深入Managed DirectX9(二)
  8. Programming 2D Games 读书笔记(第二章)
  9. boost线程(二)
  10. 【目标检测】Fast RCNN算法详解(转)
  11. c语言输入数字 获取星期几,输入字母,判断星期几,求大神指点
  12. 用HTML简单制作一个网页
  13. APK的几种安装方式
  14. 【机器学习】10:朴素贝叶斯做文本分类
  15. 全民一起玩Python 之 基础篇视频教程
  16. Using 1.7 requires compiling with Android 4.4 (KitKat); currently using API XX
  17. 老男孩之猛龙过江电影高清版迅雷下载
  18. 计算机十大算法应用 知乎,2019 智源·知乎看山杯算法大赛收官:7 支团队脱颖而出,单人队荣摘桂冠!...
  19. matlab 用古典雅可比方法求矩阵特征根 (仅使用基础函数)
  20. LUNA2016数据集

热门文章

  1. 2020域名网络安全行业热门事件盘点
  2. 【整理】1、C网和G网双模手机是否可同时支持C网和G网上网?2、上网的同时是否可以打电话?3、呼叫等待和呼叫保持的概念与“双通”手机
  3. 种业创新深圳品种 国稻种芯·中国水稻节:广东海水稻获成果
  4. 服务器运维辐射,【服务器辐射】服务器辐射大吗_服务器防辐射_服务器辐射距离_佰佰安全网...
  5. Python format 使用实例
  6. ShowWindow
  7. Taylor Swift为何听不到搜不到她的歌曲了?让我告诉你个好法子
  8. 怎么修改ftp服务器被动端口,ftp服务器改为被动模式
  9. 【笔记总结】高中英语——其二:名词性从句
  10. Servlet判断用户是否登陆过、异地登录和登出功能