一、初试Sliver
1.1 使用Sliver制作一个ListView
ListView的底层原理就是Sliver,我们可以用CustomScrollView
先做出一个视窗(所谓视窗就是里面的内容比边框大),在其中使用SliverChildBuilderDelegate
,而事实上,这也正是ListViewBuilder的底层做法。
CustomScrollView( //定义一个视窗
slivers: [
SliverList(delegate: SliverChildBuilderDelegate((context, index) {
return Container(
height: 200,
color: Colors.primaries[index%Colors.primaries.length],
);
}))
],
)
1.2 更加灵活的ListView——加入任意Widget
而使用Sliver,我们就可以实现更加灵活的功能,例如在顶部加入一个FlutterLogo:
CustomScrollView(
slivers: [
SliverToBoxAdapter(child: FlutterLogo(size:100),), //加入一个FlutterLogo
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
//使用SliverChildBuilderDelegate实现动态加载(Builder)
//delegate: SliverChildListDelegate对应不用Builder的ListView
return Container(
height: 200,
color: Colors.primaries[index%Colors.primaries.length],
);
}))
],
)
1.3 更加灵活的ListView——组合GridView
我们也可以尝试组合GridView:
CustomScrollView(
slivers: [
/* 1. 用SliverToBoxAdapter包裹的普通Widget */
const SliverToBoxAdapter(child: FlutterLogo(size:100),),
/* 2. 用SliverGrid表示的GridView */
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
delegate: SliverChildBuilderDelegate((context, index) => Container(
color: Colors.primaries[index % Colors.primaries.length]
), childCount: 6, //也就是GridView中的itemCount
)
),
/* 3. 用SliverList表示的ListViewer.builder */
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Container(
height: 200,
color: Colors.primaries[index%Colors.primaries.length],
);
})),
],
)
二、其他的几个常用Sliver
2.1 使用itemExtent——SliverFixedExtentList
您肯定还记得ListView中,为了方面滚动条的定位和跳转,我们会通过ListView中的itemExtent
属性来固定每一项在主轴上的长度,例如我们需要定位到第100个元素,只需要将其乘上对应的数即可。
这里的itemExtent
属性不可以用列表内容Widget的widget
或height
属性替代,因为那样不会获得滚动条的性能优化。
在Sliver中我们需要使用SliverFixedExtentList
来定义itemExtent属性,这样我们就可以让某些元素有固定的长度,另一些则不必。
2.2 自适应的itemExtent——SliverPrototypeExtentList
通常我们很难直接得到内容Widget的高度,例如字号可能会因为老年模式而改变,因此我们可以使用SliverPrototypeExtentList
来动态确定其itemExtent
.
SliverPrototypeExtentList
需要一个prototypeItem
属性,它只是用来测量内容在主轴上的高度,并不会真的渲染到屏幕上。当字号调整时,我们只要配置了prototypeItem
属性,它就会自动重新计算其itemExtent
.
//自适应的itemExtent
SliverPrototypeExtentList(delegate: delegate, prototypeItem: prototypeItem),
2.3 使用PageView——SliverFillViewport
回想一下PageView
组件——其中的每一个项目都会沾满整个屏幕,通过滑动来切换,通常我们可以用它来做软件的开屏教程。
在Sliver中,提供了一个叫做SliverFillViewport
的组件,它也是PageView的底层原理。
2.4 自动回缩的华丽导航条——SliverAppBar
我们可以使用SliverAppBar制作一个华丽的动态导航条,首先我们要去掉Scaffold中的AppBar属性,并用下面的代码添加到CustomScrollView
中:
SliverAppBar(
floating: true, //稍微一向下拉导航条就出现
snap: true,
//title: Text("111"),
pinned: false,//不固定
expandedHeight: 100,//导航条高度
stretch: true,//使用更长的拉伸(flexibleSpace)
flexibleSpace: FlexibleSpaceBar(
background: FlutterLogo(size:100),//导航条中的背景
title: Text("111"),
//上缩效果
collapseMode: CollapseMode.parallax,
//下拉效果
stretchModes: [
StretchMode.blurBackground,
StretchMode.zoomBackground,
StretchMode.fadeTitle
],
),
),
三、Sliver转换器
3.1 普通Widget的Sliver对应版本
例如:
-
Opacity
=>SliverOpacity
-
AnimatedOpacity
=>SilverAnimatedOpacity
-
值得注意的是,
SizedBox
直接对应空的SliverToBoxAdapter()
,也就是不传任何东西
普通Widget基本都能找到其Sliver形式,可以直接用sliver属性嵌套(相当于普通Widget的child)属性。
3.2 特殊Sliver
SliverFillRemaining
——占据当前视窗的剩余全部空间,但最多占满一整页。- 注意:使用
SliverLayoutBuilder
可以自己作出一个SliverFillRemaining组件哦~
完整实例:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: const CustomScrollView(
slivers: [
SliverAppBar(
floating: true, //稍微一向下拉导航条就出现
snap: true,
//title: Text("111"),
pinned: false,//不固定
expandedHeight: 100,//导航条高度
stretch: true,//使用更长的拉伸(flexibleSpace)
flexibleSpace: FlexibleSpaceBar(
background: FlutterLogo(size:100),//导航条中的背景
title: Text("111"),
//上缩效果
collapseMode: CollapseMode.parallax,
//下拉效果
stretchModes: [
StretchMode.blurBackground,
StretchMode.zoomBackground,
StretchMode.fadeTitle
],
),
),
SliverToBoxAdapter(child: FlutterLogo(size: 100)),
SliverToBoxAdapter(child: FlutterLogo(size: 100)),
SliverToBoxAdapter(child: FlutterLogo(size: 100)),
SliverFillRemaining(
child:Center( //用加载圈填满剩余视口
child: CircularProgressIndicator(),
)
)
],
),
);
}
}
3.3 SliverAppBar原理——SliverPersistentHeader
使用原理类似,
SliverToBoxAdapter(),
//防止SliverPersistentHeader成为最顶层的Sliver,以至于无法上拉刷新
SliverPersistentHeader(
delegate:
pinned: true,//像AppBar一样钉在最顶部
)