SnackBars managed by the ScaffoldMessenger
Summary
#The SnackBar API within the Scaffold is now handled by the
ScaffoldMessenger, one of which is
available by default within the context of a MaterialApp.
Context
#Prior to this change, SnackBars would be shown by calling
on the Scaffold within the current BuildContext.
By calling Scaffold.of(context).showSnackBar,
the current Scaffold would animate a SnackBar into view.
This would only apply to the current Scaffold,
and would not persist across routes if they were changed
in the course of the SnackBars presentation.
This would also lead to errors if showSnackBar
would be called in the course of executing an
asynchronous event, and the BuildContext became invalidated
by the route changing and the Scaffold being disposed of.
The ScaffoldMessenger now handles SnackBars in order to
persist across routes and always be displayed on the current Scaffold.
By default, a root ScaffoldMessenger is included in the MaterialApp,
but you can create your own controlled scope for the ScaffoldMessenger
to further control which Scaffolds receive your SnackBars.
Description of change
#The previous approach called upon the Scaffold to show a SnackBar.
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'),
);
},
)
);The new approach calls on the ScaffoldMessenger to show
the SnackBar. In this case, the Builder is no longer
required to provide a new scope with a BuildContext that
is "under" the Scaffold.
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'),
),
);When presenting a SnackBar during a transition,
the SnackBar completes a Hero animation,
moving smoothly to the next page.
The ScaffoldMessenger creates a scope in which all descendant
Scaffolds register to receive SnackBars,
which is how they persist across these transitions.
When using the root ScaffoldMessenger provided by the
MaterialApp, all descendant Scaffolds receive SnackBars,
unless a new ScaffoldMessenger scope is created further down the tree.
By instantiating your own ScaffoldMessenger,
you can control which Scaffolds receive SnackBars, and which are not,
based on the context of your application.
The method debugCheckHasScaffoldMessenger is available to assert
that a given context has a ScaffoldMessenger ancestor.
Trying to present a SnackBar without a ScaffoldMessenger ancestor
present results in an assertion such as the following:
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.Migration guide
#Code before migration:
// 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);Code after migration:
// 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
#Landed in version: 1.23.0-13.0.pre
In stable release: 2.0.0
References
#API documentation:
Relevant issues:
Relevant PRs:
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-04-04。 查看文档源码 或者 为本页面内容提出建议.