效果图:
Sliver.gif
class SliverPage extends StatefulWidget {
static const routeName = '/sliver';
@override
_SliverPageState createState() => _SliverPageState();
}
class _SliverPageState extends State<SliverPage> with TickerProviderStateMixin {
TabController _controller;
ScrollController _scrollController; // 用于监听滚动offsetY
final List<String> _tabTitles = ["关注", "预测"];
final double _expandedHeight = 195; // SliverAppBar属性中可扩展的高度
bool _isScrollTop = false; // 用于页面滚动是否到指定位置,显示导航栏中组件的作用
@override
void initState() {
super.initState();
_controller = TabController(length: _tabTitles.length, vsync: this);
_scrollController = ScrollController();
double dividingValue = _expandedHeight - 50;
// 滚动监听
_scrollController.addListener(() {
if ((_scrollController.offset >= dividingValue) && _isScrollTop == false) {
setState(() {
_isScrollTop = true;
});
} else if ((_scrollController.offset < dividingValue) && _isScrollTop == true) {
setState(() {
_isScrollTop = false;
});
}
});
}
// 销毁
@override
void dispose() {
_controller.dispose();
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: NestedScrollView(
controller: _scrollController, // 监听滚动
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [ // 只能存放Sliver类型的Widget;类似Container可用SliverToBoxAdapter()
// 背景、顶部右侧按钮、头像+文本、水平可滚动列表
SliverHeadView(expandedHeight: _expandedHeight, isScrollTop: _isScrollTop),
// 吸顶效果的TabBar
buildTabBar()
];
},
body: TabBarView( // 设置对应的页面
controller: _controller,
children: [
FoucsPage(),
PredictPage()
]
)
)
);
}
组件 SliverHeadView()
class SliverHeadView extends StatelessWidget {
final bool isScrollTop;
final double expandedHeight;
SliverHeadView({
@required this.expandedHeight,
this.isScrollTop = false,
});
final List<String> _optionTitles = ["史蒂芬·库里", "迈克尔·乔丹", "科比·布莱恩特", "凯文·杜兰特", "德里克·罗斯"];
final List<String> _optionImgs = ["Curry", "Jordan", "KB", "KD", "Rose"];
@override
Widget build(BuildContext context) {
return SliverAppBar(
elevation: 0.0,
pinned: true,
expandedHeight: expandedHeight,
backgroundColor: Color(0xFFF5ca2b),
automaticallyImplyLeading: false, // 去除默认系统的返回健
leading: buildLeading(),
actions: buildActions(),
flexibleSpace: FlexibleSpaceBar(
background: Container(
color: Colors.white,
child: Stack(
children: [
// 背景图片
buildBg(),
// 顶部右侧按钮
buildRightButton(),
// 头像 + 文本
buildImageText(),
// 水平可滚动列表
buildOptions()
],
),
),
)
);
}
// 导航栏左侧缩放动画头像
Widget buildLeading() {
return ScaleAnimated(
display: isScrollTop,
child: CircleImage(
image: AssetImage("assets/images/others_mi.jpg"),
imageWH: 40,
borderColor: Colors.white,
borderWidth: 2,
)
);
}
// 导航栏右侧缩放按钮
List<Widget> buildActions() {
return [
ScaleAnimated(
display: isScrollTop,
child: Padding(
padding: const EdgeInsets.only(right: 15),
child: Container(
padding: EdgeInsets.fromLTRB(18, 10, 15, 10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18)
),
child: Row(
children: [
Text("NBA资料 ", style: TextStyle(
color: Colors.black,
fontSize: 13
)),
Icon(Icons.arrow_forward_ios, color: Colors.black, size: 13,)
],
),
),
)
)
];
}
// 背景图片
Widget buildBg() {
return Positioned(
top: 0,
left: 0,
right: 0,
child: Image.asset("assets/images/others_bg.png", fit: BoxFit.cover)
);
}
// 顶部右侧按钮
Widget buildRightButton() {
return Positioned(
right: 15,
top: 35,
child: FlatButton(
color: Colors.white,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Row(
children: [
Text("NBA资料 ", style: TextStyle(
color: Colors.black,
fontSize: 13
)),
Icon(Icons.arrow_forward_ios, color: Colors.black, size: 13,)
],
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18.0),
),
onPressed: () {
print("顶部资料");
},
)
);
}
// 头像+文本
Widget buildImageText() {
return Positioned(
left: 20,
top: 90,
child: Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CircleImage(
image: AssetImage("assets/images/others_mi.jpg"),
imageWH: 60,
borderColor: Colors.white,
borderWidth: 2,
),
SizedBox(width: 15,),
Text("NBA小子", style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.bold
))
],
),
)
);
}
// 水平可滚动列表
Widget buildOptions() {
return Positioned(
left: 20,
right: 20,
top: 170,
child: Container(
height: 65,
padding: EdgeInsets.only(top: 5, bottom: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(topLeft: Radius.circular(8), topRight: Radius.circular(8)),
boxShadow: [
BoxShadow(color: Colors.white, offset: Offset(-1, -2), blurRadius: 10, spreadRadius: -5),
BoxShadow(color: Colors.white, offset: Offset(1, -2), blurRadius: 10, spreadRadius: -5)
]
),
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
children: [0, 1, 2, 3, 4].map((index) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.asset("assets/images/${_optionImgs[index]}.png", fit: BoxFit.cover, width: 35, height: 35,),
Text("${_optionTitles[index]}", style: TextStyle(
fontSize: 13,
color: Colors.black
))
],
),
);
}).toList(),
),
)
);
}
}
关于缩放动画组件
class ScaleAnimated extends StatelessWidget {
final Widget child;
final bool display;
ScaleAnimated({
@required this.child,
this.display = false
});
@override
Widget build(BuildContext context) {
return AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return ScaleTransition(
scale: animation,
child: child,
);
},
child: display ? child : SizedBox.shrink(),
);
}
}
关于吸顶效果组件 利用SliverPersistentHeader
// 吸顶效果的TabBar
Widget buildTabBar() {
return SliverPersistentHeader( // SliverPersistentHeader:吸顶效果
pinned: true,
delegate: CeilingHeaderDelegate(
child: TabBar(
controller: _controller,
labelColor: Color(0xFFF5ca2b),
labelStyle: TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold
),
unselectedLabelColor: Color(0xff999999),
indicatorSize: TabBarIndicatorSize.label,
indicatorColor: Color(0xFFF5ca2b),
indicatorWeight: 3,
tabs: _tabTitles.map((value) => Tab(text: value)).toList()
)
), // 不能直接实现,代理为抽象类;所以继承此代理
);
}
}
// 吸顶效果
class CeilingHeaderDelegate extends SliverPersistentHeaderDelegate {
final TabBar child;
CeilingHeaderDelegate({
@required this.child
});
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Colors.white,
child: child,
);
}
@override
double get maxExtent => this.child.preferredSize.height;
@override
double get minExtent => this.child.preferredSize.height;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return true;
}
}
网友评论