概述
鄙人于闲暇之日,自学Flutter已有两月之久,古人曰:百闻不如一见,百见不如一试,特此利用生平之所学,实战微信以项目。Flutter,学语法之轻易,用组件之简单,源码开源,插件丰富。然一份代码,却可完美运行于iOS和Android之上,其运行流畅,且效果杠杠,岂不拍案叫绝,牛B轰轰~。
如有iOS、Android、Web开发之经验,联想之前之所学,类比之前之所用,除写法不同,但语法通用,若多加练习,定能快速上手,耳熟蓝翔,不多逼逼,推荐以下之文档。
此文作微信通讯录以文章,虽功能看似简单,但内含技术丰富,且功能十分有趣。作为初学Flutter,拿其小试牛刀,必将初有成效。于Flutter而言, 鄙人也算是初生牛犊不怕虎,并非是天神下凡一锤五。当然,笔者必将知无不言、言无不尽,梳理实战过程之问题,总结解决问题之方案,让尔等知其然,知其所以然。望能抛玉引砖,摆渡众生,如有纰漏,还望斧正。
效果图
列表
索引
侧滑
contacts_page_0.png
contacts_page_1.png
contacts_page_2.png
列表
一、功能分析
搭建通讯录之列表,其知识点涵盖A-Z 索引Bar、悬停效果view、自定义Header、索引联动 、汉字转拼音,若想实现前面之功能,这里推荐以下之插件,好风凭借力,送我上青云。
azlistview 实现A-Z 索引Bar、悬停效果view、自定义Header、索引联动
lpinyin 实现汉字转拼音
关于具体其使用,还请下载其Demo,运行于电脑之上,查看其运行效果,在此就不多逼逼。
二、数据配置
// 获取联系人列表
Future fetchContacts() async {
// 先清除掉数据
_contactsList.clear();
_contactsMap.clear();
// 获取用户信息列表
final jsonStr =
await rootBundle.loadString(Constant.mockData + 'contacts.json');
// contactsJson
final List contactsJson = json.decode(jsonStr);
// 遍历
contactsJson.forEach((json) {
final User user = User.fromJson(json);
_contactsList.add(user);
_contactsMap[user.idstr] = user;
});
for (int i = 0, length = _contactsList.length; i < length; i++) {
String pinyin = PinyinHelper.getPinyinE(_contactsList[i].screenName);
String tag = pinyin.substring(0, 1).toUpperCase();
_contactsList[i].screenNamePinyin = pinyin;
if (RegExp("[A-Z]").hasMatch(tag)) {
_contactsList[i].tagIndex = tag;
} else {
_contactsList[i].tagIndex = "#";
}
}
// 根据A-Z排序
SuspensionUtil.sortListBySuspensionTag(_contactsList);
// 返回数据
return _contactsList;
}
三、UI搭建
由azlistview组件提供的API或Property可知,需要提供以下之部件(Widget):
// 列表中某一个 item 部件
itemBuilder: (context, model) => _buildListItem(model),
// 顶部悬浮的Widget
suspensionWidget: _buildSusWidget(_suspensionTag, isFloat: true),
// 自定义header
header: AzListViewHeader(
// - [特殊字符](https://blog.csdn.net/cfxy666/article/details/87609526)
// - [特殊字符](http://www.fhdq.net/)
tag: "♀",
height: 5 * _itemHeight,
builder: (context) {
return _buildHeader();
},
),
// IndexBar 这个可以不写,使用默认的IndexBar
indexBarBuilder: (context, tagList, onTouch){},
// 自定义 点击IndexBar 中的某个 tag,放大显示在屏幕中间的 hint,必须showIndexHint: true, 默认就是true
indexHintBuilder: (context, hint) {
return Container(
alignment: Alignment.center,
width: 80.0,
height: 80.0,
decoration: BoxDecoration(color: Color(0xFFC7C7CB), shape: BoxShape.circle),
child:Text(hint, style: TextStyle(color: Colors.white, fontSize: 30.0)),
);
},
具体UI搭建,这里不多赘述,还请移驾鄙人提供的Demo,翻阅查看其代码。这里笔者以自定义悬浮View和组头View为例,穿针引线,搭建符合要求之UI。效果图如下所示:
contacts_page_3.png
A:悬浮View
B:组头View
代码实现:
/// 构建悬浮部件
/// [susTag] 标签名称
/// [isFloat] 是否悬浮 默认是 false
Widget _buildSusWidget(String susTag, {bool isFloat = false}) {
return Container(
height: _suspensionHeight.toDouble(),
padding: EdgeInsets.only(left: ScreenUtil.getInstance().setWidth(51.0)),
decoration: BoxDecoration(
color: isFloat ? Colors.white : Style.pBackgroundColor,
border: isFloat
? Border(bottom: BorderSide(color: Color(0xFFE6E6E6), width: 0.5))
: null,
),
alignment: Alignment.centerLeft,
child: Text(
'$susTag',
softWrap: false,
style: TextStyle(
fontSize: ScreenUtil.getInstance().setSp(39.0),
color: isFloat ? Style.pTintColor : Color(0xff777777),
),
),
);
}
四、特别提醒
azlistview 中要求itemCell 、悬停View、自定义的Header、以及IndexBar中每个tag的高度必须是 int类型且不可动态修改。如涉及屏幕适配,还请向上(下)取整。
/// 悬浮view 高度 向上取整
int _suspensionHeight =
(ScreenUtil.getInstance().setHeight(99.0) as double).ceil();
/// 每个item 高度 向上取整