Saltar al contenido principal

SnackBars administrados por el ScaffoldMessenger

Los SnackBars ahora son gestionados por el ScaffoldMessenger, y persisten a través de las rutas.

Resumen

#

La API de SnackBar dentro del Scaffold ahora es manejada por el ScaffoldMessenger, uno de los cuales está disponible por defecto dentro del contexto de un MaterialApp.

Contexto

#

Antes de este cambio, los SnackBars se mostraban llamando al Scaffold dentro del BuildContext actual. Al llamar a Scaffold.of(context).showSnackBar, el Scaffold actual animaba un SnackBar a la vista. Esto solo se aplicaba al Scaffold actual, y no persistía a través de las rutas si estas cambiaban durante la presentación del SnackBar. Esto también provocaba errores si se llamaba a showSnackBar durante la ejecución de un evento asíncrono, y el BuildContext se invalidaba debido al cambio de ruta y al desecho (disposed) del Scaffold.

El ScaffoldMessenger ahora maneja los SnackBars para que persistan a través de las rutas y siempre se muestren en el Scaffold actual. Por defecto, se incluye un ScaffoldMessenger raíz en el MaterialApp, pero puedes crear tu propio alcance controlado para el ScaffoldMessenger para controlar mejor qué Scaffolds reciben tus SnackBars.

Descripción del cambio

#

El enfoque anterior requería del Scaffold para mostrar un SnackBar.

dart
Scaffold(
  key: scaffoldKey,
  body: Builder(
    builder: (BuildContext context) {
      return GestureDetector(
        onTap: () {
          Scaffold.of(context).showSnackBar(SnackBar(
            content: const Text('snack'),
            duration: const Duration(seconds: 1),
            action: SnackBarAction(
              label: 'ACTION',
              onPressed: () { },
            ),
          ));
        },
        child: const Text('SHOW SNACK'),
      );
    },
  )
);

El nuevo enfoque requiere del ScaffoldMessenger para mostrar el SnackBar. En este caso, ya no se requiere el Builder para proporcionar un nuevo alcance con un BuildContext que esté "debajo" del Scaffold.

dart
Scaffold(
  key: scaffoldKey,
  body: GestureDetector(
    onTap: () {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: const Text('snack'),
        duration: const Duration(seconds: 1),
        action: SnackBarAction(
          label: 'ACTION',
          onPressed: () { },
        ),
      ));
    },
    child: const Text('SHOW SNACK'),
  ),
);

Al presentar un SnackBar durante una transición, el SnackBar completa una animación Hero, moviéndose suavemente a la página siguiente.

El ScaffoldMessenger crea un alcance en el que todos los Scaffolds descendientes se registran para recibir SnackBars, que es como persisten a través de estas transiciones. Al usar el ScaffoldMessenger raíz provisto por el MaterialApp, todos los Scaffolds descendientes reciben SnackBars, a menos que se cree un nuevo alcance de ScaffoldMessenger más abajo en el árbol. Al instanciar tu propio ScaffoldMessenger, puedes controlar qué Scaffolds reciben SnackBars y cuáles no, según el contexto de tu aplicación.

El método debugCheckHasScaffoldMessenger está disponible para asegurar que un contexto dado tenga un ancestro ScaffoldMessenger. Intentar presentar un SnackBar sin un ancestro ScaffoldMessenger presente da como resultado una aserción como la siguiente:

No ScaffoldMessenger widget found.
Scaffold widgets require a ScaffoldMessenger widget ancestor.
Typically, the ScaffoldMessenger widget is introduced by the MaterialApp
at the top of your application widget tree.

Guía de migración

#

Código antes de la migración:

dart
// The ScaffoldState of the current context was used for managing SnackBars.
Scaffold.of(context).showSnackBar(mySnackBar);
Scaffold.of(context).hideCurrentSnackBar(mySnackBar);
Scaffold.of(context).removeCurrentSnackBar(mySnackBar);

// If a Scaffold.key is specified, the ScaffoldState can be directly
// accessed without first obtaining it from a BuildContext via
// Scaffold.of. From the key, use the GlobalKey.currentState
// getter. This was previously used to manage SnackBars.
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
Scaffold(
  key: scaffoldKey,
  body: ...,
);

scaffoldKey.currentState.showSnackBar(mySnackBar);
scaffoldKey.currentState.hideCurrentSnackBar(mySnackBar);
scaffoldKey.currentState.removeCurrentSnackBar(mySnackBar);

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

dart
// The ScaffoldMessengerState of the current context is used for managing SnackBars.
ScaffoldMessenger.of(context).showSnackBar(mySnackBar);
ScaffoldMessenger.of(context).hideCurrentSnackBar(mySnackBar);
ScaffoldMessenger.of(context).removeCurrentSnackBar(mySnackBar);

// If a ScaffoldMessenger.key is specified, the ScaffoldMessengerState can be directly
// accessed without first obtaining it from a BuildContext via
// ScaffoldMessenger.of. From the key, use the GlobalKey.currentState
// getter. This is used to manage SnackBars.
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
ScaffoldMessenger(
  key: scaffoldMessengerKey,
  child: ...
)

scaffoldMessengerKey.currentState.showSnackBar(mySnackBar);
scaffoldMessengerKey.currentState.hideCurrentSnackBar(mySnackBar);
scaffoldMessengerKey.currentState.removeCurrentSnackBar(mySnackBar);

// The root ScaffoldMessenger can also be accessed by providing a key to
// MaterialApp.scaffoldMessengerKey. This way, the ScaffoldMessengerState can be directly accessed
// without first obtaining it from a BuildContext via ScaffoldMessenger.of. From the key, use
// the GlobalKey.currentState getter.
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
MaterialApp(
  scaffoldMessengerKey: rootScaffoldMessengerKey,
  home: ...
)

rootScaffoldMessengerKey.currentState.showSnackBar(mySnackBar);
rootScaffoldMessengerKey.currentState.hideCurrentSnackBar(mySnackBar);
rootScaffoldMessengerKey.currentState.removeCurrentSnackBar(mySnackBar);

Timeline

#

Introducido en la versión: 1.23.0-13.0.pre
En el lanzamiento estable: 2.0.0

Referencias

#

Documentación de la API:

Issues relevantes:

PRs relevantes: