在開發flutter懸停頭部中,發現一個問題。
我們通常使用SliverAppBar(),去實現懸停的功能,在使用appbar的時候滿足不了我們的需求,就需要自定義,
如下:在title中寫了一個搜索框的功能,但是我發現在上滑隱藏的時候,icon有漸變效果,包括普通的text都有漸變的效果,像我寫的TextField則沒有這種效果。
也就是淺入淺出的效果。如圖:
return NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return <Widget>[ SliverAppBar(
//這是自定義的頭部布局 title: Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Icon( Iconfont.zhiboicon, ), flex: 0, ), Expanded( child: Container( margin: EdgeInsets.fromLTRB(10, 0, 10, 0), child: TextField( style: TextStyle( fontSize: 13, color: ColorKit.hexToColor('#979797')), decoration: InputDecoration( contentPadding: EdgeInsets.all(5), isCollapsed: true, prefixIcon: Container( margin: EdgeInsets.fromLTRB(10, 0, 5, 0), child: Icon( Iconfont.sousuoicon, size: 15, color: ColorKit.hexToColor('#999999'), ), ), //添加內部左邊圖標 prefixIconConstraints: BoxConstraints(), fillColor: ColorKit.hexToColor('#F6F6F6'), filled: true, hintText: '請輸入搜索內容', hintStyle: TextStyle(fontSize: 13), border: OutlineInputBorder( borderRadius: BorderRadius.circular(20), borderSide: BorderSide.none, ), ), onChanged: (text) { print('onchanged+$text'); }, onSubmitted: (text) { print('onSubmitted+$text'); }, ), ), flex: 1, ), Expanded( child: Icon( Iconfont.addicon, color: Colors.blue, ), flex: 0, ), ], ), ), pinned: true, floating: true, forceElevated: innerBoxIsScrolled, bottom: PreferredSize( preferredSize: Size(double.infinity, 48), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 1, child: Container( // height: 48, child: TabBar( indicatorSize: TabBarIndicatorSize.label, isScrollable: true, controller: _tabController, tabs: _tabbarTitile.map((e) { return Tab( text: e, ); }).toList(), ), )), Expanded( flex: 0, child: Container( margin: EdgeInsets.fromLTRB(10, 0, 10, 0), child: Icon(Iconfont.gengduoicon), )) ], ), ), ), ]; }, body: TabBarView( controller: _tabController, //tabbar控制器 children: _tabbarView, ), );
作為一個flutter都小白,每次遇到問題都喜歡網上找現成的,但是flutter都文章少的可憐。
后來還是下定決定自己研究一下,看了很多文章,這個方法可行,就是單獨把頭部的布局拿出來用SliverPersistentHeader來實現。
關於SliverPersistentHeader的介紹請看這里:沒錯是我,點我
“控件當滾動到邊緣時根據滾動的距離縮小高度,有點類似 SliverAppBar
的背景效果”
我寫的自定義SliverPersistentHeaderDelegate。如果你也是小白,看到這里不是很明白。別擔心,我會把整個代碼和使用方法寫上來的。
上代碼:
自己寫一個類,然后繼承SliverPersistentHeaderDelegate。
寫幾個常用的入參,比如我寫的是傳入的child,也就是傳入的小部件,這里用PreferredSize,為什么用這個呢,因為這個組件高度是固定的,也就是自己去設置內容的高度。在maxExtent和minExtent中需要獲取這個高度。必須是固定高度(我經過好多天研究出來的,可能是知識范圍有限,如果有問題,可以幫我指出改正)。
第二個是islucency,是布爾類型,在外部傳入是否滾動時淡入淡出。不知道為啥就寫上了,可能是習慣,為了以后有不用這個功能的時候。
第三個是背景顏色。這里單獨傳入一個背景色。有兩種原因,一種是如果直接在child中定義背景色,那么淡入淡出的時候,背景也會虛化。另外一種就是在入參中添加backgroundColor,背景不會虛化,虛化的是內容的小部件,大家也可以看到下面Opacity是嵌套在Container中的。不傳參數默認白色,當然這個屬性也不會影響小部件自己設置背景色。
import 'dart:math' as math; import 'package:flutter/material.dart'; class CommonSliverHeaderDelegate extends SliverPersistentHeaderDelegate{ PreferredSize child;//傳入preferredsize組件,因為此組件需要固定高度 bool islucency;//入參 是否更加滑動變化透明度,true,false Color backgroundColor;//需要設置的背景色 CommonSliverHeaderDelegate({@required this.islucency,@required this.child,this.backgroundColor}); @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { double mainHeight = maxExtent - shrinkOffset;//動態獲取滑動剩余高度 return Container( color: backgroundColor??Colors.white, child: Opacity( opacity:islucency==true&&mainHeight!=maxExtent?((mainHeight / maxExtent)*0.5).clamp(0, 1):1,//根據滑動高度隱藏顯示 child: child ), ); } @override // TODO: implement maxExtent double get maxExtent => this.child.preferredSize.height; @override // TODO: implement minExtent double get minExtent => this.child.preferredSize.height; @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { // TODO: implement shouldRebuild return true; } }
好,具體的使用:
代碼可能有點亂,大家看重點就行。
首先是NestedScrollView組件,然后就直接添加SliverPersistentHeader組件,在組件中的delegate屬性中加入咱們自定義的組件CommonSliverHeaderDelegate。
return SafeArea( child: NestedScrollView( headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { return <Widget>[ SliverPersistentHeader( delegate: CommonSliverHeaderDelegate( islucency: true,//需要淡入淡出 child:PreferredSize(//這里傳入PreferredSize組件,必須是這個,因為咱們自定義中接收到就是這個。 preferredSize: Size.fromHeight(60),//定義組件高度,必須寫。 child: Row(//自由發揮 children: [ Container( padding: EdgeInsets.all(20), alignment: Alignment.center, child: Text('sdfsdf'), ), Container( padding: EdgeInsets.all(20), alignment: Alignment.center, child: Text('sdfsdf'), ), Container( padding: EdgeInsets.all(20), alignment: Alignment.center, child: Icon(Icons.add), ), ], ) ) )), SliverPersistentHeader( floating: true, pinned: true, delegate: CommonSliverHeaderDelegate( islucency: false, child:PreferredSize( preferredSize: Size(double.infinity,48), child:Container( child: TabBar( controller: this._tabController, tabs: <Widget>[ Tab(text: '資訊'), Tab(text: '技術'), ], ), ) ) )), // SliverAppBar( // pinned: true, // floating: true, // bottom: PreferredSize( // preferredSize: Size(double.infinity,48), // child:TabBar( // indicatorPadding: EdgeInsets.all(10), // labelColor: Colors.black, // controller: this._tabController, // tabs: <Widget>[ // Tab(text: '資訊'), // Tab(text: '技術'), // ], // ), // ) // ), SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 5, mainAxisSpacing: 3), delegate: SliverChildBuilderDelegate((BuildContext context, int index) { return Container( color: Colors.primaries[index % Colors.primaries.length], ); }, childCount: 20), ) ]; }, body: TabBarView( controller: _tabController, //tabbar控制器 children: <Widget>[ Scaffold( body: Text('sdf'), ), Text('sdf'), ], ), ), );
上效果:
此功能到此為止。接下來我會研究tabbar的自定義高度問題,希望大家有什么問題可以和我探討。
SliverAppBar