NestedScrollView
一个滚动视图,其中可以嵌套其他滚动视图,它们的滚动位置是内在链接的。这意味着使用NestedScrollView
,将获得两个滚动区域。一个是标题部分,另一个是它的正文部分。NestedScrollView
连接这两个部分,因此它们的行为就像一个一致的可滚动区域。这两个部分将一起滚动,并且它们的滚动位置将被链接。构造函数:
const NestedScrollView({
super.key,
this.controller,
this.scrollDirection = Axis.vertical,
this.reverse = false,
this.physics,
required this.headerSliverBuilder,
required this.body,
this.dragStartBehavior = DragStartBehavior.start,
this.floatHeaderSlivers = false,
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.scrollBehavior,
})
它可以在需要嵌套滚动视图的许多不同方式中使用,例如,可以在 body
中有一个网格和一个列表,在headerSliverBuilder
中有一个SliverAppBar
。
import 'package:flutter/material.dart';
class NestedScrollViewPage extends StatelessWidget {
const NestedScrollViewPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(
body: ExampleOne(),
);
}
}
class ExampleOne extends StatelessWidget {
const ExampleOne({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return NestedScrollView(
headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
return <Widget>[
SliverAppBar(
expandedHeight: 300,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: const Text('NestedScrollView'),
background: Image.asset('images/liu.webp',fit: BoxFit.cover,),
),
)
];
},
body: ListView.builder(
physics: const ClampingScrollPhysics(),
itemBuilder: (BuildContext context , int index){
return Container(
height: 100,
color: Colors.primaries[index % Colors.primaries.length],
child: Center(
child: Text('Item $index',style: const TextStyle(color: Colors.white,fontSize: 30),),
),
);
},
itemCount: 30,
)
);
}
}
常见的用例是一个SliverAppBar
,在 body
中有一个TabBar
和一个TabBarView
,其中可滚动的内容根据所选的选项卡而有所不同。在这种情况下,NestedScrollView
可以根据嵌套滚动视图中的操作来保持SliverAppBar
折叠或展开,从而节省了大量工作。使用SliverOverlapAbsorber
包裹另一个的Sliver
,强制其布局范围被视为重叠。
import 'package:flutter/material.dart';
class NestedScrollViewPage extends StatelessWidget {
const NestedScrollViewPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(
body: ExampleTwo(),
);
}
}
class ExampleTwo extends StatelessWidget {
const ExampleTwo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final List<String> tabs = <String>['Tab 1','Tab 2'];
return DefaultTabController(
length: tabs.length,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context,bool innerBoxIsScrolled){
return <Widget> [
SliverOverlapAbsorber(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
sliver: SliverAppBar(
backgroundColor: Colors.red,
pinned: true,
expandedHeight: 150.0,
forceElevated: innerBoxIsScrolled,
bottom: TabBar(
tabs: tabs.map((String name) => Tab(text:name)).toList(),
),
),
)
];
},
body: TabBarView(
children: tabs.map((String name) {
return SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (BuildContext context){
return CustomScrollView(
slivers: [
SliverOverlapInjector(
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
),
SliverPadding(
padding: EdgeInsets.all(10),
sliver: SliverFixedExtentList(
itemExtent: 50,
delegate: SliverChildBuilderDelegate(
(BuildContext context,int index){
return ListTile(
title: Text('Item $index'),
);
},
childCount: 50,
),
),
)
],
);
},
),
);
}).toList(),
),
),
)
);
}
}