可滚动的 AlertDialog (不再弃用)

概述

AlertDialog 现在会在绘制溢出时自动变为可滚动状态。

上下文

在此更改之前,当 AlertDialog widget 中的内容过高时,会显示绘制溢出,使内容被剪裁。这会导致以下问题:

  • 无法查看被剪裁的内容。

  • 大多数 AlertDialog 的内容下方都有按钮,用于提示用户执行操作。如果内容溢出,遮盖了按钮,用户可能不知道它们的存在。

更改描述

在改动前,可以使用下面的方法在 Column widget 中连续地列出标题和内容 widget。

Column(
  mainAxisSize: MainAxisSize.min,
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    if (title != null)
      Padding(
        padding: titlePadding ?? EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0),
        child: DefaultTextStyle(
          style: titleTextStyle ?? dialogTheme.titleTextStyle ?? theme.textTheme.title,
          child: Semantics(
          child: title,
          namesRoute: true,
          container: true,
          ),
        ),
      ),
    if (content != null)
      Flexible(
        child: Padding(
        padding: contentPadding,
        child: DefaultTextStyle(
          style: contentTextStyle ?? dialogTheme.contentTextStyle ?? theme.textTheme.subhead,
          child: content,
        ),
      ),
    ),
    // ...
  ],
);

在改动后,它们被包裹在按钮栏上方的 SingleChildScrollView 中,作为同一个可滚动的模块的一部分,按钮栏将显示在对话框底部。

Column(
  mainAxisSize: MainAxisSize.min,
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    if (title != null || content != null)
      SingleChildScrollView(
        child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
         children: <Widget>[
           if (title != null)
             titleWidget,
             if (content != null)
             contentWidget,
         ],
       ),
     ),
   // ...
  ],
),

迁移指南

此更改可能会导致以下问题:

Semantics tests might fail because of the addition of a SingleChildScrollView.
Manual testing of the Talkback and VoiceOver features show that they still exhibit the same (correct) behavior as before.

由于添加了 SingleChildScrollView,语义测试可能会失败。
TalkbackVoiceOver 功能的手动测试表明,它们仍然表现出与以前相同的(正确的)行为。

Golden tests might fail.
This change might have caused diffs in (previously passing) golden tests since the SingleChildScrollView now nests both the title and content widgets. Some Flutter projects have taken to creating semantics tests by taking goldens of semantics nodes used in Flutter’s debug build.

Golden Test 可能会失败。
由于 SingleChildScrollView 现在嵌套了标题和内容 widget,因此可能导致 Golden Test 出现不同的结果。一些 Flutter 项目已经开始通过获取 Flutter debug 构建过程中使用的语义节点 goldens 来创建语义测试。


滚动容器上附加的所有语义 golden 变动都是符合预期的,它们不会造成其他后果。

语义树示例:

flutter:        ├─SemanticsNode#30 <-- SingleChildScrollView
flutter:          │ flags: hasImplicitScrolling
flutter:          │ scrollExtentMin: 0.0
flutter:          │ scrollPosition: 0.0
flutter:          │ scrollExtentMax: 0.0
flutter:          │
flutter:          ├─SemanticsNode#31 <-- title
flutter:          │   flags: namesRoute
flutter:          │   label: "Hello"
flutter:          │
flutter:          └─SemanticsNode#32 <-- contents
flutter:              label: "Huge content"
Layout changes might result because of the scroll view.
If the dialog was already overflowing, this change corrects the problem. This layout change is expected.

滚动视图可能导致布局更改。
该变更会修复对话框发生绘制溢出的问题。这种布局上的变化是意料之中的。


当代码中有 SingleChildScrollView 嵌套在 AlertDialog.content 时,那么对话框应正常展示。但如果不是有意为之,则应将其移除,否则可能导致代码更为不可读。

迁移前的代码:

AlertDialog(
  title: Text(
    'Very, very large title that is also scrollable',
    textScaleFactor: 5,
  ),
  content: SingleChildScrollView( // won't be scrollable
    child: Text('Scrollable content', textScaleFactor: 5),
  ),
  actions: <Widget>[
    TextButton(child: Text('Button 1'), onPressed: () {}),
    TextButton(child: Text('Button 2'), onPressed: () {}),
  ],
)

迁移后的代码:

AlertDialog(
  title: Text('Very, very large title', textScaleFactor: 5),
  content: Text('Very, very large content', textScaleFactor: 5),
  actions: <Widget>[
    TextButton(child: Text('Button 1'), onPressed: () {}),
    TextButton(child: Text('Button 2'), onPressed: () {}),
  ],
)

时间线

发布于版本:1.16.3
发布于稳定版本:1.17

参考文献

设计文档

API 文档:

相关 issue:

相关 PRs: