本系列可能會伴隨你們很長時間,這裏我會從0開始搭建一個「網易雲音樂」的APP出來。git
下面是該APP 功能的思惟導圖:github
前期回顧:微信
每日推薦 | 推薦歌單 |
---|---|
本篇爲第三篇,在這裏咱們會搭建每日推薦、推薦歌單。網絡
首先仍是再來看一下「每日推薦」的UI效果:less
看到這個效果,有經驗的同窗可能直接就會喊出:CustomScrollView
!!ide
沒錯,當前頁一共分爲三部分:flex
整個頁面就是用 CustomScrollView
來作的,可是有一點不一樣:ui
平時咱們在使用 SliverAppBar
作這種摺疊效果的時候,摺疊起來是會變成主題色的,this
因此這裏我找了別人寫好的一個組件:FlexibleDetailBar
,用它之後的效果就是上面圖片那樣。spa
滑上去的時候「播放所有」那一行還停留在上方,是使用了 SliverAppBar 的 bottom參數。
這樣一個頁面的UI其實就分析完了。
然而!咱們回過頭看一下兩個頁面的UI,是否是感受很是類似!咱們來捋一下。
so,咱們從上往下來封裝。
肯定一下需求,看看須要傳入哪些參數:
bottom 須要的是一個 PreferredSizeWidget
,因此咱們的代碼是這樣:
class MusicListHeader extends StatelessWidget implements PreferredSizeWidget {
MusicListHeader({this.count, this.tail, this.onTap});
final int count;
final Widget tail;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.vertical(
top: Radius.circular(ScreenUtil().setWidth(30))),
child: Container(
color: Colors.white,
child: InkWell(
onTap: onTap,
child: SizedBox.fromSize(
size: preferredSize,
child: Row(
children: <Widget>[
HEmptyView(20),
Icon(
Icons.play_circle_outline,
size: ScreenUtil().setWidth(50),
),
HEmptyView(10),
Padding(
padding: const EdgeInsets.only(top: 3.0),
child: Text(
"播放所有",
style: mCommonTextStyle,
),
),
HEmptyView(5),
Padding(
padding: const EdgeInsets.only(top: 3.0),
child: count == null
? Container()
: Text(
"(共$count首)",
style: smallGrayTextStyle,
),
),
Spacer(),
tail ?? Container(),
],
),
),
),
),
);
}
@override
Size get preferredSize => Size.fromHeight(ScreenUtil().setWidth(100));
}
複製代碼
仍是先肯定一下需求,看看須要傳入什麼:
肯定好就以後,代碼以下:
class PlayListAppBarWidget extends StatelessWidget {
final double expandedHeight;
final Widget content;
final String backgroundImg;
final String title;
final double sigma;
final VoidCallback playOnTap;
final int count;
PlayListAppBarWidget({
@required this.expandedHeight,
@required this.content,
@required this.title,
@required this.backgroundImg,
this.sigma = 5,
this.playOnTap,
this.count,
});
@override
Widget build(BuildContext context) {
return SliverAppBar(
centerTitle: true,
expandedHeight: expandedHeight,
pinned: true,
elevation: 0,
brightness: Brightness.dark,
iconTheme: IconThemeData(color: Colors.white),
title: Text(
title,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
bottom: MusicListHeader(
onTap: playOnTap,
count: count,
),
flexibleSpace: FlexibleDetailBar(
content: content,
background: Stack(
children: <Widget>[
backgroundImg.startsWith('http')
? Image.network(
backgroundImg,
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
)
: Image.asset(backgroundImg),
BackdropFilter(
filter: ImageFilter.blur(
sigmaY: sigma,
sigmaX: sigma,
),
child: Container(
color: Colors.black38,
width: double.infinity,
height: double.infinity,
),
),
],
),
),
);
}
}
複製代碼
這裏有兩個地方須要注意一下:
startsWith('http')
Colors.black38
,這樣省的後續有白色圖片所致使文字看不清。這個item就比較簡單了,傳入一個實體類,根據參數來填值就行了,大體代碼以下:
class WidgetMusicListItem extends StatelessWidget {
final MusicData _data;
WidgetMusicListItem(this._data);
@override
Widget build(BuildContext context) {
return Container(
width: Application.screenWidth,
height: ScreenUtil().setWidth(120),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
// xxx
],
),
);
}
}
複製代碼
通過前兩次基礎頁面的搭建,咱們後續再來寫頁面的時候能夠說是簡單了百倍不止。
並且根本不用管網絡請求之類的邏輯,只需管好咱們的頁面就行了。
而在寫UI時,也必定要多看,多想,這個能不能封裝出來?那個能不能提取?
這樣之後再開發的話,真的是很是簡單。
該系列文章代碼已傳至 GitHub:github.com/wanglu1209/…
另我我的建立了一個「Flutter 交流羣」,能夠添加我我的微信 「17610912320」來入羣。