NestedScrollView
1.简介
在滑动中,ScrollView与ListView以及TabBarView,会出现滑动冲突,该控件是用于解决滑动冲突的,与CustomScrollView类似,但是不同点在于
CustomScrollView 要全部是Sliver家族的,其中TabBarView不属于Sliver家族,导致CustomScrollView 无法实现该功能,所以有了NestedScrollView
2.说明
NestedScrollView当中,有2个ScrollController.一个是inner,一个outer。 outer是负责headerSliverBuilder里面的滚动widgets inner是负责body里面的滚动widgets 当outer滚动到底了之后,就会看看inner里面是否有能滚动的东东,开始滚动
Tabview是在body里面,这里我们肯定需要对inner进行处理。 首先我们要明白,NestedScrollView是怎么处理outer和inner的关系的。
3.属性
Key key,
this.controller,
this.scrollDirection = Axis.vertical, 方向
this.reverse = false,
this.physics, //可以指定滑动方式 NeverScrollableScrollPhysics 不滑动
@required this.headerSliverBuilder, outer
@required this.body, inner
this.dragStartBehavior = DragStartBehavior.start,
this.floatHeaderSlivers = false,
this.clipBehavior = Clip.hardEdge,
this.restorationId,
4.案例 实现一个类似 AppBarLayout+CollapsingToolbarLayout + listView的效果
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// home: CustomScrollViewDemo(),
home: Scaffold(
body: NestedScrollViewDemo2(),
),
);
}
}
class NestedScrollViewDemo2 extends StatefulWidget {
NestedScrollViewDemo2({Key key}) : super(key: key);
@override
_NestedScrollViewDemo2State createState() {
return _NestedScrollViewDemo2State();
}
}
class _NestedScrollViewDemo2State extends State<NestedScrollViewDemo2> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return NestedScrollView(
scrollDirection: Axis.vertical,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
expandedHeight: 200.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('复仇者联盟'),
background: Container(
color: Colors.blue[300],
height: 200,
width: double.infinity,
child: Image.network(
'http://img.haote.com/upload/20180918/2018091815372344164.jpg',
fit: BoxFit.cover,
height: 200,
),
),
),
)
];
},
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Container(
height: 80,
// color: Colors.primaries[index % Colors.primaries.length],
color: Colors.yellow,
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(color: Colors.blue, fontSize: 20),
),
);
},
itemCount: 20,
),
// body: Container(
// height: 200,
// color: Colors.red,
// ),
);
}
}
注意:ListView头部有一段空白区域,是因为当ListView没有和AppBar一起使用时,头部会有一个padding,为了去掉padding,可以使用MediaQuery.removePadding 包裹这个ListView
body: MediaQuery.removePadding(
removeTop: true,
context: context,
child: ListView.builder(
itemBuilder: (BuildContext context, int index) {
return Container(
height: 80,
// color: Colors.primaries[index % Colors.primaries.length],
color: Colors.yellow,
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(color: Colors.blue, fontSize: 20),
),
);
},
itemCount: 20,
),
),
5.NestedScrollView + TabBarView结合使用
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// home: CustomScrollViewDemo(),
home: Scaffold(
body: NetScrollHomePage(),
),
);
}
}
class NetScrollHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return ScrollHomePageState();
}
}
class ScrollHomePageState extends State with SingleTickerProviderStateMixin {
TabController tabController;
@override
void initState() {
super.initState();
///这里的 3 代表有三个子 Item
///应用到 TabBarView 中,对应其中3个子Item
///应用到 TabBar中,对应其中32上子Item
tabController = new TabController(length: 3, vsync: this);
}
TabBar buildTabBar() {
return TabBar(
controller: tabController,
tabs: <Widget>[
new Tab(
text: "标签一",
),
new Tab(
text: "标签二",
),
new Tab(
text: "标签三",
),
],
);
}
String imageUrl =
'http://img.haote.com/upload/20180918/2018091815372344164.jpg';
FlexibleSpaceBar buildFlexibleSpaceBar() {
return FlexibleSpaceBar(
// title: Text("FlexibleSpaceBar title"),
centerTitle: true,
background: Container(
color: Colors.blue[300],
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
height: 150,
child: Image.network(
imageUrl,
fit: BoxFit.fill,
height: 160,
width: 400,
),
),
],
),
),
);
}
NestedScrollView buildNestedScrollView() {
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool b) {
return [
SliverAppBar(
///true SliverAppBar 不会滑动
pinned: true,
///是否随着滑动隐藏标题
floating: true,
///SliverAppBar展开的高度
expandedHeight: 200,
flexibleSpace: buildFlexibleSpaceBar(),
bottom: buildTabBar(),
),
SliverToBoxAdapter(
child: Container(
width: double.infinity,
height: 200,
color: Colors.red,
),
),
];
},
///主体部分
body: buildTabBarView(),
);
}
TabBarView buildTabBarView() {
return TabBarView(
controller: tabController,
children: <Widget>[
SingleChildScrollView(
child: Container(
alignment: Alignment.bottomLeft,
child: Text("这是第一个页面"),
height: 1000,
),
),
Text(
"这是第二个页面",
style: TextStyle(color: Colors.blue),
),
Text(
"这是第三个页面",
style: TextStyle(color: Colors.red),
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: Text(" 配制"),
),
/// 处理滑动
body: buildNestedScrollView(),
);
}
}