flutter仿微信底部圖標(biāo)漸變功能的實(shí)現(xiàn)代碼
先給大家展示下效果圖,感覺(jué)不錯(cuò)請(qǐng)參考實(shí)例代碼。

實(shí)現(xiàn)思路
在flutter中,如果想實(shí)現(xiàn)上面的頁(yè)面切換效果,必然會(huì)想到pageView。pageView的controller可以監(jiān)聽(tīng)到pageView的滾動(dòng)事件,也可以獲取pageView滾動(dòng)的位置,所以我們?cè)跐L動(dòng)事件中根據(jù)位置去改變對(duì)應(yīng)的圖標(biāo)顏色就可以實(shí)現(xiàn)了。
改變圖標(biāo)顏色
圖標(biāo)是從微信中提取出來(lái)的,都是webp格式的圖片。要改變圖片顏色可以使用ImageIcon這個(gè)組件。
ImageIcon會(huì)把一張圖片變成單色圖片,所以只要圖片沒(méi)有多色的要求,就可以用這個(gè)組件。
既然能改變顏色了,我們也需要知道pageView滾動(dòng)的時(shí)候究竟要改什么顏色。從一個(gè)頁(yè)面滾動(dòng)到另一個(gè)頁(yè)面的過(guò)程中,顏色都是線性漸變的,要獲取這個(gè)過(guò)程中的顏色可以使用flutter的Color類提供的lerp方法,作用是獲取兩種顏色之間的線性差值

