桌面上的預設 `PrimaryScrollController`
概述
#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。
// 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 類的新成員 automaticallyInheritForPlatforms 和 scrollDirection 在 shouldInherit 中進行評估,使使用者能夠清晰地控制 PrimaryScrollController 的行為。
預設情況下,會為移動平臺維護向後相容性。PrimaryScrollController.shouldInherit 對垂直 ScrollView 返回 true。在桌面上,預設情況下此值為 false。
// 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 來顯式管理單個 ScrollView 的 PrimaryScrollController。對於跨多個 ScrollView 的行為,現在可以透過設定特定平臺以及首選繼承的滾動方向來配置 PrimaryScrollController。
使用 PrimaryScrollController 的小部件,如 NestedScrollView、Scrollbar 和 DropdownMenuButton,其現有功能將不會發生變化。iOS 滾動到頂部等功能也將繼續按預期工作,無需進行遷移。
在桌面上,只有 ScrollAction 和 ScrollIntent 類會受到此更改的影響,需要進行遷移。預設情況下,如果當前 Focus 包含在 Scrollable 中,PrimaryScrollController 將用於執行回退鍵盤滾動 Shortcuts。由於在桌面上並排顯示多個 ScrollView 很常見,因此 Flutter 無法決定“在此檢視中哪個 ScrollView 應該是主要的,並接收鍵盤滾動操作?”
在此更改之前,如果存在多個 ScrollView,則會丟擲相同的斷言(ScrollController attached to multiple ScrollViews.)。現在,在桌面平臺上,使用者需要指定 primary: true 來指定哪個 ScrollView 是接收未處理鍵盤 Shortcuts 的回退。
遷移指南
#遷移前的程式碼
// 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');
}
),
),
]
);
},
),
);遷移後的程式碼
// 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