Unverified Commit 64c845c5 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Re-land ScaffoldMessenger (#66504)

parent 7f2ca5e5
...@@ -15,6 +15,7 @@ import 'floating_action_button.dart'; ...@@ -15,6 +15,7 @@ import 'floating_action_button.dart';
import 'icons.dart'; import 'icons.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'page.dart'; import 'page.dart';
import 'scaffold.dart' show ScaffoldMessenger, ScaffoldMessengerState;
import 'theme.dart'; import 'theme.dart';
/// [MaterialApp] uses this [TextStyle] as its [DefaultTextStyle] to encourage /// [MaterialApp] uses this [TextStyle] as its [DefaultTextStyle] to encourage
...@@ -166,6 +167,7 @@ class MaterialApp extends StatefulWidget { ...@@ -166,6 +167,7 @@ class MaterialApp extends StatefulWidget {
const MaterialApp({ const MaterialApp({
Key? key, Key? key,
this.navigatorKey, this.navigatorKey,
this.scaffoldMessengerKey,
this.home, this.home,
this.routes = const <String, WidgetBuilder>{}, this.routes = const <String, WidgetBuilder>{},
this.initialRoute, this.initialRoute,
...@@ -214,6 +216,7 @@ class MaterialApp extends StatefulWidget { ...@@ -214,6 +216,7 @@ class MaterialApp extends StatefulWidget {
/// Creates a [MaterialApp] that uses the [Router] instead of a [Navigator]. /// Creates a [MaterialApp] that uses the [Router] instead of a [Navigator].
const MaterialApp.router({ const MaterialApp.router({
Key? key, Key? key,
this.scaffoldMessengerKey,
this.routeInformationProvider, this.routeInformationProvider,
required this.routeInformationParser, required this.routeInformationParser,
required this.routerDelegate, required this.routerDelegate,
...@@ -263,6 +266,14 @@ class MaterialApp extends StatefulWidget { ...@@ -263,6 +266,14 @@ class MaterialApp extends StatefulWidget {
/// {@macro flutter.widgets.widgetsApp.navigatorKey} /// {@macro flutter.widgets.widgetsApp.navigatorKey}
final GlobalKey<NavigatorState>? navigatorKey; final GlobalKey<NavigatorState>? navigatorKey;
/// A key to use when building the [ScaffoldMessenger].
///
/// If a [scaffoldMessengerKey] is specified, the [ScaffoldMessenger] can be
/// directly manipulated without first obtaining it from a [BuildContext] via
/// [ScaffoldMessenger.of]: from the [scaffoldMessengerKey], use the
/// [GlobalKey.currentState] getter.
final GlobalKey<ScaffoldMessengerState>? scaffoldMessengerKey;
/// {@macro flutter.widgets.widgetsApp.home} /// {@macro flutter.widgets.widgetsApp.home}
final Widget? home; final Widget? home;
...@@ -724,7 +735,9 @@ class _MaterialAppState extends State<MaterialApp> { ...@@ -724,7 +735,9 @@ class _MaterialAppState extends State<MaterialApp> {
} }
theme ??= widget.theme ?? ThemeData.light(); theme ??= widget.theme ?? ThemeData.light();
return AnimatedTheme( return ScaffoldMessenger(
key: widget.scaffoldMessengerKey,
child: AnimatedTheme(
data: theme, data: theme,
isMaterialAppTheme: true, isMaterialAppTheme: true,
child: widget.builder != null child: widget.builder != null
...@@ -745,6 +758,7 @@ class _MaterialAppState extends State<MaterialApp> { ...@@ -745,6 +758,7 @@ class _MaterialAppState extends State<MaterialApp> {
}, },
) )
: child!, : child!,
)
); );
} }
......
...@@ -135,7 +135,7 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate { ...@@ -135,7 +135,7 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate {
/// icon: const Icon(Icons.add_alert), /// icon: const Icon(Icons.add_alert),
/// tooltip: 'Show Snackbar', /// tooltip: 'Show Snackbar',
/// onPressed: () { /// onPressed: () {
/// scaffoldKey.currentState.showSnackBar(snackBar); /// ScaffoldMessenger.of(context).showSnackBar(snackBar);
/// }, /// },
/// ), /// ),
/// IconButton( /// IconButton(
......
...@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart'; ...@@ -7,7 +7,7 @@ import 'package:flutter/widgets.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart'; import 'material_localizations.dart';
import 'scaffold.dart' show Scaffold; import 'scaffold.dart' show Scaffold, ScaffoldMessenger;
/// Asserts that the given context has a [Material] ancestor. /// Asserts that the given context has a [Material] ancestor.
/// ///
...@@ -123,3 +123,34 @@ bool debugCheckHasScaffold(BuildContext context) { ...@@ -123,3 +123,34 @@ bool debugCheckHasScaffold(BuildContext context) {
}()); }());
return true; return true;
} }
/// Asserts that the given context has a [ScaffoldMessenger] ancestor.
///
/// Used by various widgets to make sure that they are only used in an
/// appropriate context.
///
/// To invoke this function, use the following pattern, typically in the
/// relevant Widget's build method:
///
/// ```dart
/// assert(debugCheckHasScaffoldMessenger(context));
/// ```
///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasScaffoldMessenger(BuildContext context) {
assert(() {
if (context.findAncestorWidgetOfExactType<ScaffoldMessenger>() == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('No ScaffoldMessenger widget found.'),
ErrorDescription('${context.widget.runtimeType} widgets require a ScaffoldMessenger widget ancestor.'),
...context.describeMissingAncestor(expectedAncestorType: ScaffoldMessenger),
ErrorHint(
'Typically, the ScaffoldMessenger widget is introduced by the MaterialApp '
'at the top of your application widget tree.'
)
]);
}
return true;
}());
return true;
}
...@@ -32,7 +32,7 @@ const Curve _snackBarFadeOutCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlo ...@@ -32,7 +32,7 @@ const Curve _snackBarFadeOutCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlo
/// Specify how a [SnackBar] was closed. /// Specify how a [SnackBar] was closed.
/// ///
/// The [ScaffoldState.showSnackBar] function returns a /// The [ScaffoldMessengerState.showSnackBar] function returns a
/// [ScaffoldFeatureController]. The value of the controller's closed property /// [ScaffoldFeatureController]. The value of the controller's closed property
/// is a Future that resolves to a SnackBarClosedReason. Applications that need /// is a Future that resolves to a SnackBarClosedReason. Applications that need
/// to know how a snackbar was closed can use this value. /// to know how a snackbar was closed can use this value.
...@@ -40,7 +40,7 @@ const Curve _snackBarFadeOutCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlo ...@@ -40,7 +40,7 @@ const Curve _snackBarFadeOutCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlo
/// Example: /// Example:
/// ///
/// ```dart /// ```dart
/// Scaffold.of(context).showSnackBar( /// ScaffoldMessenger.of(context).showSnackBar(
/// SnackBar( ... ) /// SnackBar( ... )
/// ).closed.then((SnackBarClosedReason reason) { /// ).closed.then((SnackBarClosedReason reason) {
/// ... /// ...
...@@ -57,10 +57,10 @@ enum SnackBarClosedReason { ...@@ -57,10 +57,10 @@ enum SnackBarClosedReason {
swipe, swipe,
/// The snack bar was closed by the [ScaffoldFeatureController] close callback /// The snack bar was closed by the [ScaffoldFeatureController] close callback
/// or by calling [ScaffoldState.hideCurrentSnackBar] directly. /// or by calling [ScaffoldMessengerState.hideCurrentSnackBar] directly.
hide, hide,
/// The snack bar was closed by an call to [ScaffoldState.removeCurrentSnackBar]. /// The snack bar was closed by an call to [ScaffoldMessengerState.removeCurrentSnackBar].
remove, remove,
/// The snack bar was closed because its timer expired. /// The snack bar was closed because its timer expired.
...@@ -150,8 +150,8 @@ class _SnackBarActionState extends State<SnackBarAction> { ...@@ -150,8 +150,8 @@ class _SnackBarActionState extends State<SnackBarAction> {
/// ///
/// {@youtube 560 315 https://www.youtube.com/watch?v=zpO6n_oZWw0} /// {@youtube 560 315 https://www.youtube.com/watch?v=zpO6n_oZWw0}
/// ///
/// To display a snack bar, call `Scaffold.of(context).showSnackBar()`, passing /// To display a snack bar, call `ScaffoldMessenger.of(context).showSnackBar()`,
/// an instance of [SnackBar] that describes the message. /// passing an instance of [SnackBar] that describes the message.
/// ///
/// To control how long the [SnackBar] remains visible, specify a [duration]. /// To control how long the [SnackBar] remains visible, specify a [duration].
/// ///
...@@ -160,11 +160,11 @@ class _SnackBarActionState extends State<SnackBarAction> { ...@@ -160,11 +160,11 @@ class _SnackBarActionState extends State<SnackBarAction> {
/// ///
/// See also: /// See also:
/// ///
/// * [Scaffold.of], to obtain the current [ScaffoldState], which manages the /// * [ScaffoldMessenger.of], to obtain the current [ScaffoldMessengerState],
/// display and animation of snack bars. /// which manages the display and animation of snack bars.
/// * [ScaffoldState.showSnackBar], which displays a [SnackBar]. /// * [ScaffoldMessengerState.showSnackBar], which displays a [SnackBar].
/// * [ScaffoldState.removeCurrentSnackBar], which abruptly hides the currently /// * [ScaffoldMessengerState.removeCurrentSnackBar], which abruptly hides the
/// displayed snack bar, if any, and allows the next to be displayed. /// currently displayed snack bar, if any, and allows the next to be displayed.
/// * [SnackBarAction], which is used to specify an [action] button to show /// * [SnackBarAction], which is used to specify an [action] button to show
/// on the snack bar. /// on the snack bar.
/// * [SnackBarThemeData], to configure the default property values for /// * [SnackBarThemeData], to configure the default property values for
...@@ -293,7 +293,7 @@ class SnackBar extends StatefulWidget { ...@@ -293,7 +293,7 @@ class SnackBar extends StatefulWidget {
/// ///
/// See also: /// See also:
/// ///
/// * [ScaffoldState.removeCurrentSnackBar], which abruptly hides the /// * [ScaffoldMessengerState.removeCurrentSnackBar], which abruptly hides the
/// currently displayed snack bar, if any, and allows the next to be /// currently displayed snack bar, if any, and allows the next to be
/// displayed. /// displayed.
/// * <https://material.io/design/components/snackbars.html> /// * <https://material.io/design/components/snackbars.html>
...@@ -305,7 +305,7 @@ class SnackBar extends StatefulWidget { ...@@ -305,7 +305,7 @@ class SnackBar extends StatefulWidget {
/// Called the first time that the snackbar is visible within a [Scaffold]. /// Called the first time that the snackbar is visible within a [Scaffold].
final VoidCallback? onVisible; final VoidCallback? onVisible;
// API for Scaffold.showSnackBar(): // API for ScaffoldMessengerState.showSnackBar():
/// Creates an animation controller useful for driving a snack bar's entrance and exit animation. /// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
static AnimationController createAnimationController({ required TickerProvider vsync }) { static AnimationController createAnimationController({ required TickerProvider vsync }) {
...@@ -342,7 +342,6 @@ class SnackBar extends StatefulWidget { ...@@ -342,7 +342,6 @@ class SnackBar extends StatefulWidget {
State<SnackBar> createState() => _SnackBarState(); State<SnackBar> createState() => _SnackBarState();
} }
class _SnackBarState extends State<SnackBar> { class _SnackBarState extends State<SnackBar> {
bool _wasVisible = false; bool _wasVisible = false;
...@@ -560,6 +559,9 @@ class _SnackBarState extends State<SnackBar> { ...@@ -560,6 +559,9 @@ class _SnackBarState extends State<SnackBar> {
); );
} }
return ClipRect(child: snackBarTransition); return Hero(
child: ClipRect(child: snackBarTransition),
tag: '<SnackBar Hero tag - ${widget.content}>',
);
} }
} }
...@@ -510,6 +510,7 @@ class AnimatedListState extends State<AnimatedList> with TickerProviderStateMixi ...@@ -510,6 +510,7 @@ class AnimatedListState extends State<AnimatedList> with TickerProviderStateMixi
/// class _SliverAnimatedListSampleState extends State<SliverAnimatedListSample> { /// class _SliverAnimatedListSampleState extends State<SliverAnimatedListSample> {
/// final GlobalKey<SliverAnimatedListState> _listKey = GlobalKey<SliverAnimatedListState>(); /// final GlobalKey<SliverAnimatedListState> _listKey = GlobalKey<SliverAnimatedListState>();
/// final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); /// final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
/// final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
/// ListModel<int> _list; /// ListModel<int> _list;
/// int _selectedItem; /// int _selectedItem;
/// int _nextItem; // The next item inserted when the user presses the '+' button. /// int _nextItem; // The next item inserted when the user presses the '+' button.
...@@ -567,7 +568,7 @@ class AnimatedListState extends State<AnimatedList> with TickerProviderStateMixi ...@@ -567,7 +568,7 @@ class AnimatedListState extends State<AnimatedList> with TickerProviderStateMixi
/// _selectedItem = null; /// _selectedItem = null;
/// }); /// });
/// } else { /// } else {
/// _scaffoldKey.currentState.showSnackBar(SnackBar( /// _scaffoldMessengerKey.currentState.showSnackBar(SnackBar(
/// content: Text( /// content: Text(
/// 'Select an item to remove from the list.', /// 'Select an item to remove from the list.',
/// style: TextStyle(fontSize: 20), /// style: TextStyle(fontSize: 20),
...@@ -579,6 +580,7 @@ class AnimatedListState extends State<AnimatedList> with TickerProviderStateMixi ...@@ -579,6 +580,7 @@ class AnimatedListState extends State<AnimatedList> with TickerProviderStateMixi
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return MaterialApp( /// return MaterialApp(
/// scaffoldMessengerKey: _scaffoldMessengerKey,
/// home: Scaffold( /// home: Scaffold(
/// key: _scaffoldKey, /// key: _scaffoldKey,
/// body: CustomScrollView( /// body: CustomScrollView(
......
...@@ -2112,7 +2112,7 @@ typedef ElementVisitor = void Function(Element element); ...@@ -2112,7 +2112,7 @@ typedef ElementVisitor = void Function(Element element);
/// widget can be used: the build context passed to the [Builder.builder] /// widget can be used: the build context passed to the [Builder.builder]
/// callback will be that of the [Builder] itself. /// callback will be that of the [Builder] itself.
/// ///
/// For example, in the following snippet, the [ScaffoldState.showSnackBar] /// For example, in the following snippet, the [ScaffoldState.showBottomSheet]
/// method is called on the [Scaffold] widget that the build method itself /// method is called on the [Scaffold] widget that the build method itself
/// creates. If a [Builder] had not been used, and instead the `context` /// creates. If a [Builder] had not been used, and instead the `context`
/// argument of the build method itself had been used, no [Scaffold] would have /// argument of the build method itself had been used, no [Scaffold] would have
...@@ -2123,19 +2123,38 @@ typedef ElementVisitor = void Function(Element element); ...@@ -2123,19 +2123,38 @@ typedef ElementVisitor = void Function(Element element);
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// // here, Scaffold.of(context) returns null /// // here, Scaffold.of(context) returns null
/// return Scaffold( /// return Scaffold(
/// appBar: AppBar(title: Text('Demo')), /// appBar: const AppBar(title: Text('Demo')),
/// body: Builder( /// body: Builder(
/// builder: (BuildContext context) { /// builder: (BuildContext context) {
/// return TextButton( /// return TextButton(
/// child: Text('BUTTON'), /// child: const Text('BUTTON'),
/// onPressed: () { /// onPressed: () {
/// // here, Scaffold.of(context) returns the locally created Scaffold /// Scaffold.of(context).showBottomSheet<void>(
/// Scaffold.of(context).showSnackBar(SnackBar( /// (BuildContext context) {
/// content: Text('Hello.') /// return Container(
/// )); /// alignment: Alignment.center,
/// } /// height: 200,
/// color: Colors.amber,
/// child: Center(
/// child: Column(
/// mainAxisSize: MainAxisSize.min,
/// children: <Widget>[
/// const Text('BottomSheet'),
/// ElevatedButton(
/// child: const Text('Close BottomSheet'),
/// onPressed: () {
/// Navigator.pop(context),
/// },
/// )
/// ],
/// ),
/// ),
/// ); /// );
/// } /// },
/// );
/// },
/// );
/// },
/// ) /// )
/// ); /// );
/// } /// }
......
...@@ -168,6 +168,8 @@ void main() { ...@@ -168,6 +168,8 @@ void main() {
' _InheritedTheme\n' ' _InheritedTheme\n'
' Theme\n' ' Theme\n'
' AnimatedTheme\n' ' AnimatedTheme\n'
' _ScaffoldMessengerScope\n'
' ScaffoldMessenger\n'
' Builder\n' ' Builder\n'
' DefaultTextStyle\n' ' DefaultTextStyle\n'
' CustomPaint\n' ' CustomPaint\n'
...@@ -204,4 +206,83 @@ void main() { ...@@ -204,4 +206,83 @@ void main() {
' or WidgetsApp widget at the top of your application widget tree.\n', ' or WidgetsApp widget at the top of your application widget tree.\n',
)); ));
}); });
testWidgets('debugCheckHasScaffoldMessenger control test', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final GlobalKey<ScaffoldMessengerState> _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
final SnackBar snackBar = SnackBar(
content: const Text('Snack'),
action: SnackBarAction(label: 'Test', onPressed: () {})
);
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: ScaffoldMessenger(
key: _scaffoldMessengerKey,
child: Builder(
builder: (BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Container(),
);
}
)
),
)
));
final List<dynamic> exceptions = <dynamic>[];
final FlutterExceptionHandler oldHandler = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails details) {
exceptions.add(details.exception);
};
// ScaffoldMessenger shows SnackBar.
_scaffoldMessengerKey.currentState.showSnackBar(snackBar);
await tester.pumpAndSettle();
// Pump widget to rebuild without ScaffoldMessenger
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: Scaffold(
key: _scaffoldKey,
body: Container(),
),
),
));
// The Scaffold should assert we still have an ancestor ScaffoldMessenger in
// order to dismiss the SnackBar from the ScaffoldMessenger.
await tester.tap(find.text('Test'));
FlutterError.onError = oldHandler;
expect(exceptions.length, 1);
expect(exceptions.single.runtimeType, FlutterError);
final FlutterError error = exceptions.first as FlutterError;
expect(error.diagnostics.length, 5);
expect(error.diagnostics[2], isA<DiagnosticsProperty<Element>>());
expect(error.diagnostics[3], isA<DiagnosticsBlock>());
expect(error.diagnostics[4].level, DiagnosticLevel.hint);
expect(
error.diagnostics[4].toStringDeep(),
equalsIgnoringHashCodes(
'Typically, the ScaffoldMessenger widget is introduced by the\n'
'MaterialApp at the top of your application widget tree.\n',
),
);
expect(error.toStringDeep(), equalsIgnoringHashCodes(
'FlutterError\n'
' No ScaffoldMessenger widget found.\n'
' Scaffold widgets require a ScaffoldMessenger widget ancestor.\n'
' The specific widget that could not find a ScaffoldMessenger\n'
' ancestor was:\n'
' Scaffold-[LabeledGlobalKey<ScaffoldState>#00829]\n'
' The ancestors of this widget were:\n'
' MediaQuery\n'
' Directionality\n'
' [root]\n'
' Typically, the ScaffoldMessenger widget is introduced by the\n'
' MaterialApp at the top of your application widget tree.\n'
));
});
} }
...@@ -649,7 +649,7 @@ void main() { ...@@ -649,7 +649,7 @@ void main() {
builder: (BuildContext context) { builder: (BuildContext context) {
return FloatingActionButton( return FloatingActionButton(
onPressed: () { onPressed: () {
Scaffold.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Snacky!')), const SnackBar(content: Text('Snacky!')),
); );
}, },
......
...@@ -2034,6 +2034,117 @@ void main() { ...@@ -2034,6 +2034,117 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });
}); });
testWidgets('ScaffoldMessenger.of can return null', (WidgetTester tester) async {
ScaffoldMessengerState scaffoldMessenger;
const Key tapTarget = Key('tap-target');
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: Scaffold(
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
scaffoldMessenger = ScaffoldMessenger.of(context, nullOk: true);
},
behavior: HitTestBehavior.opaque,
child: Container(
height: 100.0,
width: 100.0,
key: tapTarget,
),
);
}
),
),
),
));
await tester.tap(find.byKey(tapTarget));
await tester.pump();
expect(scaffoldMessenger, isNull);
});
testWidgets('ScaffoldMessenger.of will assert if !nullOk', (WidgetTester tester) async {
const Key tapTarget = Key('tap-target');
final List<dynamic> exceptions = <dynamic>[];
final FlutterExceptionHandler oldHandler = FlutterError.onError;
FlutterError.onError = (FlutterErrorDetails details) {
exceptions.add(details.exception);
};
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: Scaffold(
body: Builder(
builder: (BuildContext context) {
return GestureDetector(
onTap: () {
ScaffoldMessenger.of(context);
},
behavior: HitTestBehavior.opaque,
child: Container(
height: 100.0,
width: 100.0,
key: tapTarget,
),
);
}
),
),
),
));
await tester.tap(find.byKey(tapTarget));
FlutterError.onError = oldHandler;
expect(exceptions.length, 1);
expect(exceptions.single.runtimeType, FlutterError);
final FlutterError error = exceptions.first as FlutterError;
expect(error.diagnostics.length, 5);
expect(error.diagnostics[2], isA<DiagnosticsProperty<Element>>());
expect(error.diagnostics[3], isA<DiagnosticsBlock>());
expect(error.diagnostics[4].level, DiagnosticLevel.hint);
expect(
error.diagnostics[4].toStringDeep(),
equalsIgnoringHashCodes(
'Typically, the ScaffoldMessenger widget is introduced by the\n'
'MaterialApp at the top of your application widget tree.\n',
),
);
expect(error.toStringDeep(), equalsIgnoringHashCodes(
'FlutterError\n'
' No ScaffoldMessenger widget found.\n'
' Builder widgets require a ScaffoldMessenger widget ancestor.\n'
' The specific widget that could not find a ScaffoldMessenger\n'
' ancestor was:\n'
' Builder\n'
' The ancestors of this widget were:\n'
' _BodyBuilder\n'
' MediaQuery\n'
' LayoutId-[<_ScaffoldSlot.body>]\n'
' CustomMultiChildLayout\n'
' AnimatedBuilder\n'
' DefaultTextStyle\n'
' AnimatedDefaultTextStyle\n'
' _InkFeatures-[GlobalKey#342d0 ink renderer]\n'
' NotificationListener<LayoutChangedNotification>\n'
' PhysicalModel\n'
' AnimatedPhysicalModel\n'
' Material\n'
' PrimaryScrollController\n'
' _ScaffoldScope\n'
' Scaffold\n'
' MediaQuery\n'
' Directionality\n'
' [root]\n'
' Typically, the ScaffoldMessenger widget is introduced by the\n'
' MaterialApp at the top of your application widget tree.\n'
));
});
} }
class _GeometryListener extends StatefulWidget { class _GeometryListener extends StatefulWidget {
......
...@@ -73,7 +73,7 @@ void main() { ...@@ -73,7 +73,7 @@ void main() {
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Scaffold.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text(text), content: const Text(text),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}), action: SnackBarAction(label: 'ACTION', onPressed: () {}),
...@@ -111,7 +111,7 @@ void main() { ...@@ -111,7 +111,7 @@ void main() {
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Scaffold.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text(text), content: const Text(text),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
action: SnackBarAction(label: action, onPressed: () {}), action: SnackBarAction(label: action, onPressed: () {}),
...@@ -155,7 +155,7 @@ void main() { ...@@ -155,7 +155,7 @@ void main() {
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Scaffold.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
elevation: elevation, elevation: elevation,
shape: shape, shape: shape,
...@@ -202,7 +202,7 @@ void main() { ...@@ -202,7 +202,7 @@ void main() {
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Scaffold.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('I am a snack bar.'), content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}), action: SnackBarAction(label: 'ACTION', onPressed: () {}),
...@@ -244,7 +244,7 @@ void main() { ...@@ -244,7 +244,7 @@ void main() {
builder: (BuildContext context) { builder: (BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Scaffold.of(context).showSnackBar(SnackBar( ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: const Text('I am a snack bar.'), content: const Text('I am a snack bar.'),
duration: const Duration(seconds: 2), duration: const Duration(seconds: 2),
action: SnackBarAction(label: 'ACTION', onPressed: () {}), action: SnackBarAction(label: 'ACTION', onPressed: () {}),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment