
Flutter 工程版本兼容配置
我們這里主要完善索引條的功能及聊天列表數(shù)據(jù)的獲取,但是在這之前我們先講一下工程版本的配置問題。當(dāng)我們?cè)诠こ讨型先胍恍├系奈募a的時(shí)候可能會(huì)出現(xiàn)一些語(yǔ)法報(bào)錯(cuò)問題,可能的原因就是版本兼容的問題,我們可以在 pubspec.yaml 文件中配置 sdk 判斷的兼容范圍,然后點(diǎn)擊 Pub get。如果新建的工程建議是大于等于 2.12.0,因?yàn)?2.12.0 是一次大的更新,加入了空安全與值類型安全。

索引條功能完善

在上一篇中我們已經(jīng)可以獲取到點(diǎn)擊位置的索引字符,現(xiàn)在我們需要實(shí)現(xiàn)的功能是點(diǎn)擊索引字符滾動(dòng)到指定的位置,并且左邊添加指示器展示對(duì)應(yīng)的索引字符。下面我們把步驟進(jìn)行拆分介紹。
滾動(dòng)效果實(shí)現(xiàn)
索引條添加點(diǎn)擊回調(diào)方法
class IndexBar extends StatefulWidget {
final void Function(String str)? indexBarCallBack;
IndexBar({this.indexBarCallBack});
}
GestureDetector(
onVerticalDragUpdate: (DragUpdateDetails details){
final Function(String str) callBack = widget.indexBarCallBack as Function(String str);
int index = _getIndex(context, details.globalPosition);
callBack(INDEX_WORDS[index]);
},
// 索引條點(diǎn)擊
onVerticalDragDown: (DragDownDetails details){
final Function(String str) callBack = widget.indexBarCallBack as Function(String str);
int index = _getIndex(context, details.globalPosition);
callBack(INDEX_WORDS[index]);
},
)
IndexBar(indexBarCallBack: (String str) {
print(str);
}),//懸浮的索引條
因?yàn)樗饕龡l是單獨(dú)定義的類,所以如果想讓外部的列表滾動(dòng)的話首先需要有一個(gè)回調(diào)方法,回調(diào)到外部。我們?cè)?IndexBar 類中添加了一個(gè) indexBarCallBack 回調(diào)方法,在點(diǎn)擊索引條的時(shí)候調(diào)用回調(diào)方法。
遍歷初始化索引字符對(duì)應(yīng)的偏移量
class _FriendsPageState extends State<FriendsPage> {
final double _cellHeight = 54.5;
final double _groupHeight = 30.0;
final Map _groupOffsetMap = {
};
final List<Friends> _listDatas = [];
ScrollController? _scrollController;
@override
void initState() {
super.initState();
//創(chuàng)建數(shù)據(jù)
_listDatas..addAll(datas)..addAll(datas);
//按首字母順序排序
_listDatas.sort((Friends a, Friends b){
return a.indexLetter.compareTo(b.indexLetter);
});
var _groupOffset = _cellHeight * _headerData.length;
//循環(huán)計(jì)算,將每個(gè)組頭的位置放入到 _groupOffsetMap 字典中
for (int i = 0; i < _listDatas.length; i++) {
if (i < 1) {
//第一個(gè) cell,一定有頭
_groupOffsetMap.addAll({_listDatas[i].indexLetter : _groupOffset});
//保存完了再加_groupOffset
_groupOffset += _cellHeight + _groupHeight;
} else if (_listDatas[i].indexLetter == _listDatas[i - 1]) {
_groupOffset += _cellHeight;
} else {
_groupOffsetMap.addAll({_listDatas[i].indexLetter : _groupOffset});
//保存完了再加_groupOffset
_groupOffset += _cellHeight + _groupHeight;
}
}
}
當(dāng)外部獲取到索引字符的時(shí)候就需要滾動(dòng)到對(duì)應(yīng)的偏移位置,這里我們用一個(gè) _groupOffsetMap 來存儲(chǔ)每個(gè)索引字符對(duì)應(yīng)的偏移位置,我們通過循環(huán)遍歷對(duì) _groupOffsetMap 進(jìn)行添加數(shù)據(jù)。
滾動(dòng)到對(duì)應(yīng)位置
ScrollController? _scrollController;
@override
void initState() {
super.initState();
_scrollController = ScrollController();
}
child: ListView.builder(itemBuilder: _itemForRow, controller: _scrollController, itemCount: _headerData.length + _listDatas.length,)
if(_groupOffsetMap[str] != null) {
print(str);
_scrollController!.animateTo(_groupOffsetMap[str], duration: Duration(microseconds: 100), curve: Curves.elasticIn);
}
在源碼中我們可以看到有一個(gè) controller 屬性,可以用來控制列表的滾動(dòng)。我們定義一個(gè) _scrollController 屬性,在 initState 方法中進(jìn)行初始化,并且在 ListView 初始化的時(shí)候賦值給 controller 屬性,并且在回調(diào)方法中滾動(dòng)到對(duì)應(yīng)位置。duration 代表滾動(dòng)時(shí)長(zhǎng),curve 代表滾動(dòng)的動(dòng)畫效果,大家可以自己選擇動(dòng)畫類型。
添加左邊指示器

我們的指示器是添加在索引條上的,所以所以條的布局方式我們采用 Row 部件,并在 children 添加指示器部件跟原來的索引條部件,整體采用左右結(jié)構(gòu)。
Container(
alignment: Alignment(0, _indicatorY),
width: 100,
child: _indicatorHidden ? null : Stack(
alignment: Alignment(-0.2, 0),
children: [
Image(image: AssetImage('images/氣泡.png'), width: 60,),
Text(
_indicatorText,
style: TextStyle(
fontSize: 35, color: Colors.white,
),
)
],
),
)
指示器部件我們采用的是一張圖片,并在上面加上文字。這里我們定義了三個(gè)變量, _indicatorY 代表豎軸方向的偏移比例系數(shù),_indicatorHidden 代表是否顯示,_indicatorText 代表顯示的文字,也就是索引字符。
GestureDetector(
onVerticalDragUpdate: (DragUpdateDetails details){
int index = _getIndex(context, details.globalPosition);
callBack(INDEX_WORDS[index]);
setState(() {
_indicatorY = 2.2 / INDEX_WORDS.length * index - 1.1;
_indicatorText = INDEX_WORDS[index];
_indicatorHidden = false;
});
},
// 索引條點(diǎn)擊
onVerticalDragDown: (DragDownDetails details){
int index = _getIndex(context, details.globalPosition);
callBack(INDEX_WORDS[index]);
setState(() {
_indicatorY = 2.2 / INDEX_WORDS.length * index - 1.1;
_indicatorText = INDEX_WORDS[index];
_indicatorHidden = false;
});
},
// 索引條點(diǎn)擊取消
onVerticalDragEnd: (DragEndDetails details){
setState(() {
_indicatorHidden = true;
});
},
)
我們?cè)邳c(diǎn)擊方法與取消方法中分別設(shè)置這三方變量的值,然后調(diào)用 setState 方法,刷新指示器的位置,顯示的文字及是否隱藏狀態(tài)。
聊天列表數(shù)據(jù)的配置及接口請(qǐng)求
聊天列表數(shù)據(jù)配置

