本节继续为大家完善通讯录页面
1.增加标签头部
2.增加索引栏目
.3.改进通讯录按索引显示,并且去掉重复的标签头
4.改进标签索引Bar,增加回调,
5.listview根据回调来跳转到相应的标签栏
6.为标签栏添加可见视图,滑动或者点击的时候显示,结束拖动或者点击的时候消失
1.增加标签头部
修改Cell,给每一个cell添加一个默认的标签头部,先不去判断第二个相同的不显示
Widget build(BuildContext context) {
return Container(
//height: 85,
color: Colors.white,
child: Column(
children: [
//分组标签(有数据显示,无数据不显示)
(groupTitle != null) ? Container(
height: 30,
color: wechatThemeColor,
width: ScreenWidth(context),
padding: EdgeInsets.only(left: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('A',style: TextStyle(fontSize: 16),),
],
),
) : Container(),
Container(
height: 54.5,
child: Row(
children: [
Container(
margin: EdgeInsets.only(left: 10),
width: 35,
height: 35,
child: (imageUrl != null) ? NetworkImage(imageUrl) : Image.asset(locationImageUrl),
),
Container(
margin: EdgeInsets.only(left: 10),
child: Text(name,style: TextStyle(fontSize: 16,color: Colors.black),),
),],),),//cell内容
//分割线
Container( height: 0.5,color: Color.fromRGBO(220, 220, 220, 1), child: Row(children: [Container(width: 50,color: Colors.white,),],),),
],
));}
}
改变数据源
final List<Friends> _friendsDatas = [
Friends(locationImageUrl: 'images/ice.png',name: '张三',indexChar: 'Z'),
Friends(locationImageUrl: 'images/ice.png',name: '李四',indexChar: 'L'),
Friends(locationImageUrl: 'images/ice.png',name: '王二',indexChar: 'W'),
Friends(locationImageUrl: 'images/ice.png',name: '麻子',indexChar: 'M'),
Friends(locationImageUrl: 'images/ice.png',name: '安娜',indexChar: 'A'),
Friends(locationImageUrl: 'images/ice.png',name: '黄飞鸿',indexChar: 'H'),
Friends(locationImageUrl: 'images/ice.png',name: '霍元甲',indexChar: 'H'),
Friends(locationImageUrl: 'images/ice.png',name: '周杰伦',indexChar: 'Z'),
Friends(locationImageUrl: 'images/ice.png',name: '林俊杰',indexChar: 'L'),
Friends(locationImageUrl: 'images/ice.png',name: '王力宏',indexChar: 'W'),
Friends(locationImageUrl: 'images/ice.png',name: '欧豪',indexChar: 'O'),
Friends(locationImageUrl: 'images/ice.png',name: '大张伟',indexChar: 'D'),
];
run
2.增加索引栏目
右侧的标签分组栏为悬浮视图,所以我们改造下
Widget build(BuildContext context) {
return Scaffold(
appBar:...省略,
body:Stack(
children: [
ListView.builder(itemBuilder: _cellForRow,itemCount: _headDatas.length + _friendsDatas.length,),
Positioned(
child: IndexCharBar(),
right: 0,
top: ScreenHeight(context)/8,
height: ScreenHeight(context)/2,
width: 40,
),
],
)
);
}
新建friends_indexchar_bar.dart
import 'package:flutter/material.dart';
class IndexCharBar extends StatefulWidget {
@override
_IndexCharBarState createState() => _IndexCharBarState();
}
class _IndexCharBarState extends State<IndexCharBar> {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
);
}
}
run
继续改进:为indexBar创建索引,根据传进来的通讯录文字来创建
class IndexCharBar extends StatefulWidget {
final List<Friends> friendsDatas;
IndexCharBar({
this.friendsDatas,//根据传进来的数据来创建索引
});
@override
_IndexCharBarState createState() => _IndexCharBarState();
}
数据处理(处理数据、创建索引视图)
class _IndexCharBarState extends State<IndexCharBar> {
final List<Widget> widgets = [];
final List<String> chars = [];
//处理数据
void handleFriendsDatas(){
//先排序,按照字母的先后顺序来排序(A,B,C,D......)
List<Friends> newDatas = [];
newDatas.addAll(widget.friendsDatas);
newDatas.sort(
(Friends a,Friends b){
return a.indexChar.compareTo(b.indexChar);
}
);
//去重,得到Chars
for(int i = 0;i < newDatas.length ; i++){
Friends friend = newDatas[i];
if(!chars.contains(friend.indexChar)){
chars.add(friend.indexChar);
}
}
}
void initWidgets(){
for(int i = 0 ; i < chars.length ; i++)
widgets.add(
Expanded(
child: Container(
width: 40,
alignment: Alignment.center,
child: Text(chars[i],style: TextStyle(color: _textColor)),
),
),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
//处理数据
handleFriendsDatas();
initWidgets();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: widgets,
),
);
}
}
run
3.改进通讯录按索引显示,并且去掉重复的标签头
为通讯录数据进行排序
void handleDatas(){
_friendsDatas.sort((Friends a,Friends b){
return a.indexChar.compareTo(b.indexChar);
}
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
//处理数据
handleDatas();
}
run
多增加点假数据
void handleDatas(){
final List<Friends> tmpDatas = [];
tmpDatas..addAll(_friendsDatas)..addAll(_friendsDatas);
_friendsDatas.addAll(tmpDatas);
_friendsDatas.sort((Friends a,Friends b){
return a.indexChar.compareTo(b.indexChar);
}
);
}
run
,去掉重复头
Widget _cellForRow(BuildContext context , int index){
if(index < _headDatas.length){
return FriendsCell(locationImageUrl: _headDatas[index].locationImageUrl,name: _headDatas[index].name,);
}else{
//如果当前cell的indexChar与前一个相同的话,则不显示,去掉重复的标签头
int currentIndex = index - _headDatas.length;
int fontIndex = currentIndex - 1;
if(currentIndex > 0 ){
if(_friendsDatas[currentIndex].indexChar == _friendsDatas[fontIndex].indexChar){
return FriendsCell(locationImageUrl: _friendsDatas[index-_headDatas.length].locationImageUrl,name: _friendsDatas[index-_headDatas.length].name);
}
}
return FriendsCell(locationImageUrl: _friendsDatas[index-_headDatas.length].locationImageUrl,name: _friendsDatas[index-_headDatas.length].name,groupTitle: _friendsDatas[index-_headDatas.length].indexChar,);
}
}
run
4.改进标签索引Bar,增加回调,点击标签栏上面的索引,或者滑动,都能知道点击或者滑到了哪个索引。
添加点击效果,点击的时候呈现背景色,且文字颜色为白色,还原时,背景色消失,文字颜色变为黑色
用GestureDetector将Bar包装起来,并且添加点击事件
child: GestureDetector(
onVerticalDragDown:(DragDownDetails details){
setState(() {
_bgColor = Color.fromRGBO(1, 1, 1, 0.5);
_textColor = Colors.white;
});
} ,
onVerticalDragEnd:(DragEndDetails details){
setState(() {
_bgColor = Color.fromRGBO(1, 1, 1, 0);
_textColor = Colors.black;
});
} ,
onVerticalDragUpdate: (DragUpdateDetails details){
setState(() {
_bgColor = Color.fromRGBO(1, 1, 1, 0.5);
_textColor = Colors.white;
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: widgets,
),
),
计算点击或者滑动的索引
onVerticalDragUpdate: (DragUpdateDetails details){
//全局转标转化,转化局部左边
RenderBox box = context.findRenderObject();
var local = box.globalToLocal(details.globalPosition);
//print('${local.dy}');
//获取每一个索引的高度
double charhight = ScreenHeight(context) / 2 / chars.length;
int index = 0 ;
if(local.dy < 0){//滑出最顶部了
}else if(local.dy > ScreenHeight(context) / 2){//滑出最底部了
index = chars.length - 1;
}else{
index = local.dy ~/ charhight;
}
print('当前点击的是${chars[index]}');
setState(() {
_bgColor = Color.fromRGBO(1, 1, 1, 0.5);
_textColor = Colors.white;
});
},
其他地方也要用到,所以方法抛出来
String findIndexChar(BuildContext context , Offset point){
//全局转标转化,转化局部坐标
RenderBox box = context.findRenderObject();
var local = box.globalToLocal(point);
//print('${local.dy}');
//获取每一个索引的高度
double charhight = ScreenHeight(context) / 2 / chars.length;
int index = 0 ;
if(local.dy < 0){//滑出最顶部了
}else if(local.dy > ScreenHeight(context) / 2){//滑出最底部了
index = chars.length - 1;
}else{
index = local.dy ~/ charhight;
}
return chars[index];
}
抛出回调,告知listview,我选择了什么索引
修改构造方法,添加回调参数
class IndexCharBar extends StatefulWidget {
final List<Friends> friendsDatas;
var selectCharBlock = (String indexChar){};
//构造方法
IndexCharBar({
this.friendsDatas,//根据传进来的数据来创建索引
this.selectCharBlock,
});
@override
_IndexCharBarState createState() => _IndexCharBarState();
}
传值
onVerticalDragDown:(DragDownDetails details){
String slectedChar = findIndexChar(context ,details.globalPosition);
if(widget.selectCharBlock != null){
widget.selectCharBlock(slectedChar);
}
setState(() {
_bgColor = Color.fromRGBO(1, 1, 1, 0.5);
_textColor = Colors.white;
});
} ,
onVerticalDragUpdate: (DragUpdateDetails details){
String slectedChar = findIndexChar(context ,details.globalPosition);
if(widget.selectCharBlock != null){
widget.selectCharBlock(slectedChar);
}
setState(() {
_bgColor = Color.fromRGBO(1, 1, 1, 0.5);
_textColor = Colors.white;
});
},
run
5.最后一步:listview根据回调来跳转到相应的标签栏
分析:首先我们先计算滑动到每个标签索引所需要滑动的距离
核心代码:计算每个索引的内容高度
//listview的滑动器
final ScrollController _scrollController = ScrollController();
//记录每个索引内容的高度
final Map _charIndexMap = Map();
void handleDatas(){
final List<Friends> tmpDatas = [];
tmpDatas..addAll(_friendsDatas)..addAll(_friendsDatas);
_friendsDatas.addAll(tmpDatas);
_friendsDatas.sort((Friends a,Friends b){
return a.indexChar.compareTo(b.indexChar);
}
);
//头部距离
double headerheight = 55.0 * _headDatas.length;
double tmpHeight = headerheight;
//计算每个索引所占的高度
for (int i = 0 ; i < _friendsDatas.length ; i++){
Friends friend = _friendsDatas[i];
if(!_charIndexMap.containsKey(friend.indexChar)){//遇到了新的索引,则把前面的相加起来
if(i > 0){//新出来的要加上头部
tmpHeight += 30;//加上头部
}
_charIndexMap[friend.indexChar] = tmpHeight;
tmpHeight += 55.0;//加上一个cell的高度
}else{
tmpHeight += 55.0;
}
}
print('计算出来的map是$_charIndexMap');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: ...省略,
body:Stack(
children: [
ListView.builder(itemBuilder: _cellForRow,itemCount: _headDatas.length + _friendsDatas.length,controller: _scrollController,),
Positioned(
child: IndexCharBar(friendsDatas: _friendsDatas,selectCharBlock: (String idx){
double offset = _charIndexMap[idx];
print('传过来的值是${idx}+将要滑动到${offset}距离');
_scrollController.animateTo(offset, duration:Duration(milliseconds:200) , curve: Curves.easeIn);
},),
right: 0,
top: ScreenHeight(context)/8,
height: ScreenHeight(context)/2,
//bottom: ScreenWidth(context)/4,
width: 40,
),
],
)
);
}
优化:当要滑动的offset大于listview内容的总高度,则滑到最底部即可
待补充....
优化:为标签栏添加可见视图,滑动或者点击的时候显示,结束拖动或者点击的时候消失
优化方法:
Map findIndexChar(BuildContext context , Offset point){
//全局转标转化,转化局部坐标
RenderBox box = context.findRenderObject();
var local = box.globalToLocal(point);
//print('${local.dy}');
//获取每一个索引的高度
double charhight = ScreenHeight(context) / 2 / chars.length;
int index = 0 ;
if(local.dy < 0){//滑出最顶部了
}else if(local.dy > ScreenHeight(context) / 2){//滑出最底部了
index = chars.length - 1;
}else{
index = local.dy ~/ charhight;
}
Map map = Map();
map['index'] = index;
map['char'] = chars[index];
return map;
}
GestureDetector(
onVerticalDragDown:(DragDownDetails details){
String slectedChar = findIndexChar(context ,details.globalPosition)['char'];
if(widget.selectCharBlock != null){
widget.selectCharBlock(slectedChar);
}
setState(() {
_selectIndex = findIndexChar(context ,details.globalPosition)['index'];
});
} ,
onVerticalDragUpdate: (DragUpdateDetails details){
String slectedChar = findIndexChar(context ,details.globalPosition)['char'];
if(widget.selectCharBlock != null){
widget.selectCharBlock(slectedChar);
}
setState(() {
_selectIndex = findIndexChar(context ,details.globalPosition)['index'];
});
},
onVerticalDragEnd:(DragEndDetails details){
setState(() {
_selectIndex = -999;
});
} ,
double showOffsetY(int index){
double charhight = ScreenHeight(context) / 2 / chars.length;
return charhight * index;
}
image.png
网友评论