今天是Scroll篇最後一天
講講滾動綜藝大集合的神器
一樣先上圖

本節內容引用自Flutter實戰一書 6.1 可滚动组件简介 與 6.5 CustomScrollView
通常可滚动组件的子组件可能会非常多、占用的总高度也会非常大;如果要一次性将子组件全部构建出将会非常昂贵!
为此,Flutter中提出一个Sliver(中文为“薄片”的意思)概念,如果一个可滚动组件支持Sliver模型,那么该滚动可以将子组件分成好多个“薄片”(Sliver),只有当Sliver出现在视口中时才会去构建它,这种模型也称为“基于Sliver的延迟构建模型”。
可滚动组件中有很多都支持基于Sliver的延迟构建模型,如ListView、GridView,但是也有不支持该模型的,如SingleChildScrollView。
也就是說ScrollView有分兩種:會 跟 不會 lazy的
會lazy的cell在Flutter裡的世界就叫做Sliver(不是Silver喔XDDD)
我們之前提到的children跟builder,只有builder裡的Widget才算Sliver
实际上Sliver版的可滚动组件和非Sliver版的可滚动组件
最大的区别就是前者不包含滚动模型(自身不能再滚动),而后者包含滚动模型
也正因如此,CustomScrollView才可以将多个Sliver"粘"在一起,这些Sliver共用CustomScrollView的Scrollable。
SliverPadding、SliverAppBar等是和可滚动组件无关的,它们主要是为了结合CustomScrollView一起使用
NSCollectionLayoutGroup
在很多布局系统中都有ViewPort的概念,在Flutter中,术语ViewPort(视口),如无特别说明,则是指一个Widget的实际显示区域。例如,一个ListView的显示区域高度是800像素,虽然其列表项总高度可能远远超过800像素,但是其ViewPort仍然是800像素。
感覺應該就是Frame☘️☘️☘️
class LessonPageCustomScrollView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final space = 10.0;
    final tintColor = Colors.pinkAccent.withOpacity(0.5);
    final horizontalKey = new GlobalKey();
    void scrollToTop() {
      Scrollable.ensureVisible(horizontalKey.currentContext);
    }
    Widget _createCell(String title) {
      return Card(
        color: tintColor,
        child: InkWell(
          child: Container(
            alignment: Alignment.center,
            padding: EdgeInsets.all(space),
            child: Text(title, textAlign: TextAlign.center,
              style: TextStyle(
                color: Colors.white,
                fontSize: 24,
                fontWeight: FontWeight.w500
              )
            )
          ),
          onTap: title.startsWith("horizontal") ? scrollToTop : null,
        )
      );
    }
    Widget buildNoArrowRefreshIndicator(
        BuildContext context,
        RefreshIndicatorMode refreshState,
        double pulledExtent,
        double refreshTriggerPullDistance,
        double refreshIndicatorExtent,
        ) {
      const Curve opacityCurve = Interval(0.4, 0.8, curve: Curves.easeInOut);
      return Align(
        alignment: Alignment.bottomCenter,
        child: Padding(
          padding: const EdgeInsets.only(bottom: 16.0),
          child: refreshState == RefreshIndicatorMode.drag
              ? Opacity(
            opacity: opacityCurve.transform(
                min(pulledExtent / refreshTriggerPullDistance, 1.0)
            ),
            child: Icon(
              CupertinoIcons.car_detailed,
              color: CupertinoDynamicColor.resolve(CupertinoColors.inactiveGray, context),
              size: 36.0,
            ),
          )
              : Opacity(
            opacity: opacityCurve.transform(
                min(pulledExtent / refreshIndicatorExtent, 1.0)
            ),
            child: const CupertinoActivityIndicator(radius: 14.0),
          ),
        ),
      );
    }
    return Material(
      child: CustomScrollView(
        slivers: <Widget>[
          //頂
          SliverAppBar(
            backgroundColor: tintColor,
            pinned: false,
            expandedHeight: 200,
            flexibleSpace: FlexibleSpaceBar(
              title: Text("第十三堂課"),
              background: Image.asset("resource/images/fantasy_unicorn.jpg",
                fit: BoxFit.cover
              ),
            ),
          ),
          //拉
          CupertinoSliverRefreshControl(
            builder: buildNoArrowRefreshIndicator,
            onRefresh: () async {
              print("拉爽的");
            },
          ),
          //上
          SliverPadding(
            padding: EdgeInsets.only(top: space, left: space, right: space),
            sliver: SliverGrid(
              delegate: SliverChildBuilderDelegate((ctx, idx){
                return _createCell("Grid\n$idx");
              }, childCount: 10),
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                mainAxisSpacing: space,
                crossAxisSpacing: space,
                childAspectRatio: 1
              )
            ),
          ),
          //中
          SliverFixedExtentList(
            key: horizontalKey,
            itemExtent: 100,
            delegate: SliverChildBuilderDelegate ((ctx, idx) {
              return ListView.builder(
                scrollDirection: Axis.horizontal,
                itemCount: 10,
                itemBuilder: (ctx, idx) {
                  return _createCell("horizontal\n$idx");
              });
            }, childCount: 1 )
          ),
          //下
          SliverList(
            delegate: SliverChildBuilderDelegate ((ctx, idx) {
              return _createCell("list $idx");
            }, childCount: 10 )
          )
        ],
      ),
    );
  }
}
下集預告:底部導航與頁籤
