概述

#

Scaffold 中的 SnackBar API 現在由 ScaffoldMessenger 處理,而 ScaffoldMessenger 預設在 MaterialApp 的上下文中提供一個。

背景

#

在此更改之前,SnackBar 會透過呼叫當前 BuildContext 內的 Scaffold 來顯示。透過呼叫 Scaffold.of(context).showSnackBar,當前 Scaffold 會將 SnackBar 動畫化顯示。這隻適用於當前的 Scaffold,並且在 SnackBar 顯示過程中路由發生變化時不會在路由之間持久化。如果 showSnackBar 在執行非同步事件的過程中被呼叫,並且 BuildContext 由於路由更改和 Scaffold 被銷燬而失效,也會導致錯誤。

ScaffoldMessenger 現在負責處理 SnackBar,以便在路由之間持久化並在當前 Scaffold 上始終顯示。預設情況下,MaterialApp 中包含一個根 ScaffoldMessenger,但您可以建立自己的 ScaffoldMessenger 受控範圍,以進一步控制哪些 Scaffold 會接收您的 SnackBar

變更說明

#

以前的方法是呼叫 Scaffold 來顯示 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'),
      );
    },
  )
);

新的方法是呼叫 ScaffoldMessenger 來顯示 SnackBar。在這種情況下,不再需要 Builder 來提供一個具有“位於”Scaffold“之下”的 BuildContext 的新作用域。

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'),
  ),
);

在過渡期間顯示 SnackBar 時,SnackBar 會完成 Hero 動畫,平滑地移動到下一頁。

ScaffoldMessenger 建立一個作用域,其中所有後代 Scaffold 都會註冊以接收 SnackBar,這就是它們在這些過渡中持久化的方式。當使用 MaterialApp 提供的根 ScaffoldMessenger 時,所有後代 Scaffold 都會接收 SnackBar,除非在樹的更下方建立了新的 ScaffoldMessenger 作用域。透過例項化您自己的 ScaffoldMessenger,您可以根據應用程式的上下文控制哪些 Scaffold 接收 SnackBar,哪些不接收。

方法 debugCheckHasScaffoldMessenger 可用於斷言給定的上下文具有 ScaffoldMessenger 祖先。嘗試在沒有 ScaffoldMessenger 祖先的情況下顯示 SnackBar 會導致斷言,如下所示:

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.

遷移指南

#

遷移前的程式碼

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);

遷移後的程式碼

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);

時間線

#

釋出於版本:1.23.0-13.0.pre
穩定版本:2.0.0

參考資料

#

API 文件

相關問題

相關 PR