Flutter城市通讯录列表字母索引联动效果实现
前⾔
在像通讯录,联系⼈列表,城市选择列表等数据量⽐较多的长列表页⾯中,我们经常会留意到产品设计会在页⾯的右侧区域提供⼀个竖向的字母索引列表,供⽤户点击选择快速定位到长列表中的指定索引位置,以便于⽤户快速定位到⾃⼰要筛选的数据,从⽽提升⽤户体验,今天我们就以城市列表为例,来剖析⼀下,这样的体验效果如果⽤Flutter来实现
技术实现分析
页⾯城市列表布局采⽤ListView嵌套ListView,其中外层ListView负责显⽰当前城市的分组字母信息,内层Listview负责显⽰当前字母索引分组下的所有城市
右侧竖向字母索引列表采⽤ListView展⽰,当点击右侧字母索引的某⼀个时,动态的计算出,当前字母索引在城⾥列表的⾓标索引值,然后计算得出被点击的字符索引在城⾥列表⾥的⾼度值,通过ScrollController让城⾥列表⾃动定位到计算出的⾼度位置,从⽽实现,点击字母所以动态定位城市列表位置的联动效果
效果如下
核⼼代码
flutter开发app/**
* @desc 选择城市地区联动索引页
* @author xiedong
* @date 2020-04-30.
*/
class PhoneCountryCodePage extends StatefulWidget {
@override
@override
State<StatefulWidget>createState()=>PageState();
}
class PageState extends State<PhoneCountryCodePage>{
var GET_PHONE_COUNTRY_CODE_URL =
"raw.githubusercontent/xiedong11/flutter_app/master/static/phoneCode.json"; List<String> letters =[];
List<PhoneCountryCodeData> data;
ScrollController _scrollController =ScrollController();
int _currentIndex =0;
@override
void initState(){
super.initState();
getPhoneCodeDataList();
}
getPhoneCodeDataList() async {
var response = Instance().get(GET_PHONE_COUNTRY_CODE_URL); var resultEntity =new PhoneCountryCodeEntity.fromJson(json.decode(response));
de==200){
this.setState((){
data = resultEntity.data;
for(int i =0; i < data.length; i++){
letters.add(data[i].UpperCase());
}
});
}
}
@override
Widget build(BuildContext context){
return Scaffold(
appBar:AppBar(
title:Text("城市地区选择"),
centerTitle:true,
),
body:Stack(
children:<Widget>[
data == null || data.length ==0
?Text("")
:Padding(
padding: ly(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].UpperCase()),
ListView.builder(
itemBuilder:
(BuildContext context,int index2){
return Container(
height:46,
child:GestureDetector(
// behavior: anslucent,
child:Padding(
padding:
EdgeInsets.symmetric(vertical:10),
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: ly(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);
},
);
},
),
)
,
),
)
],
),
);
Json数据映射实体类
);
}
}
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),
),
);
}
}
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 ));
});
}
难点分析
⽂章开头当点击右侧字母索引的某⼀个时,动态的计算出当前字母索引在城⾥列表的⾓标索引值,然后计算得出被点击的字符索引在城⾥列表⾥的⾼度值,通过ScrollController 让城⾥列表⾃动定位到计算出的⾼度位置,从⽽实现,点击字母所以动态定位城市列表位置的联动效果。
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 ;
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论