Saltar al contenido principal

AlertDialog scrollable (Ya no está depreciado)

AlertDialog debería hacer scroll automáticamente cuando se desborda.

Resumen

#

Un AlertDialog ahora hace scroll automáticamente cuando se desborda.

Contexto

#

Antes de este cambio, cuando el contenido del widget de un AlertDialog era demasiado alto, la pantalla se desbordaba, lo que provocaba que el contenido se recortara. Esto dio lugar a los siguientes problemas:

  • No había forma de ver la parte del contenido que quedaba recortada.
  • La mayoría de los alert dialogs tienen botones debajo del contenido para solicitar acciones a los usuarios. Si el contenido se desbordaba, ocultando los botones, los usuarios podrían no percatarse de su existencia.

Descripción del cambio

#

El enfoque anterior listaba consecutivamente los widgets de título y contenido en un widget Column.

dart
Column(
  mainAxisSize: MainAxisSize.min,
  crossAxisAlignment: CrossAxisAlignment.stretch,
  children: <Widget>[
    if (title != null)
      Padding(
        padding: titlePadding ?? EdgeInsets.fromLTRB(24, 24, 24, content == null ? 20 : 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,
        ),
      ),
    ),
    // ...
  ],
);

El nuevo enfoque envuelve ambos widgets en un SingleChildScrollView por encima de la barra de botones, haciendo que ambos widgets formen parte del mismo scrollable y exponiendo la barra de botones en la parte inferior del diálogo.

dart
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,
         ],
       ),
     ),
   // ...
  ],
),

Guía de migración

#

Podrías ver los siguientes problemas como resultado de este cambio:

Las pruebas de Semantics podrían fallar debido a la adición de un SingleChildScrollView.

Las pruebas manuales de las funciones Talkback y VoiceOver demuestran que siguen exhibiendo el mismo comportamiento (correcto) que antes.

Las pruebas golden podrían fallar.

Este cambio podría haber causado diferencias en pruebas golden (que antes pasaban), dado que el SingleChildScrollView ahora anida los widgets de título y contenido. Algunos proyectos de Flutter han optado por crear pruebas de Semantics tomando capturas golden de los nodos de Semantics utilizados en la compilación de depuración de Flutter.


Cualquier actualización de golden de Semantics que refleje la adición del contenedor de desplazamiento es esperada y estas diferencias deberían ser seguras de aceptar.

Ejemplo del árbol de Semantics resultante:

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"
Podrían producirse cambios de diseño (layout) debido a la vista de desplazamiento.

Si el diálogo ya se estaba desbordando, este cambio corrige el problema. Este cambio de diseño es esperado.


Un SingleChildScrollView anidado en AlertDialog.content debería funcionar correctamente si se deja en el código, pero debería eliminarse si no es intencionado, ya que podría causar confusión.

Código antes de la migración:

dart
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: () {}),
  ],
)

Código después de la migración:

dart
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: () {}),
  ],
)

Timeline

#

Lanzado en la versión: 1.16.3
En versión estable: 1.17

Referencias

#

Doc de diseño:

Documentación de la API:

Problema relevante:

PRs relevantes: