桌面上的默认 `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