跳至主要內容

桌機上的預設 `PrimaryScrollController`

摘要

#

PrimaryScrollController API 已更新,不再自動附加到桌機平台上的垂直 ScrollView

上下文

#

在此變更之前,如果 ScrollView 具有 Axis.vertical 滾動方向且尚未提供 ScrollController,則 ScrollView.primary 將預設為 true。這使得 iOS 上的捲動到頂部功能等常見 UI 模式可以直接用於 Flutter 應用程式。然而,在桌機上,此預設值通常會導致以下斷言錯誤:

ScrollController attached to multiple ScrollViews.

雖然行動應用程式一次顯示一個 ScrollView 很常見,但桌機 UI 模式更可能並排顯示多個 ScrollViewPrimaryScrollController 之前的實作與此模式衝突,導致出現通常沒有幫助的錯誤訊息。為了補救此問題,PrimaryScrollController 已更新,新增了其他參數,並改善了多個依賴它的 Widget 的錯誤訊息。

變更說明

#

ScrollView 之前的實作導致在所有平台上,所有沒有 ScrollController 的垂直 ScrollViewprimary 預設為 true。這種預設行為並非總是清晰,特別是因為它與 PrimaryScrollController 本身是分開的。

dart
// Previously, this ListView would always result in primary being true,
// and attached to the PrimaryScrollController on all platforms.
Scaffold(
  body: ListView.builder(
    itemBuilder: (BuildContext context, int index) {
      return Text('Item $index');
    }
  ),
);

實作變更 ScrollView.primary 為可為 null,並將後備決策移至 PrimaryScrollController。當 primary 為 null 且未提供 ScrollController 時,ScrollView 將查找 PrimaryScrollController 並改為呼叫 shouldInherit 以確定給定的 ScrollView 是否應使用 PrimaryScrollController

PrimaryScrollController 類別的新成員 automaticallyInheritForPlatformsscrollDirection,會在 shouldInherit 中評估,讓使用者清楚且可控制 PrimaryScrollController 的行為。

預設情況下,行動平台的向後相容性會維持。PrimaryScrollController.shouldInherit 對於垂直 ScrollView 回傳 true。在桌機上,此預設值回傳 false。

dart
// Only on mobile platforms will this attach to the PrimaryScrollController by
// default.
Scaffold(
  body: ListView.builder(
    itemBuilder: (BuildContext context, int index) {
      return Text('Item $index');
    }
  ),
);

若要變更預設值,使用者可以將 ScrollView.primary 設定為 true 或 false,以明確管理個別 ScrollViewPrimaryScrollController。對於多個 ScrollView 的行為,現在可以透過設定特定平台以及繼承偏好的滾動方向來配置 PrimaryScrollController

使用 PrimaryScrollController 的 Widget,例如 NestedScrollViewScrollbarDropdownMenuButton,現有功能不會發生任何變更。諸如 iOS 捲動到頂部等功能也將繼續正常運作,而無需任何遷移。

桌機上的 ScrollActionScrollIntent 是此變更唯一影響的類別,需要遷移。預設情況下,如果目前的 Focus 包含在 Scrollable 中,則 PrimaryScrollController 會用於執行後備鍵盤滾動 Shortcuts。由於在桌機平台上並排顯示多個 ScrollView 很常見,因此 Flutter 無法決定「此視圖中哪個 ScrollView 應該是主要的並接收鍵盤滾動動作?」

如果在此變更之前存在多個 ScrollView,則會拋出相同的斷言 (ScrollController attached to multiple ScrollViews.)。現在,在桌機平台上,使用者需要指定 primary: true,以指定哪個 ScrollView 是接收未處理鍵盤 Shortcuts 的後備。

遷移指南

#

遷移前的程式碼

dart
// These side-by-side ListViews would throw errors from Scrollbars and
// ScrollActions previously due to the PrimaryScrollController.
Scaffold(
  body: LayoutBuilder(
    builder: (context, constraints) {
      return Row(
        children: [
          SizedBox(
            height: constraints.maxHeight,
            width: constraints.maxWidth / 2,
            child: ListView.builder(
              itemBuilder: (BuildContext context, int index) {
                return Text('List 1 - Item $index');
              }
            ),
          ),
          SizedBox(
            height: constraints.maxHeight,
            width: constraints.maxWidth / 2,
            child: ListView.builder(
              itemBuilder: (BuildContext context, int index) {
                return Text('List 2 - Item $index');
              }
            ),
          ),
        ]
      );
    },
  ),
);

遷移後的程式碼

dart
// These side-by-side ListViews will no longer throw errors, but for
// default ScrollActions, one will need to be designated as primary.
Scaffold(
  body: LayoutBuilder(
    builder: (context, constraints) {
      return Row(
        children: [
          SizedBox(
            height: constraints.maxHeight,
            width: constraints.maxWidth / 2,
            child: ListView.builder(
              // This ScrollView will use the PrimaryScrollController
              primary: true,
              itemBuilder: (BuildContext context, int index) {
                return Text('List 1 - Item $index');
              }
            ),
          ),
          SizedBox(
            height: constraints.maxHeight,
            width: constraints.maxWidth / 2,
            child: ListView.builder(
              itemBuilder: (BuildContext context, int index) {
                return Text('List 2 - Item $index');
              }
            ),
          ),
        ]
      );
    },
  ),
);

時程表

#

已於版本中發佈:3.3.0-0.0.pre
在穩定版本中:3.3

參考資料

#

API 文件

設計文件

相關問題

相關 PR