在清單頂部放置一個浮動的 app bar

為了方便使用者檢視清單,你可能希望在使用者向下捲動清單時隱藏 app bar,尤其在你的 app bar 特別高,導致它佔據了很多豎向空間的時候。

一般情況下,你可以透過給 Scaffold 元件設定一個 appBar 屬性來建立一個 app bar。這個 app bar 會始終固定在 Scaffold 元件的 body 上方。

把 app bar 從 Scaffold 元件挪到一個 CustomScrollView 裡,可以讓你建立一個隨著你滑動 CustomScrollView 裡清單的同時在螢幕外自動隨之捲動的 app bar。

下面這篇教程將介紹如何透過 CustomScrollView 來生成一個帶有隨著使用者滑動清單同時會在螢幕外隨之捲動的 app bar 的清單。

  1. 建立一個 CustomScrollView

  2. 透過 SliverAppBar 來新增一個浮動的 app bar

  3. 透過 SliverList 來新增清單

1. 建立一個 CustomScrollView

要建立一個浮動的 app bar,你需要將 app bar 放在一個包含清單的 CustomScrollView 裡,這會同步 app bar 和清單的捲動位置。你可以把 CustomScrollView 元件當成一個可以讓你把不同型別的可捲動清單和元件混合對應在一起的 ListView

可以放在 CustomScrollView 裡的可捲動清單和元件我們稱之為 slivers。有幾種型別的 slivers,比如 SliverListSliverGridSliverAppBar。實際上,ListViewGridView 元件底層使用的就是 SliverListSliverGrid

以下例子示範了建立一個包含 SliverAppBarSliverListCustomScrollView。另外你需要刪除你之前可能設定在 Scaffold 元件上的 app bar!

Scaffold(
  // No appBar property provided, only the body.
  body: CustomScrollView(
      // Add the app bar and list of items as slivers in the next steps.
      slivers: <Widget>[]),
);

2. 使用 SliverAppBar 來新增一個浮動的 app bar

接下來為 CustomScrollView 新增一個 app bar。 Flutter 提供開箱即用的 SliverAppBar 元件,與普通的 AppBar 元件非常相似,你可以使用 SliverAppBar 來顯示標題、標籤、影象等內容。

同時,SliverAppBar 元件也提供一種建立『浮動』 app bar 的能力,當用戶向下捲動清單時,app bar 會隨之在螢幕外捲動。此外,你可以設定 SliverAppBar 在使用者捲動時縮小或展開。

要達到這個效果:

  1. 先建立一個只顯示標題的 app bar

  2. floating 屬性設為 true,這使用戶在向上捲動清單時能快速顯示 app bar。

  3. 新增一個 flexibleSpace 元件,這個元件將填充可用的 expandedHeight

CustomScrollView(
  slivers: [
    // Add the app bar to the CustomScrollView.
    const SliverAppBar(
      // Provide a standard title.
      title: Text(title),
      // Allows the user to reveal the app bar if they begin scrolling
      // back up the list of items.
      floating: true,
      // Display a placeholder widget to visualize the shrinking size.
      flexibleSpace: Placeholder(),
      // Make the initial height of the SliverAppBar larger than normal.
      expandedHeight: 200,
    ),
  ],
)

3. 使用 SliverList 來新增一個清單

現在你已經建立好一個 app bar,接下來應該給 CustomScrollView 新增一個清單。你有兩種選擇:選擇 SliverList 或者 SliverGrid。如果你需要一個一個往下排地顯示清單中的內容,應該用 SliverList 元件。如果需要網格狀地顯示清單中的內容,應該用 SliverGrid 元件。

SliverListSliverGrid 元件都需要一個必要引數:SliverChildDelegate。雖然聽起來很花稍,但它只是用來給清單元件 SliverListSliverGrid 提供一個代理。例如,SliverChildBuilderDelegate 允許你建立一組可以在捲動時延遲載入的清單項,就和 ListView.builder 元件差不多。

// Next, create a SliverList
SliverList(
  // Use a delegate to build items as they're scrolled on screen.
  delegate: SliverChildBuilderDelegate(
    // The builder function returns a ListTile with a title that
    // displays the index of the current item.
    (context, index) => ListTile(title: Text('Item #$index')),
    // Builds 1000 ListTiles
    childCount: 1000,
  ),
)

互動式範例

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    const title = 'Floating App Bar';

    return MaterialApp(
      title: title,
      home: Scaffold(
        // No appbar provided to the Scaffold, only a body with a
        // CustomScrollView.
        body: CustomScrollView(
          slivers: [
            // Add the app bar to the CustomScrollView.
            const SliverAppBar(
              // Provide a standard title.
              title: Text(title),
              // Allows the user to reveal the app bar if they begin scrolling
              // back up the list of items.
              floating: true,
              // Display a placeholder widget to visualize the shrinking size.
              flexibleSpace: Placeholder(),
              // Make the initial height of the SliverAppBar larger than normal.
              expandedHeight: 200,
            ),
            // Next, create a SliverList
            SliverList(
              // Use a delegate to build items as they're scrolled on screen.
              delegate: SliverChildBuilderDelegate(
                // The builder function returns a ListTile with a title that
                // displays the index of the current item.
                (context, index) => ListTile(title: Text('Item #$index')),
                // Builds 1000 ListTiles
                childCount: 1000,
              ),
            ),
          ],
        ),
      ),
    );
  }
}