类型
RenderBox
Sliver
Sliver组成部分
- Scrollable 监听到用户滑动行为后,根据最新的滑动偏移构建 Viewport 。
- Viewport 将当前视口信息和配置信息通过 SliverConstraints 传递给 Sliver。
- Sliver 中对子组件(RenderBox)按需进行构建和布局,然后确认自身的位置、绘制等信息。
Scrollable
const Scrollable({
Key? key,
this.axisDirection = AxisDirection.down,
this.controller,
this.physics,
required this.viewportBuilder,
this.incrementCalculator
this.excludeFromSemantics = false,
this.semanticChildCount,
this.dragStartBehavior = DragStartBehavior.start,
this.restorationId,
this.scrollBehavior,
}) : assert(axisDirection != null),
assert(dragStartBehavior != null),
assert(viewportBuilder != null),
assert(excludeFromSemantics != null),
assert(semanticChildCount == null || semanticChildCount >= 0),
super (key: key);
physics
:此属性接受一个ScrollPhysics
类型的对象。
ClampingScrollPhysics
:列表滑动到边界时将不能继续滑动,Android效果 。BouncingScrollPhysics
:iOS 下弹性效果。
viewportBuilder
:构建Viewport
的回调。Viewport
Viewport({
Key? key,
this.axisDirection = AxisDirection.down,
this.crossAxisDirection,
this.anchor = 0.0,
required ViewportOffset offset, // 用户的滚动偏移
// 类型为Key,表示从什么地方开始绘制,默认是第一个元素
this.center,
this.cacheExtent, // 预渲染区域
//该参数用于配合解释cacheExtent的含义,也可以为主轴长度的乘数
this.cacheExtentStyle = CacheExtentStyle.pixel,
this.clipBehavior = Clip.hardEdge,
List<Widget> slivers = const <Widget>[], // 需要显示的 Sliver 列表
})
Sliver
Sliver
主要作用是对子组件进行构建和布局,比如ListView
的Sliver
需要实现子组件(列表项)按需加载功能,只有当列表项进入预渲染区域时才会去对它进行构建和布局、渲染。常用可滑动组件
SingleChildScrollView
const SingleChildScrollView({
Key? key,
this.scrollDirection = Axis.vertical,
this.reverse = false,
this.padding,
bool? primary,
this.physics,
this.controller,
this.child,
this.dragStartBehavior = DragStartBehavior.start,
this.clipBehavior = Clip.hardEdge,
this.restorationId,
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
}) : assert(scrollDirection != null),
assert(dragStartBehavior != null),
assert(clipBehavior != null),
assert(!(controller != null && primary == true),
),
primary = primary ?? controller == null && identical(scrollDirection, Axis.vertical),
super(key: key);
用途:竖向布局的页面,例如一个页面可能在有些手机尺寸下不能放下或者是偏手机底部有输入框的页面。
需要注意的是,通常**SingleChildScrollView**
只应在期望的内容不会超过屏幕太多时使用,这是因为SingleChildScrollView
不支持基于Sliver
的延迟加载模型。ListView
ListView({
...
//可滚动widget公共参数
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController? controller,
bool? primary,
ScrollPhysics? physics,
EdgeInsetsGeometry? padding,
//ListView各个构造函数的共同参数
double? itemExtent,
Widget? prototypeItem,
bool shrinkWrap = false,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
double? cacheExtent, // 预渲染区域长度
//子widget列表
List<Widget> children = const <Widget>[],
})
在具体的项目中应用
ListView.builder(
itemExtent: 49,
padding: EdgeInsets.only(bottom: 80),
itemCount: vm.buildingList.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {},
child: Container(),
);
},
)
itemExtent
指的是ListView
在其主轴方向上的长度。prototypeItem
指的是列表子项长度相同但是不确定,这时候可以指定一个widget
,但是和上面的itemExtent
相斥。children
参数,接收一个widget的数组,当然这个时候就可以把它当作SingleChildScrollView
来使用了。这个就类似ios中的UITableView
的作用了,更多负责视图的竖直列表。GridView
组件是构建一个二维网格列表,类似于iOS中的
UICollectionView
。GridView({
Key? key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController? controller,
bool? primary,
ScrollPhysics? physics,
bool shrinkWrap = false,
EdgeInsetsGeometry? padding,
required this.gridDelegate,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
double? cacheExtent,
List<Widget> children = const <Widget>[],
...
})
SliverGridDelegate
可以理解成组件的排列协议,主要是约束该组件的排列形式。SliverGridDelegateWithFixedCrossAxisCount({
@required double crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
double childAspectRatio = 1.0,
})
crossAxisCount
:横轴子元素的数量。mainAxisSpacing
:主轴方向的间距。crossAxisSpacing
:横轴方向子元素的间距。childAspectRatio
:子元素在横轴长度和主轴长度的比例。
当然当间距之间的数值都为0时,就可以用这个写法。
GridView.count(
crossAxisCount: 3,
childAspectRatio: 1.0,
children: <Widget>[],
);
用法:
GridView.builder(
...
required SliverGridDelegate gridDelegate,
required IndexedWidgetBuilder itemBuilder,
)
一个负责GridView
的样式,一个负责子Widget
的布局样式。
PageView
PageView({
Key? key,
this.scrollDirection = Axis.horizontal, // 滑动方向
this.reverse = false,
PageController? controller,
this.physics,
List<Widget> children = const <Widget>[],
this.onPageChanged,
//每次滑动是否强制切换整个页面,如果为false,则会根据实际的滑动距离显示页面
this.pageSnapping = true,
this.allowImplicitScrolling = false,
this.padEnds = true,
})
使用场景,无定时器的swiper页面,可以滑动切换的页面。allowImplicitScrolling
表示可以缓存两页数据,前后两页。
TabBarView
这个组件是对PageView
的封装。
TabBarView({
Key? key,
required this.children, // tab 页
this.controller, // TabController
this.physics,
this.dragStartBehavior = DragStartBehavior.start,
})
一般和TabBar
一起使用,例如:
return Material(
child: Container(
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
alignment: Alignment.center,
width: MediaQuery.of(context).size.width,
color: Colors.white,
child: TabBar(
indicatorColor: AppColors.primaryColor,
indicatorSize: TabBarIndicatorSize.label,
labelColor: AppColors.primaryColor,
labelStyle: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w800,
),
unselectedLabelColor: AppColors.textLevel3,
controller: _tabController,
tabs: widget.vm.tabs
.map((e) => Tab(
text: e,
))
.toList(),
),
),
Expanded(
child: TabBarView(
controller: _tabController,
children: [
],
),
),
],
),
),
);
TabBar
负责头部的文本绘制,包括底部的横线;TabController
主要起到TabBar
和TabBarView
中内容的桥梁;TabBarView
则是单个页面内容。
CustomScrollView
使用场景:一个页面中包含多个可滑动的组件,例如ListView``GridView
等。column
包裹的控件,会发现只会在各自的区域内滚动,而CustomScrollView
组件来帮助我们创建一个公共的 Scrollable 和 Viewport ,然后它的 slivers 参数接受一个 Sliver 数组,这样我们就可以使用CustomScrollView 方面的实现我们期望的功能了。这种控件是针对可滑动区域是整个页面的,例如一些首页的长视图页面。
var listView = SliverFixedExtentList(
itemExtent: 56,
delegate: SliverChildBuilderDelegate(
(_, index) => ListTile(title: Text('$index')),
childCount: 10,
),
);
// 使用
return CustomScrollView(
slivers: [
listView,
listView,
],
);
Sliver名称 | 功能 | 对应的可滚动组件 |
---|---|---|
SliverList | 列表 | ListView |
SliverFixedExtentList | 高度固定的列表 | ListView,指定itemExtent时 |
SliverAnimatedList | 添加/删除列表项可以执行动画 | AnimatedList |
SliverGrid | 网格 | GridView |
SliverPrototypeExtentList | 根据原型生成高度固定的列表 | ListView,指定prototypeItem 时 |
SliverFillViewport | 包含多个子组件,每个都可以填满屏幕 | PageView |
除了和列表对应的 Sliver 之外还有一些用于对 Sliver 进行布局、装饰的组件,它们的子组件必须是 Sliver,我们列举几个常用的:
Sliver名称 | 对应 RenderBox |
---|---|
SliverPadding | Padding |
SliverVisibility、SliverOpacity | Visibility、Opacity |
SliverFadeTransition | FadeTransition |
SliverLayoutBuilder | LayoutBuilder |
还有一些其它常用的 Sliver:
Sliver名称 | 说明 |
---|---|
SliverAppBar | 对应 AppBar,主要是为了在 CustomScrollView 中使用。 |
SliverToBoxAdapter | 一个适配器,可以将 RenderBox 适配为 Sliver,后面介绍。 |
SliverPersistentHeader | 滑动到顶部时可以固定住。 |
这个作用更类似于iOS中的UIScorllerView
。