這里給大家推薦一個(gè) api 倉(cāng)庫(kù) RAP,大家可以自己注冊(cè)一個(gè)賬號(hào),并配置接口名稱及返回的數(shù)據(jù)。這里我們定義了聊天列表的接口 /api/chat/list,并且配置了數(shù)據(jù),這里我配置的比較簡(jiǎn)單,就三個(gè)屬性 imageUrl、name、name。這里初始值配置的時(shí)候 @cname 代表隨機(jī)生成中文名稱,@cparagraph 代表隨機(jī)生成 message 內(nèi)容。頭像 imageUrl 我們還要借助一個(gè)網(wǎng)站 randomuser 可以隨機(jī)生成假的用戶信息數(shù)據(jù)。

這里我們只用到頭像鏈接地址就可以了,但是這里有一個(gè)規(guī)律,例如我們將頭像鏈接中的 75 改為別的數(shù)字,就會(huì)顯示別的頭像圖片,所以接口文檔中頭像鏈接我們是這樣配置的 ,https://randomuser.me/api/portraits/women/@natural(20,100).jpg,其中 @natural(20,100) 代表隨機(jī)生成 20 到 100 之間的數(shù)據(jù)。
網(wǎng)絡(luò)常用三方庫(kù)


這里給大家推薦一個(gè)網(wǎng)站 pub.dev,作用類似 github,我們可以在上面搜索一下開源的 flutter 三方庫(kù)。網(wǎng)絡(luò)請(qǐng)求的話目前使用比較多的三方庫(kù)是 http 跟 dio。這里我們先使用 http 進(jìn)行網(wǎng)絡(luò)請(qǐng)求演示,這個(gè)三方庫(kù)是 flutter 官方發(fā)布的。
三方庫(kù)配置


在三方庫(kù)詳情頁(yè)點(diǎn)擊復(fù)制按鈕,在 pubspec.yaml 文件中進(jìn)行配置,點(diǎn)擊 Pub get 按鈕進(jìn)行集成。
網(wǎng)絡(luò)請(qǐng)求示例
@override
void initState() {
super.initState();
//獲取網(wǎng)絡(luò)數(shù)據(jù)
_getDatas();
}
_getDatas() async {
final url = Uri.parse('http://rap2api.taobao.org/app/mock/294394/api/chat/list');
final response = await http.get(url);
print(response);
}
這里需要注意的是需要先導(dǎo)入頭文件 import 'package:http/http.dart' as http;,async 代表異步執(zhí)行,await 代表等待請(qǐng)求完成才往下執(zhí)行。