SliverAppBar
A material design app bar that integrates with a [CustomScrollView].
AppBar 和 SliverAppBar 是Material Design中的 App Bar,也就是 Android 中的 Toolbar
SliverAppBar是一個與 CustomScrollView 結合使用的material design
風格的標題欄 .
不同於AppBar, 它可以展開或收縮.
構造方法:
const SliverAppBar({
Key key,
this.leading, //左側標題
this.automaticallyImplyLeading = true, //如果leading為null,是否自動填充一個leading
this.title, //標題
this.actions, //選單
this.flexibleSpace, //可以展開區域,通常是一個FlexibleSpaceBar
this.bottom, //底部內容區域
this.elevation,
this.forceElevated = false, //結合 elevation 使用,當elevation 不為 0 的時候,是否顯示陰影
this.backgroundColor,
this.brightness,
this.iconTheme,
this.actionsIconTheme,
this.textTheme,
this.primary = true,
this.centerTitle,
this.excludeHeaderSemantics = false,
this.titleSpacing = NavigationToolbar.kMiddleSpacing,
this.expandedHeight, //展開後的高度
this.floating = false, //是否向下滑動時立即顯示appBar
this.pinned = false, //appBar是否置頂
this.snap = false, //當手指放開時,SliverAppBar是否會根據當前的位置展開/收起
this.stretch = false,
this.stretchTriggerOffset = 100.0,
this.onStretchTrigger,
this.shape,
})
複製程式碼
如果你看過AppBar的構造方法,那麼你會發現AppBar的構造方法都是在這裡面的。其屬性也是一致的,下面我們針對公共的屬性做一下詳細的解釋:
-
snap : 需要注意的是
snap
是和floading
結合使用的 , 並且要求floating=true
, 否則使用會報錯. -
automaticallyImplyLeading : 如果為true(預設) , 那麼頁面可以回退的話,會新增一個返回按鈕.
-
leading:在標題前面顯示的一個控制元件,在首頁通常顯示應用的 logo;在其他介面通常顯示為返回按鈕
-
title: Toolbar 中主要內容,通常顯示為當前介面的標題文字
-
actions:一個 Widget 列表,代表 Toolbar 中所顯示的選單,對於常用的選單,通常使用 IconButton 來表示;對於不常用的選單通常使用 PopupMenuButton 來顯示為三個點,點選後彈出二級選單
-
bottom:一個 AppBarBottomWidget 物件,通常是 TabBar。用來在 Toolbar 標題下面顯示一個 Tab 導航欄
-
elevation:紙墨設計中控制元件的 z 座標順序,預設值為 4,對於可滾動的 SliverAppBar,當 SliverAppBar 和內容同級的時候,該值為 0, 當內容滾動 SliverAppBar 變為 Toolbar 的時候,修改 elevation 的值 flexibleSpace:一個顯示在 AppBar 下方的控制元件,高度和 AppBar 高度一樣,可以實現一些特殊的效果,該屬性通常在 SliverAppBar 中使用
-
backgroundColor:APP bar 的顏色,預設值為 ThemeData.primaryColor。改值通常和下面的三個屬性一起使用
-
brightness:App bar 的亮度,有白色和黑色兩種主題,預設值為 ThemeData.primaryColorBrightness
-
iconTheme:App bar 上圖示的顏色、透明度、和尺寸資訊。預設值為 ThemeData.primaryIconTheme
-
textTheme: App bar 上的文字樣式。預設值為 ThemeData.primaryTextTheme
-
centerTitle: 標題是否居中顯示,預設值根據不同的作業系統,顯示方式不一樣
一個簡單的SliverAppBar
SliverAppBar(
automaticallyImplyLeading: false,
elevation: 5,
forceElevated: true,
expandedHeight: 200,
floating: true,
snap: false,
pinned: true,
stretch: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('SliverAppBar'),
background: Image.asset(
'images/pic1.jpg',
fit: BoxFit.fill,
),
//標題是否居中
centerTitle: false,
//標題間距
//titlePadding: EdgeInsetsDirectional.only(start: 0, bottom: 16),
collapseMode: CollapseMode.parallax,
))
複製程式碼
雖然基本相同,構造方法也是非常的簡單,但是我們卻不能直接使用它,由官方文件可以看到我們通常結合ScrollView來使用它。
我們結合CustomScrollView來看下例子吧:
/*
* Created by 李卓原 on 2018/9/13.
* email: zhuoyuan93@gmail.com
*
*/
import 'package:flutter/material.dart';
class DiscoverListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: _sliverBuilder,
body: Center(
child: ListView.builder(
itemBuilder: _itemBuilder,
itemCount: 15,
),
)),
);
}
List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
centerTitle: true, //標題居中
expandedHeight: 200.0, //展開高度200
floating: false, //不隨著滑動隱藏標題
pinned: true, //固定在頂部
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text('我是一個FlexibleSpaceBar'),
background: Image.network(
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531798262708&di=53d278a8427f482c5b836fa0e057f4ea&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F342ac65c103853434cc02dda9f13b07eca80883a.jpg",
fit: BoxFit.cover,
),
),
)
];
}
Widget _itemBuilder(BuildContext context, int index) {
return ListTile(
leading: Icon(Icons.android),
title: Text('無與倫比的標題+$index'),
);
}
}
複製程式碼
首先我們使用了NestedScrollView中的headerSliverBuilder屬性新增了SliverAppBar
然後我們設定展開的高度為200,不讓標題欄隨著滑動滾動出可視區域, 最後我們給NestedScrollView的body加了一個長度為15的ListView
然後我們來看下效果:
我們把 pinned的屬性設定為false(不固定在頂部)再看下效果
接下來我們來看下bottom屬性,允許我們在在下面放置你想放置其他Widget,好吧我們來放個TabBar看下
其實程式碼很簡單,只不過我們需要讓DiscoverListPage繼承於 StatefulWidget,然後讓State with TickerProviderStateMixin , 併為SliverAppBar新增個bottom ,改造後的程式碼 :
/*
* Created by 李卓原 on 2018/9/13.
* email: zhuoyuan93@gmail.com
*
*/
import 'package:flutter/material.dart';
class DiscoverListPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => DiscoverListState();
}
class DiscoverListState extends State<DiscoverListPage>
with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: _sliverBuilder,
body: Center(
child: ListView.builder(
itemBuilder: _itemBuilder,
itemCount: 15,
),
)),
);
}
List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
//標題居中
centerTitle: true,
//展開高度200
expandedHeight: 200.0,
//不隨著滑動隱藏標題
floating: false,
//固定在頂部
pinned: false,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text('我是一個FlexibleSpaceBar'),
background: Image.network(
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531798262708&di=53d278a8427f482c5b836fa0e057f4ea&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F342ac65c103853434cc02dda9f13b07eca80883a.jpg",
fit: BoxFit.cover,
),
),
// bottom 這是新增的 這是新增的 這是新增的 這是新增的
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.cake), text: '左側'),
Tab(icon: Icon(Icons.golf_course), text: '右側'),
],
controller: TabController(length: 2, vsync: this),
),
)
];
}
Widget _itemBuilder(BuildContext context, int index) {
return ListTile(
leading: Icon(Icons.android),
title: Text('無與倫比的標題+$index'),
);
}
}
複製程式碼
看一下效果: 簡直, 醜的不忍直視。 當然我們是希望這個TabBar在SliverAppBar下方,並且隨著SliverAppBar滾動的。
由於TabBar的高度所以我們並不能讓SliverAppBar滑動到頂部,所以要想實現隨著SliverAppBar的移動,把TabBar放在bottom也不是很合適的。 在這裡,我們可以藉助於SliverPersistentHeader中的SliverPersistentHeader屬性來解決
SliverPersistentHeader的構造很簡單,只有簡單的幾個屬性,不再具體講了
const SliverPersistentHeader({
Key key,
@required this.delegate,
this.pinned = false,
this.floating = false,
})
複製程式碼
全部程式碼如下:
/*
* Created by 李卓原 on 2018/9/13.
* email: zhuoyuan93@gmail.com
*
*/
import 'package:flutter/material.dart';
class DiscoverListPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => DiscoverListState();
}
class DiscoverListState extends State<DiscoverListPage>
with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
headerSliverBuilder: _sliverBuilder,
body: Center(
child: ListView.builder(
itemBuilder: _itemBuilder,
itemCount: 15,
),
)),
);
}
List<Widget> _sliverBuilder(BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
//標題居中
centerTitle: true,
//展開高度200
expandedHeight: 200.0,
//不隨著滑動隱藏標題
floating: false,
//固定在頂部
pinned: false,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text('我是一個FlexibleSpaceBar'),
background: Image.network(
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1531798262708&di=53d278a8427f482c5b836fa0e057f4ea&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F342ac65c103853434cc02dda9f13b07eca80883a.jpg",
fit: BoxFit.cover,
),
),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(TabBar(
labelColor: Colors.red,
unselectedLabelColor: Colors.grey,
tabs: [
Tab(icon: Icon(Icons.cake), text: '左側'),
Tab(icon: Icon(Icons.golf_course), text: '右側'),
],
controller: TabController(length: 2, vsync: this),
)))
];
}
Widget _itemBuilder(BuildContext context, int index) {
return ListTile(
leading: Icon(Icons.android),
title: Text('無與倫比的標題+$index'),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar);
final TabBar _tabBar;
@override
double get minExtent => _tabBar.preferredSize.height;
@override
double get maxExtent => _tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
child: _tabBar,
);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}
複製程式碼
效果:
比剛才有了明顯的進步,但是有童鞋就問了,我們怎麼讓這個SliverPersistentHeader中的內容(TabBar)不隨著ListView的滾動而滑動呢?
其實很簡單,上面我貼出了SliverPersistentHeader的構造方法,因為SliverPersistentHeader跟SliverAppBar一樣都有一個 pinned屬性,將它設定為true這裡面的內容就會在到達頂部後停止跟隨ListView移動了。