CustomScrollView
來源: CustomScrollView 是可使用
Sliver
來自定義滾動模型(效果)的組件。舉個例子,假設有一個頁面,頂部須要一個GridView
,底部須要一個ListView
,而要求整個頁面的滑動效果是統一的,即它們看起來是一個總體。若是使用GridView+ListView
來實現的話,就不能保證一致的滑動效果,而CustomScrollView
就能夠實現這個效果。html
實現源碼請查看原文:CustomScrollViewweb
簡易版代碼以下:less
class CustomScrollViewTestRoute extends StatelessWidget { @override Widget build(BuildContext context) { return Material( child: CustomScrollView( slivers: <Widget>[ SliverAppBar( //AppBar,包含一個導航欄 ), SliverPadding( padding: const EdgeInsets.all(8.0), sliver: new SliverGrid( //Grid 內容 ), ), new SliverFixedExtentList( // ListView 內容 ), ], ), ); } }
添加頭部
可使用
SliverPersistentHeader
來實現,也能夠直接在ListView
頂部添加一個Head
來實現。ide
效果以下:
源碼實現也很簡單,這裏直接在CustomScrollView
裏面嵌套兩個SliverFixedExtentList
,一個顯示Head 頭部
,一個顯示List 列表
。
svg
參考:Flutter:Slivers你們族,讓滑動視圖的組合變得很簡單!
使用 SliverPersistentHeader
來實現須要自定義類
繼承SliverPersistentHeaderDelegate
flex
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate{ _SliverAppBarDelegate({ @required this.minHeight, @required this.maxHeight, @required this.child, }); final double minHeight; final double maxHeight; final Widget child; @override double get minExtent => minHeight; @override double get maxExtent => math.max(maxHeight, minHeight); @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return new SizedBox.expand(child: child); } @override bool shouldRebuild(_SliverAppBarDelegate oldDelegate) { return maxHeight != oldDelegate.maxHeight || minHeight != oldDelegate.minHeight || child != oldDelegate.child; } }
使用的時候直接添加到sliver
裏面便可ui
SliverPersistentHeader( pinned: false, floating: false, delegate: _SliverAppBarDelegate( minHeight: 60.0, maxHeight: 250.0, child: Container( color: Colors.blue, child: Center( child: Text('header',style: TextStyle(color: Colors.white),), ) ), ), ),
視差滑動
視差滾動是指讓多層背景以不一樣的速度移動,在造成立體滾動效果的同時,還能保證良好的視覺體驗。this
效果圖以下:
要實現該效果主要用到了SliverAppBar
組件。spa
作過Android
開發的都知道CollapsingToolbarLayout
控件,它能夠實現頁面頭部展開、合併的視差效果。在Flutter
中是經過SliverAppBar
組件實現相似的效果。.net
直接查看SliverAppBar
組件支持的字段吧:
const SliverAppBar({ Key key, this.leading,// 左側的widget this.automaticallyImplyLeading = true, this.title,//標題 this.actions,//標題右側的操做 this.flexibleSpace,// 背景widget,如 FlexibleSpaceBar 可設置標題,背景圖片,標題邊距等 this.bottom, // 底部區 this.elevation,//陰影 this.forceElevated = false,//是否顯示陰影 this.backgroundColor,//背景顏色 this.brightness,//狀態欄主題 this.iconTheme,// 圖標主題 this.actionsIconTheme,//action圖標主題 this.textTheme,//文字主題 this.primary = true,//是否顯示在狀態欄的下面,false就會佔領狀態欄的高度 this.centerTitle,//標題居中顯示 this.titleSpacing = NavigationToolbar.kMiddleSpacing,//標題橫向間距 this.expandedHeight,//合併的高度,默認是狀態欄的高度加AppBar的高度 this.floating = false,//滑動時是否懸浮 this.pinned = false,// 滑動時標題欄是否固定 this.snap = false,// 滑動時標題欄跟隨移動並固定在頂部, pinned 和 floating 效果的組合 this.stretch = false,// 標題跟隨滑動時拉伸,收縮 this.stretchTriggerOffset = 100.0,// 標題跟隨滑動時拉伸,收縮的偏移量 this.onStretchTrigger,// 跟隨滑動時拉伸,收縮的回調 this.shape,// 陰影形狀,elevation 大於0 纔會顯示 })
在字段的後面都寫明瞭相應的介紹,只須要在使用的時候設置相關的參數便可實現效果。
監聽滑動
ScrollController
使用列表提供的controller
字段,並調用監聽方法監聽滑動距離
_controller.addListener((){ print('_controller offset : ${_controller.offset}'); });
NotificationListener
- 使用
ScrollNotification
控件去監聽滾動列表。
@override Widget build(BuildContext context) { return Material( child: NotificationListener<ScrollNotification>( onNotification: (scrollNotification) { //註冊通知回調 if (scrollNotification is ScrollStartNotification) { //滾動開始 print('Scroll Start: ${scrollNotification.metrics.pixels}'); }else if (scrollNotification is ScrollUpdateNotification) { //滾動位置更新 print('Scroll Update: ${scrollNotification.metrics.pixels}'); } else if (scrollNotification is ScrollEndNotification) { //滾動結束 print('Scroll End: ${scrollNotification.metrics.pixels}'); } return false; }, child: CustomScrollView( controller: _controller, // 滑動列表 widget ), ) ); }
回到頂部功能
在新聞列表,或者列表數據不少的時候,咱們日後翻好幾頁以後,忽然想回到列表的頂部,這時候該如何實現呢?
- 首先,在
initState
方法裏,初始化ScrollController
, - 隨後,在視圖構建方法
build
中,咱們將ScrollController
對象與 滾動列表controll
關聯,並在SliverAppBar
添加一個按鈕用於點擊後調用_controller.animateTo
方法返回列表頂部。 - 最後,在
dispose
方法中,咱們對ScrollController
進行了資源釋放。
實現源碼以下:
class _CustomScrollViewPageState extends State<CustomScrollViewPage> { //滑動控制器 ScrollController _controller; @override void initState() { //初始化控制器 _controller = ScrollController(); super.initState(); } @override void dispose() { //銷燬控制器 _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Material( child: CustomScrollView( controller: _controller, slivers: <Widget>[ //AppBar,包含一個導航欄 SliverAppBar( pinned: true, expandedHeight: 250.0, actions: <Widget>[ RaisedButton( child: Text('返回頂部'), onPressed: (){ _controller.animateTo(.0, duration: Duration(milliseconds: 200), curve: Curves.ease); }, ) ], flexibleSpace: FlexibleSpaceBar( title: const Text('CustomScrollView'), background: Image.network( "https://ssyerv1.oss-cn-hangzhou.aliyuncs.com/picture/389e31d03d36465d8acd9939784df6f0.jpg!sswm", fit: BoxFit.cover,), ), ), //List new SliverFixedExtentList( itemExtent: 50.0, delegate: new SliverChildBuilderDelegate( (BuildContext context, int index) { //建立列表項 return new Container( alignment: Alignment.center, color: Colors.lightBlue[100 * (index % 9)], child: new Text('list item $index'), ); }, childCount: 50 //50個列表項 ), ), ], ), ); } }
完~
本文同步分享在 博客「_龍衣」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。