Flutter吸附效果如何實現?

ershixiong發表於2019-06-13

本文多圖預警,請注意在wifi下觀看O(∩_∩)O~~
很多軟體都有吸附頂部的效果,一圖勝千言(這裡偷懶,使用的是簡書中的圖)。

吸附效果
而在android中一般是使用 AppBarLayout + CoordinatorLayout + CollapsingToolbarLayout來實現的,那麼在flutter中, 這個效果該如何實現呢?
這裡我們先看一個概念,叫 CustomScrollView。

CustomScrollView

官方文件是這樣解釋的。一個使用slivers來建立自定義滾動效果的ScrollView
CustomScrollView讓你直接提供 slivers來建立不同的滾動效果,比如lists,grids 以及 expanding headers。
如果要建立一個包含 可伸展的appbar ,後面跟著list 和grid 這樣的滾動檢視,可以使用這三種slivers:SliverAppBarSliverAppBarSliverGrid
在這些slivers中的widget必須產生 RenderSliver物件。
為了控制這些滾動檢視的初始滾動偏移量,可以通過 controller和他的 ScrollController.initialScrollOffset屬性來設定。

下面的例子展示了 scroll view包含 flexible pinned app bar, 一個grid 和一個無限的list。

class CustomWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        SliverAppBar(
            pinned: true,
            expandedHeight: 150.0,
            flexibleSpace: const FlexibleSpaceBar(
              title: Text('Available seats'),
            ),
            actions: <Widget>[
              IconButton(
                icon: const Icon(Icons.add_circle),
                tooltip: 'Add new entry',
                onPressed: () {
                  /* ... */
                },
              ),
            ]),
        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'),
              );
            },
          ),
        ),
      ],
    );
  }
}
複製程式碼

效果圖見下方SliverAppBar的圖,兩個合在一起展示了。

SliverAppBar

定義: 配套 CustomScrollView使用的material design app bar。
一個app bar由 一個 toolbar 和 其他潛在的widgets組成,比如 TabBarFlexibleSpaceBar。 App bars 通常會 通過IconButtons來暴露一個或者多個common actions,後面也可選的跟PopupMenuButton,可以做一些常用操作。
Sliver app bars 通常被用作 CustomScrollView的第一個孩子,可以根據scroll view中其他孩子的滾動偏移量來動態調整高度。 如果要使用一個固定高度的app bar 可以看AppBar, 用在Scaffold.appBar這一塊。
這個AppBar展示一些toolbar widgets,leading(最左側的按鈕)、title以及actions,都在bottom之上。如果flexibleSpace widget也被指定內容了,它將會被放在 toolbar 和 bottom widget 之下。
下面這段程式碼可以被CustomScrollView的[CustomScrollView.slivers]使用:

 SliverAppBar(
   expandedHeight: 150.0,
   flexibleSpace: const FlexibleSpaceBar(
     title: Text('Available seats'),
   ),
   actions: <Widget>[
     IconButton(
       icon: const Icon(Icons.add_circle),
       tooltip: 'Add new entry',
       onPressed: () { /* ... */ },
     ),
   ]
 )
複製程式碼

將這段程式碼放入到上面CustomScrollView的樣例程式碼中,效果如下:

SliverAppBar

下面的內容主要是細緻分析 SliverAppBar建構函式中 floatingsnappinned取不同的值對於互動效果的影響變化。

動畫樣例

App bar -- [floating]: false, [pinned]: false, [snap]: false:

app_bar

現象描述:

  • 向上滑動時: AppBar 延伸區域先收起來->appbar滑出去->list滑動出去
  • 向下滑動時: list滑動回來->appbar滑回來->延伸區域滑回來

App bar -- [floating]: true, [pinned]: false, [snap]: false:

app_bar_floating

現象描述:

  • 向上滑動時: AppBar 延伸區域先收起來->appbar滑出去->list滑動出去
  • 向下滑動時: appbar滑回來->延伸區域滑回來->list滑回來

App bar -- [floating]: true, [pinned]: false, [snap]: true:

app_bar_floating_snap

現象描述:

  • 向上滑動時: AppBar 延伸區域先收起來->appbar滑出去->list滑動出去
  • 向下滑動時: appbar滑回來->延伸區域在檢測到有輕微下滑的加速度就會動畫形式直接展開蓋在list之上->list滑回來

App bar -- [floating]: true, [pinned]: true, [snap]: false:

app_bar_pinned_floating

現象描述:

  • 向上滑動時: AppBar 延伸區域先收起來->list滑動出去
  • 向下滑動時: 延伸區域滑回來->list滑回來

App bar -- [floating]: true, [pinned]: true, [snap]: true:

app_bar_pinned_floating_snap

現象描述:

  • 向上滑動時: AppBar 延伸區域先收起來->list滑動出去
  • 向下滑動時: 延伸區域在檢測到有輕微下滑的加速度就會動畫形式直接展開蓋在list之上->list滑回來

App bar with [floating]: false, [pinned]: true, [snap]: false:

app_bar_pinned

現象描述:

  • 向上滑動時: AppBar 延伸區域先收起來->list滑動出去
  • 向下滑動時: list滑回來->延伸區域滑回來

snap屬性只能在float屬性為true的情況下設定.
總結下來
pinned最容易理解,是否固定appbar不滑出螢幕。
floating改變了下滑時候appbar出現優先順序的順序,appbar(以及延伸區域)出現的優先順序>list的優先順序。
snap要結合floating,檢測到下滑的輕微加速度就會以動畫形式將appbar完全展開蓋在list之上。
這裡留意到一個現象是在慢速持續的向下滑動時,snap的值true和false差別不大。
本文樣例分支程式碼已上傳github ,分支CustomScrollView分支。


如果你覺得這篇文章對你有益,還請幫忙轉發和點贊,萬分感謝。

Flutter爛筆頭

您的關注將是我堅持的動力源泉,再次感謝。

相關文章