06 多子元素滑动布局
滑动布局作为 “多子元素布局” 的另一个分支,如 ListView
、GridView
、Pageview
,它们在实现上要复杂的多,从下图一个的流程上我们大致可以知道它们的关系:
由上图我们可以知道,流程最终回产生两个 RenderObject :
RenderSliver
:Base class for the render objects that implement scroll effects in viewports.RenderViewport
:A render object that is bigger on the inside.
/// [RenderViewport] cannot contain [RenderBox] children directly. Instead, use
/// a [RenderSliverList], [RenderSliverFixedExtentList], [RenderSliverGrid], or
/// a [RenderSliverToBoxAdapter], for example.
并且从 RenderViewport
的说明我们知道,RenderViewport
内部是不能直接放置 RenderBox
,需要通过 RenderSliver
大家族来完成布局。而从源码可知:RenderViewport
对应的 Widget Viewport
就是一个 MultiChildRenderObjectWidget
。 (你看,又回到 MultiChildRenderObjectWidget
了吧。)
再稍微说下上图的流程:
ListView
、Pageview
、GridView
等都是通过Scrollable
、ViewPort
、Sliver
大家族实现的效果。这里简单不规范描述就是:一个“可滑动”的控件,嵌套了一个“视觉窗口”,然后内部通过“碎片”展示 children 。- 不同的是
PageView
没有继承SrollView
,而是直接通过NotificationListener
和ScrollNotification
嵌套实现。
注意
TabBarView
内部就是:NotificationListener
+PageView
是不是觉得少了什么?哈哈哈,有的有的,官方同样提供了解决“?疼”的自定义滑动 CustomScrollView,
它继承了 ScrollView,
可通过 slivers 参数实现布局,这些 slivers
最终回通过 Scrollable
的 buildViewport
添加到 ViewPort
中,如下代码所示:
CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 20,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('list item $index'),
);
},
),
),
],
)
学员评价