里面有3個(gè)參數(shù),a和b都是顏色,t是夾在0到1之間的,當(dāng)t為0時(shí)返回a,當(dāng)t為1時(shí)返回b
也就是在滾動(dòng)事件中,計(jì)算出 t ,根據(jù) t 改變圖標(biāo)顏色就可以實(shí)現(xiàn)上面的效果了。
pageController.addListener(() {
int currentPage = pageController.page.toInt();
//當(dāng)前頁(yè)面的page是double類型的, 把它減去當(dāng)前頁(yè)面的int類型就可以得出當(dāng)前頁(yè)面到下一個(gè)頁(yè)面的偏移量了
double t = pageController.page - currentPage;
//根據(jù)上一次的頁(yè)面位置獲得方向
if (lastPage <= pageController.page) {
//向右滑動(dòng)時(shí)currentPage是當(dāng)前頁(yè)
//從當(dāng)前頁(yè)過(guò)渡到下一頁(yè)
streamController.sink.add(StreamModel(
timeline: t, index: currentPage, gotoIndex: currentPage + 1));
} else {
//向左滑動(dòng)時(shí)currentPage是上一頁(yè)
//從當(dāng)前頁(yè)過(guò)渡到上一頁(yè)
streamController.sink.add(StreamModel(
timeline: t, index: currentPage + 1, gotoIndex: currentPage));
}
lastPage = pageController.page;
});
上面代碼中currentPage的值舉個(gè)例子:當(dāng)前page是1,要滑動(dòng)到2,那么它的值是1.11...1.21...這樣一直到2,所以在這個(gè)過(guò)程中currentPage是當(dāng)前頁(yè)。如果當(dāng)前page是4,要滑動(dòng)到3的時(shí)候,它的值是3.99...3.81...這樣一直到3,在這個(gè)過(guò)程中currentPage就是上一頁(yè)了。
t 的計(jì)算就更簡(jiǎn)單了,1.11-1=0.11,3.99-3=0.99 .....
管理圖標(biāo)顏色
因?yàn)槲沂怯昧俗詭У牡撞繉?dǎo)航BottomNavigationBar,在pageController的滾動(dòng)事件中改變圖標(biāo)顏色太麻煩了,所以用了Stream來(lái)管理圖標(biāo)的狀態(tài)。使用Stream創(chuàng)建一個(gè)多訂閱的管道,讓所有圖標(biāo)都訂閱它,然后在滑動(dòng)事件中把需要的數(shù)據(jù)都發(fā)送給所有圖標(biāo)。
需要的數(shù)據(jù):
class StreamModel {
const StreamModel({this.timeline, this.index, this.gotoIndex});
final double timeline;
final int index;
final int gotoIndex;
}
圖標(biāo)組件
構(gòu)造方法設(shè)置一個(gè)index,方便判斷圖標(biāo)是哪個(gè)。
使用StreamBuilder包住要改變顏色的組件,并且綁定從構(gòu)造函數(shù)設(shè)置的StreamController。
在StreamBuilder中根據(jù)pageView滾動(dòng)事件傳進(jìn)來(lái)的參數(shù)控制圖標(biāo)顏色。
class BottomNavIcon extends StatelessWidget {
final StreamController<StreamModel> streamController;
final int index;
final String img;
final String title;
final double fontSize;
Color _color;
Color _activeColor;
final bool isActive;
BottomNavIcon(this.title, this.img, this.index,
{@required this.streamController,
this.isActive = false,
this.fontSize = 18.0,
Color color = Colors.grey,
Color activeColor = Colors.blue}) {
_color = isActive ? activeColor : color;
_activeColor = isActive ? color : activeColor;
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: streamController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
final StreamModel data = snapshot.data;
double t = 0.0;
if (data != null) {
//開(kāi)始的index
if (data.index == index) {
t = data.index > data.gotoIndex
? data.timeline
: 1.0 - data.timeline;
print("this${data.index}:${t}");
}
//結(jié)束的index
if (data.gotoIndex == index) {
t = data.index > data.gotoIndex
? 1.0 - data.timeline //開(kāi)始的index大于結(jié)束的index方向向左
: data.timeline; //小于方向向右
//過(guò)渡到的圖標(biāo)顏色的插值超過(guò)0.6時(shí), 個(gè)人感覺(jué)當(dāng)前顏色和結(jié)束的哪個(gè)顏色相差太多,
//所以超過(guò)0.6時(shí)恢復(fù)默認(rèn)顏色
t = t >= 0.6 ? 1 : t;
print("goto${data.gotoIndex}:${t}");
}
}
if (t > 0.0 && t < 1.0) {
//color.lerp 獲取兩種顏色之間的線性插值
return Column(
children: <Widget>[
ImageIcon(AssetImage(this.img),
color: Color.lerp(_color, _activeColor, t)),
Text(title,
style: TextStyle(
fontSize: fontSize,
color: Color.lerp(_color, _activeColor, t))),
],
);
}
return Column(
children: <Widget>[
ImageIcon(AssetImage(this.img),
color:
Color.fromRGBO(_color.red, _color.green, _color.blue, 1)),
Text(title,
style: TextStyle(
fontSize: fontSize,
color: Color.fromRGBO(
_color.red, _color.green, _color.blue, 1))),
],
);
});
}
}
圖標(biāo)的顏色都是當(dāng)前的(index == data.index)漸漸變淺,要滾動(dòng)到(index==data.gotoIndex)的圖標(biāo)顏色漸深
創(chuàng)建多訂閱的管道(Stream)
final StreamController<StreamModel> streamController =
StreamController.broadcast();
加載圖標(biāo)
for (int i = 0; i < pages.length; i++) {
TabBarModel model = pages[i];
bars.add(
BottomNavigationBarItem(
icon: BottomNavIcon(
model.title,
'assets/images/tabbar_' + model.icon + '_c.webp',
i,
streamController: streamController,
),
activeIcon: BottomNavIcon(
model.title,
'assets/images/tabbar_' + model.icon + '_s.webp',
i,
streamController: streamController,
isActive: true,
),
title: Center(),
),
);
}
上面代碼的title為Center的原因是已經(jīng)在圖標(biāo)組件中創(chuàng)建了一個(gè)顯示標(biāo)題的組件,方便一起設(shè)置顏色。這里就不需要了,但是它的title不允許為null,所以隨便給它一個(gè)高寬都是0的組件
結(jié)語(yǔ)
其實(shí)這個(gè)效果和微信的不是一模一樣,微信的應(yīng)該是選中圖標(biāo)疊加到默認(rèn)圖標(biāo)上面。默認(rèn)圖標(biāo)顏色線性漸變,選中圖標(biāo)透明度漸變。flutter實(shí)現(xiàn)這個(gè)用自帶的BottomNavigationBar估計(jì)不行,可能需要自定義一個(gè)底部導(dǎo)航。
第一次寫(xiě)技術(shù)文章,感覺(jué)有點(diǎn)亂,所以貼下完整的代碼地址:
gist: gist.github.com/327100395/9 …
dartPad: dartpad.dev/9dee2497a99…(圖片讀的是本地的,在dartPad中路徑錯(cuò)誤,所以圖片不顯示)
相關(guān)文章
flutter 動(dòng)手?jǐn)]一個(gè)城市選擇citypicker功能
在一些項(xiàng)目開(kāi)發(fā)中經(jīng)常會(huì)用到城市選擇器功能,今天小編動(dòng)手?jǐn)]一個(gè)基于flutter 城市選擇citypicker功能,具體實(shí)現(xiàn)過(guò)程跟隨小編一起看看吧2021-08-08
Handler實(shí)現(xiàn)倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了Handler實(shí)現(xiàn)倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
Android 自定義彈性ListView控件實(shí)例代碼(三種方法)
關(guān)于在Android中實(shí)現(xiàn)ListView的彈性效果,有很多不同的方法,網(wǎng)上一搜,也有很多,下面貼出在項(xiàng)目中經(jīng)常用到的兩種實(shí)現(xiàn)ListView彈性效果的方法(基本上拿來(lái)就可以用),需要的朋友參考下本段代碼2016-01-01
Android利用LitePal操作數(shù)據(jù)庫(kù)存取圖片
這篇文章主要為大家詳細(xì)介紹了Android利用LitePal操作數(shù)據(jù)庫(kù)存取圖片的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
android 使用OkHttp上傳多張圖片的實(shí)現(xiàn)代碼
這篇文章主要介紹了android 使用OkHttp上傳多張圖片的相關(guān)資料,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-07-07
Android Flutter實(shí)現(xiàn)五種酷炫文字動(dòng)畫(huà)效果詳解
animated_text_kit這一動(dòng)畫(huà)庫(kù)有多種文字動(dòng)畫(huà)效果,文中將利用它實(shí)現(xiàn)五種酷炫的文字動(dòng)畫(huà):波浪涌動(dòng)效果、波浪線跳動(dòng)文字組、彩虹動(dòng)效、滾動(dòng)廣告牌效果和打字效果,需要的可以參考一下2022-03-03
Android中利用App實(shí)現(xiàn)消息推送機(jī)制的代碼
Android仿支付寶自定義密碼輸入框及安全鍵盤(pán)(密碼鍵盤(pán))

