Flutter 仿抖音效果 (一) 全屏點(diǎn)愛星
Flutter 仿抖音效果 (二) 界面布局
[Flutter 仿抖音效果 (三) 視頻播放列表] (http://www.reibang.com/p/d0f44241d80f)
項(xiàng)目地址:https://github.com/CZXBigBrother/flutter_TikTok 持續(xù)效果更新
實(shí)現(xiàn)界面布局效果需要解決的問題
- 1.整體布局實(shí)現(xiàn)
-
2.底部歌曲左右移動(dòng)效果
Snip20191231_10.png
布局的實(shí)現(xiàn)
1.基本的布局是簡(jiǎn)單的,外層通過Stack作為根
2.左邊點(diǎn)贊的控件組通過Align進(jìn)行統(tǒng)一布局
3.頂部控件組通過Positioned進(jìn)行布局,設(shè)置頂部距離,其實(shí)也可以用align,我們多使用幾種來習(xí)慣flutter的布局
4.底部同樣使用Positioned,設(shè)置底部距離
5.子頁(yè)面的左右滑動(dòng)使用PageView,一開始我們要從推薦開始左滑到關(guān)注,可以使用reverse屬性,不需要更多額外的操作
細(xì)節(jié)實(shí)現(xiàn)
1.pageController監(jiān)聽
PageController pageController = new PageController(keepPage: false);
pageController.addListener(() {
// print(pageController.page);
if (pageController.page == 1) {
this.stream.sink.add(1);
} else if (pageController.page == 0) {
this.stream.sink.add(0);
}
});
刷新頂部的下劃線時(shí),我們一樣使用StreamController刷新,這樣效率比setstate高很多
StreamController<int> stream = new StreamController.broadcast();
StreamBuilder<int>(
initialData: 0,
builder: (context, snapshot) {
return Text(
"推薦",
style: TextStyle(
color: Colors.white,
decoration: snapshot.data == 0
? TextDecoration.underline
: TextDecoration.none,
),
);
},
stream: this.stream.stream,
)
2.歌曲名走馬燈效果
這個(gè)效果看起來挺麻煩的其實(shí)實(shí)現(xiàn)起來超級(jí)的簡(jiǎn)單用最普通的ListView就能快速的實(shí)現(xiàn)
首頁(yè)listview里面套入的是最簡(jiǎn)單的container+text
Container(
// color: Colors.red,
// alignment: Alignment.centerLeft,
child: Text(
"三根皮帶歌曲,嘩啦啦啦啦啦啦啦啦啦啦啦",
maxLines: 1,
style: TextStyle(
color: Colors.white,
),
textAlign: TextAlign.left,
),
)
listview添加一個(gè)ScrollController做為滑動(dòng)的控制
ScrollController scroController = new ScrollController();
使用一個(gè)定時(shí)器,把listview滑到最大的位置之后,在滑回去
先通過scroController.position.maxScrollExtent獲取最大位置,
然后通過scroController.animateTo進(jìn)行滑動(dòng),因?yàn)槲以O(shè)置一次循環(huán)的時(shí)間是3000毫秒,所以滑過去和滑回來的時(shí)間各占一般 new Duration(milliseconds: (time * 0.5).toInt()),還有就是歌名沒有大于最大寬度時(shí)候其實(shí)我們不需要進(jìn)行滑動(dòng),所以判斷maxScrollExtent是否大于0來確定是否進(jìn)行滑動(dòng)動(dòng)畫
Timer timer;
void startTimer() {
int time = 3000;
timer = Timer.periodic(new Duration(milliseconds: time), (timer) {
if (scroController.positions.isNotEmpty == false) {
print('界面被銷毀');
return;
}
double maxScrollExtent = scroController.position.maxScrollExtent;
// print(maxScrollExtent);
// double pixels = scroController.position.pixels;
if (maxScrollExtent > 0) {
scroController.animateTo(maxScrollExtent,
duration: new Duration(milliseconds: (time * 0.5).toInt()),
curve: Curves.linear);
Future.delayed(Duration(milliseconds: (time * 0.5).toInt()), () {
if (scroController.positions.isNotEmpty == true) {
scroController.animateTo(0,
duration: new Duration(milliseconds: (time * 0.5).toInt()),
curve: Curves.linear);
}
});
} else {
print('不需要移動(dòng)');
timer.cancel();
}
});
}