概述

#

PrimaryScrollController API 已更新,不再自動附加到桌面平臺的垂直 ScrollView

背景

#

在此更改之前,如果 ScrollView 的滾動方向為 Axis.vertical 且尚未提供 ScrollController,則 ScrollView.primary 預設設定為 true。這使得常見的 UI 模式(例如 iOS 上的滾動到頂部功能)能夠在 Flutter 應用中開箱即用。但在桌面上,此預設設定經常會導致以下斷言錯誤:

ScrollController attached to multiple ScrollViews.

雖然移動應用程式通常一次只顯示一個 ScrollView,但桌面 UI 模式更可能將多個 ScrollView 並排顯示。PrimaryScrollController 的先前實現與此模式衝突,導致出現一個通常無用的錯誤訊息。為解決此問題,PrimaryScrollController 已更新,增加了其他引數以及針對依賴它的多個小部件的更好的錯誤訊息。

變更說明

#

ScrollView 的先前實現導致所有垂直 ScrollView(在所有平臺上,只要未提供 ScrollController)的 primary 預設值為 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 改為可空型別,並將回退決策轉移到 PrimaryScrollController。當 primary 為 null 且未提供 ScrollController 時,ScrollView 將查詢 PrimaryScrollController,並呼叫 shouldInherit 來確定給定的 ScrollView 是否應使用 PrimaryScrollController

PrimaryScrollController 類的新成員 automaticallyInheritForPlatformsscrollDirectionshouldInherit 中進行評估,使使用者能夠清晰地控制 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 的小部件,如 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