Remove deprecated Scaffold SnackBar API (#98549)
This commit is contained in:
parent
1cf5c74009
commit
135bb5d4f2
@ -2017,195 +2017,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
||||
|
||||
// Used for both the snackbar and material banner APIs
|
||||
ScaffoldMessengerState? _scaffoldMessenger;
|
||||
bool? _accessibleNavigation;
|
||||
|
||||
// SNACKBAR API
|
||||
final Queue<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>> _snackBars = Queue<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>>();
|
||||
AnimationController? _snackBarController;
|
||||
Timer? _snackBarTimer;
|
||||
|
||||
/// [ScaffoldMessengerState.showSnackBar] shows a [SnackBar] at the bottom of
|
||||
/// the scaffold. This method should not be used, and will be deprecated in
|
||||
/// the near future..
|
||||
///
|
||||
/// A scaffold can show at most one snack bar at a time. If this function is
|
||||
/// called while another snack bar is already visible, the given snack bar
|
||||
/// will be added to a queue and displayed after the earlier snack bars have
|
||||
/// closed.
|
||||
///
|
||||
/// To control how long a [SnackBar] remains visible, use [SnackBar.duration].
|
||||
///
|
||||
/// To remove the [SnackBar] with an exit animation, use
|
||||
/// [ScaffoldMessengerState.hideCurrentSnackBar] or call
|
||||
/// [ScaffoldFeatureController.close] on the returned [ScaffoldFeatureController].
|
||||
/// To remove a [SnackBar] suddenly (without an animation), use
|
||||
/// [ScaffoldMessengerState.removeCurrentSnackBar].
|
||||
///
|
||||
/// See [ScaffoldMessenger.of] for information about how to obtain the
|
||||
/// [ScaffoldMessengerState].
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// Here is an example of showing a [SnackBar] when the user presses a button.
|
||||
///
|
||||
/// ** See code in examples/api/lib/material/scaffold/scaffold_state.show_snack_bar.0.dart **
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ScaffoldMessenger], this should be used instead to manage [SnackBar]s.
|
||||
@Deprecated(
|
||||
'Use ScaffoldMessenger.showSnackBar. '
|
||||
'This feature was deprecated after v1.23.0-14.0.pre.',
|
||||
)
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar(SnackBar snackbar) {
|
||||
_snackBarController ??= SnackBar.createAnimationController(vsync: this)
|
||||
..addStatusListener(_handleSnackBarStatusChange);
|
||||
if (_snackBars.isEmpty) {
|
||||
assert(_snackBarController!.isDismissed);
|
||||
_snackBarController!.forward();
|
||||
}
|
||||
late ScaffoldFeatureController<SnackBar, SnackBarClosedReason> controller;
|
||||
controller = ScaffoldFeatureController<SnackBar, SnackBarClosedReason>._(
|
||||
// We provide a fallback key so that if back-to-back snackbars happen to
|
||||
// match in structure, material ink splashes and highlights don't survive
|
||||
// from one to the next.
|
||||
snackbar.withAnimation(_snackBarController!, fallbackKey: UniqueKey()),
|
||||
Completer<SnackBarClosedReason>(),
|
||||
() {
|
||||
assert(_snackBars.first == controller);
|
||||
hideCurrentSnackBar();
|
||||
},
|
||||
null, // SnackBar doesn't use a builder function so setState() wouldn't rebuild it
|
||||
);
|
||||
setState(() {
|
||||
_snackBars.addLast(controller);
|
||||
});
|
||||
return controller;
|
||||
}
|
||||
|
||||
void _handleSnackBarStatusChange(AnimationStatus status) {
|
||||
switch (status) {
|
||||
case AnimationStatus.dismissed:
|
||||
assert(_snackBars.isNotEmpty);
|
||||
setState(() {
|
||||
_snackBars.removeFirst();
|
||||
});
|
||||
if (_snackBars.isNotEmpty)
|
||||
_snackBarController!.forward();
|
||||
break;
|
||||
case AnimationStatus.completed:
|
||||
setState(() {
|
||||
assert(_snackBarTimer == null);
|
||||
// build will create a new timer if necessary to dismiss the snack bar
|
||||
});
|
||||
break;
|
||||
case AnimationStatus.forward:
|
||||
case AnimationStatus.reverse:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// [ScaffoldMessengerState.removeCurrentSnackBar] removes the current
|
||||
/// [SnackBar] (if any) immediately. This method should not be used, and will
|
||||
/// be deprecated in the near future.
|
||||
///
|
||||
/// The removed snack bar does not run its normal exit animation. If there are
|
||||
/// any queued snack bars, they begin their entrance animation immediately.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ScaffoldMessenger], this should be used instead to manage [SnackBar]s.
|
||||
@Deprecated(
|
||||
'Use ScaffoldMessenger.removeCurrentSnackBar. '
|
||||
'This feature was deprecated after v1.23.0-14.0.pre.',
|
||||
)
|
||||
void removeCurrentSnackBar({ SnackBarClosedReason reason = SnackBarClosedReason.remove }) {
|
||||
assert(reason != null);
|
||||
|
||||
// SnackBars and SnackBarActions can call to hide and remove themselves, but
|
||||
// they are not aware of who presented them, the Scaffold or the
|
||||
// ScaffoldMessenger. As such, when the SnackBar classes call upon Scaffold
|
||||
// to remove (the current default), we should re-direct to the
|
||||
// ScaffoldMessenger here if that is where the SnackBar originated from.
|
||||
if (_messengerSnackBar != null) {
|
||||
// ScaffoldMessenger is presenting SnackBars.
|
||||
assert(debugCheckHasScaffoldMessenger(context));
|
||||
assert(
|
||||
_scaffoldMessenger != null,
|
||||
'A SnackBar was shown by the ScaffoldMessenger, but has been called upon '
|
||||
'to be removed from a Scaffold that is not registered with a '
|
||||
'ScaffoldMessenger, this can happen if a Scaffold has been rebuilt '
|
||||
'without an ancestor ScaffoldMessenger.',
|
||||
);
|
||||
_scaffoldMessenger!.removeCurrentSnackBar(reason: reason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_snackBars.isEmpty)
|
||||
return;
|
||||
final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
|
||||
if (!completer.isCompleted)
|
||||
completer.complete(reason);
|
||||
_snackBarTimer?.cancel();
|
||||
_snackBarTimer = null;
|
||||
_snackBarController!.value = 0.0;
|
||||
}
|
||||
|
||||
/// [ScaffoldMessengerState.hideCurrentSnackBar] removes the current
|
||||
/// [SnackBar] by running its normal exit animation. This method should not be
|
||||
/// used, and will be deprecated in the near future.
|
||||
///
|
||||
/// The closed completer is called after the animation is complete.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [ScaffoldMessenger], this should be used instead to manage [SnackBar]s.
|
||||
@Deprecated(
|
||||
'Use ScaffoldMessenger.hideCurrentSnackBar. '
|
||||
'This feature was deprecated after v1.23.0-14.0.pre.',
|
||||
)
|
||||
void hideCurrentSnackBar({ SnackBarClosedReason reason = SnackBarClosedReason.hide }) {
|
||||
assert(reason != null);
|
||||
|
||||
// SnackBars and SnackBarActions can call to hide and remove themselves, but
|
||||
// they are not aware of who presented them, the Scaffold or the
|
||||
// ScaffoldMessenger. As such, when the SnackBar classes call upon Scaffold
|
||||
// to remove (the current default), we should re-direct to the
|
||||
// ScaffoldMessenger here if that is where the SnackBar originated from.
|
||||
if (_messengerSnackBar != null) {
|
||||
// ScaffoldMessenger is presenting SnackBars.
|
||||
assert(debugCheckHasScaffoldMessenger(context));
|
||||
assert(
|
||||
_scaffoldMessenger != null,
|
||||
'A SnackBar was shown by the ScaffoldMessenger, but has been called upon '
|
||||
'to be removed from a Scaffold that is not registered with a '
|
||||
'ScaffoldMessenger, this can happen if a Scaffold has been rebuilt '
|
||||
'without an ancestor ScaffoldMessenger.',
|
||||
);
|
||||
_scaffoldMessenger!.hideCurrentSnackBar(reason: reason);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_snackBars.isEmpty || _snackBarController!.status == AnimationStatus.dismissed)
|
||||
return;
|
||||
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
final Completer<SnackBarClosedReason> completer = _snackBars.first._completer;
|
||||
if (mediaQuery.accessibleNavigation) {
|
||||
_snackBarController!.value = 0.0;
|
||||
completer.complete(reason);
|
||||
} else {
|
||||
_snackBarController!.reverse().then<void>((void value) {
|
||||
assert(mounted);
|
||||
if (!completer.isCompleted)
|
||||
completer.complete(reason);
|
||||
});
|
||||
}
|
||||
_snackBarTimer?.cancel();
|
||||
_snackBarTimer = null;
|
||||
}
|
||||
|
||||
// The _messengerSnackBar represents the current SnackBar being managed by
|
||||
// the ScaffoldMessenger, instead of the Scaffold.
|
||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? _messengerSnackBar;
|
||||
|
||||
// This is used to update the _messengerSnackBar by the ScaffoldMessenger.
|
||||
@ -2672,31 +2485,12 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
||||
_scaffoldMessenger = currentScaffoldMessenger;
|
||||
_scaffoldMessenger?._register(this);
|
||||
|
||||
// TODO(Piinks): Remove old SnackBar API after migrating ScaffoldMessenger
|
||||
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
// If we transition from accessible navigation to non-accessible navigation
|
||||
// and there is a SnackBar that would have timed out that has already
|
||||
// completed its timer, dismiss that SnackBar. If the timer hasn't finished
|
||||
// yet, let it timeout as normal.
|
||||
if ((_accessibleNavigation ?? false)
|
||||
&& !mediaQuery.accessibleNavigation
|
||||
&& _snackBarTimer != null
|
||||
&& !_snackBarTimer!.isActive) {
|
||||
hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
|
||||
}
|
||||
_accessibleNavigation = mediaQuery.accessibleNavigation;
|
||||
|
||||
_maybeBuildPersistentBottomSheet();
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// TODO(Piinks): Remove old SnackBar API after migrating ScaffoldMessenger
|
||||
_snackBarController?.dispose();
|
||||
_snackBarTimer?.cancel();
|
||||
_snackBarTimer = null;
|
||||
|
||||
_geometryNotifier.dispose();
|
||||
_floatingActionButtonMoveController.dispose();
|
||||
_floatingActionButtonVisibilityController.dispose();
|
||||
@ -2817,31 +2611,6 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
||||
final ThemeData themeData = Theme.of(context);
|
||||
final TextDirection textDirection = Directionality.of(context);
|
||||
|
||||
// TODO(Piinks): Remove old SnackBar API after migrating ScaffoldMessenger
|
||||
_accessibleNavigation = mediaQuery.accessibleNavigation;
|
||||
if (_snackBars.isNotEmpty) {
|
||||
final ModalRoute<dynamic>? route = ModalRoute.of(context);
|
||||
if (route == null || route.isCurrent) {
|
||||
if (_snackBarController!.isCompleted && _snackBarTimer == null) {
|
||||
final SnackBar snackBar = _snackBars.first._widget;
|
||||
_snackBarTimer = Timer(snackBar.duration, () {
|
||||
assert(
|
||||
_snackBarController!.status == AnimationStatus.forward ||
|
||||
_snackBarController!.status == AnimationStatus.completed,
|
||||
);
|
||||
// Look up MediaQuery again in case the setting changed.
|
||||
final MediaQueryData mediaQuery = MediaQuery.of(context);
|
||||
if (mediaQuery.accessibleNavigation && snackBar.action != null)
|
||||
return;
|
||||
hideCurrentSnackBar(reason: SnackBarClosedReason.timeout);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
_snackBarTimer?.cancel();
|
||||
_snackBarTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
final List<LayoutId> children = <LayoutId>[];
|
||||
_addIfNonNull(
|
||||
children,
|
||||
@ -2895,15 +2664,6 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
||||
|
||||
bool isSnackBarFloating = false;
|
||||
double? snackBarWidth;
|
||||
// We should only be using one API for SnackBars. Currently, we can use the
|
||||
// Scaffold, which creates a SnackBar queue (_snackBars), or the
|
||||
// ScaffoldMessenger, which sends a SnackBar to descendant Scaffolds.
|
||||
// (_messengerSnackBar).
|
||||
assert(
|
||||
_snackBars.isEmpty || _messengerSnackBar == null,
|
||||
'Only one API should be used to manage SnackBars. The ScaffoldMessenger is '
|
||||
'the preferred API instead of the Scaffold methods.',
|
||||
);
|
||||
|
||||
if (_currentBottomSheet != null || _dismissedBottomSheets.isNotEmpty) {
|
||||
final Widget stack = Stack(
|
||||
@ -2944,27 +2704,6 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin, Resto
|
||||
);
|
||||
}
|
||||
|
||||
// SnackBar set by Scaffold
|
||||
// TODO(Piinks): Remove old SnackBar API after migrating ScaffoldMessenger
|
||||
if (_snackBars.isNotEmpty) {
|
||||
final SnackBarBehavior snackBarBehavior = _snackBars.first._widget.behavior
|
||||
?? themeData.snackBarTheme.behavior
|
||||
?? SnackBarBehavior.fixed;
|
||||
isSnackBarFloating = snackBarBehavior == SnackBarBehavior.floating;
|
||||
snackBarWidth = _snackBars.first._widget.width;
|
||||
|
||||
_addIfNonNull(
|
||||
children,
|
||||
_snackBars.first._widget,
|
||||
_ScaffoldSlot.snackBar,
|
||||
removeLeftPadding: false,
|
||||
removeTopPadding: true,
|
||||
removeRightPadding: false,
|
||||
removeBottomPadding: widget.bottomNavigationBar != null || widget.persistentFooterButtons != null,
|
||||
maintainBottomViewPadding: !_resizeToAvoidBottomInset,
|
||||
);
|
||||
}
|
||||
|
||||
bool extendBodyBehindMaterialBanner = false;
|
||||
// MaterialBanner set by ScaffoldMessenger
|
||||
if (_messengerMaterialBanner != null) {
|
||||
|
@ -122,7 +122,7 @@ class _SnackBarActionState extends State<SnackBarAction> {
|
||||
_haveTriggeredAction = true;
|
||||
});
|
||||
widget.onPressed();
|
||||
Scaffold.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.action);
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.action);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -583,14 +583,14 @@ class _SnackBarState extends State<SnackBar> {
|
||||
container: true,
|
||||
liveRegion: true,
|
||||
onDismiss: () {
|
||||
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
|
||||
ScaffoldMessenger.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.dismiss);
|
||||
},
|
||||
child: Dismissible(
|
||||
key: const Key('dismissible'),
|
||||
direction: widget.dismissDirection,
|
||||
resizeDuration: null,
|
||||
onDismissed: (DismissDirection direction) {
|
||||
Scaffold.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
|
||||
ScaffoldMessenger.of(context).removeCurrentSnackBar(reason: SnackBarClosedReason.swipe);
|
||||
},
|
||||
child: snackBar,
|
||||
),
|
||||
|
@ -263,8 +263,10 @@ void main() {
|
||||
),
|
||||
),
|
||||
));
|
||||
// The Scaffold should assert we still have an ancestor ScaffoldMessenger in
|
||||
// order to dismiss the SnackBar from the ScaffoldMessenger.
|
||||
// Tap SnackBarAction to dismiss.
|
||||
// The SnackBarAction should assert we still have an ancestor
|
||||
// ScaffoldMessenger in order to dismiss the SnackBar from the
|
||||
// Scaffold.
|
||||
await tester.tap(find.text('Test'));
|
||||
FlutterError.onError = oldHandler;
|
||||
|
||||
@ -286,11 +288,67 @@ void main() {
|
||||
expect(error.toStringDeep(), equalsIgnoringHashCodes(
|
||||
'FlutterError\n'
|
||||
' No ScaffoldMessenger widget found.\n'
|
||||
' Scaffold widgets require a ScaffoldMessenger widget ancestor.\n'
|
||||
' SnackBarAction widgets require a ScaffoldMessenger widget\n'
|
||||
' ancestor.\n'
|
||||
' The specific widget that could not find a ScaffoldMessenger\n'
|
||||
' ancestor was:\n'
|
||||
' Scaffold-[LabeledGlobalKey<ScaffoldState>#00829]\n'
|
||||
' SnackBarAction\n'
|
||||
' The ancestors of this widget were:\n'
|
||||
' TextButtonTheme\n'
|
||||
' Padding\n'
|
||||
' Row\n'
|
||||
' Padding\n'
|
||||
' MediaQuery\n'
|
||||
' Padding\n'
|
||||
' SafeArea\n'
|
||||
' FadeTransition\n'
|
||||
' IconTheme\n'
|
||||
' IconTheme\n'
|
||||
' _InheritedCupertinoTheme\n'
|
||||
' CupertinoTheme\n'
|
||||
' _InheritedTheme\n'
|
||||
' Theme\n'
|
||||
' DefaultTextStyle\n'
|
||||
' AnimatedDefaultTextStyle\n'
|
||||
' _InkFeatures-[GlobalKey#00000 ink renderer]\n'
|
||||
' NotificationListener<LayoutChangedNotification>\n'
|
||||
' PhysicalModel\n'
|
||||
' AnimatedPhysicalModel\n'
|
||||
' Material\n'
|
||||
' FractionalTranslation\n'
|
||||
' SlideTransition\n'
|
||||
' Listener\n'
|
||||
' _GestureSemantics\n'
|
||||
' RawGestureDetector\n'
|
||||
' GestureDetector\n'
|
||||
" Dismissible-[<'dismissible'>]\n"
|
||||
' Semantics\n'
|
||||
' Align\n'
|
||||
' AnimatedBuilder\n'
|
||||
' ClipRect\n'
|
||||
' KeyedSubtree-[GlobalKey#00000]\n'
|
||||
' _EffectiveTickerMode\n'
|
||||
' TickerMode\n'
|
||||
' Offstage\n'
|
||||
' SizedBox\n'
|
||||
' Hero\n'
|
||||
' SnackBar-[#00000]\n'
|
||||
' MediaQuery\n'
|
||||
' LayoutId-[<_ScaffoldSlot.snackBar>]\n'
|
||||
' CustomMultiChildLayout\n'
|
||||
' AnimatedBuilder\n'
|
||||
' DefaultTextStyle\n'
|
||||
' AnimatedDefaultTextStyle\n'
|
||||
' _InkFeatures-[GlobalKey#00000 ink renderer]\n'
|
||||
' NotificationListener<LayoutChangedNotification>\n'
|
||||
' PhysicalModel\n'
|
||||
' AnimatedPhysicalModel\n'
|
||||
' Material\n'
|
||||
' _ScrollMetricsNotificationObserverScope\n'
|
||||
' NotificationListener<ScrollMetricsNotification>\n'
|
||||
' ScrollMetricsNotificationObserver\n'
|
||||
' _ScaffoldScope\n'
|
||||
' Scaffold-[LabeledGlobalKey<ScaffoldState>#00000]\n'
|
||||
' MediaQuery\n'
|
||||
' Directionality\n'
|
||||
' [root]\n'
|
||||
|
@ -8,58 +8,12 @@
|
||||
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart' show FlutterExceptionHandler;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('SnackBar control test', (WidgetTester tester) async {
|
||||
const String helloSnackBar = 'Hello SnackBar';
|
||||
const Key tapTarget = Key('tap-target');
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
key: tapTarget,
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(const SnackBar(
|
||||
content: Text(helloSnackBar),
|
||||
duration: Duration(seconds: 2),
|
||||
));
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const SizedBox(
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
expect(find.text(helloSnackBar), findsNothing);
|
||||
await tester.tap(find.byKey(tapTarget));
|
||||
expect(find.text(helloSnackBar), findsNothing);
|
||||
await tester.pump(); // schedule animation
|
||||
expect(find.text(helloSnackBar), findsOneWidget);
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text(helloSnackBar), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
|
||||
expect(find.text(helloSnackBar), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 1.50s
|
||||
expect(find.text(helloSnackBar), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 2.25s
|
||||
expect(find.text(helloSnackBar), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 3.00s // timer triggers to dismiss snackbar, reverse animation is scheduled
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text(helloSnackBar), findsOneWidget); // frame 0 of dismiss animation
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 3.75s // last frame of animation, snackbar removed from build
|
||||
expect(find.text(helloSnackBar), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('SnackBar control test - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
const String helloSnackBar = 'Hello SnackBar';
|
||||
const Key tapTarget = Key('tap-target');
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
@ -105,81 +59,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('SnackBar twice test', (WidgetTester tester) async {
|
||||
int snackBarCount = 0;
|
||||
const Key tapTarget = Key('tap-target');
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
key: tapTarget,
|
||||
onTap: () {
|
||||
snackBarCount += 1;
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text('bar$snackBarCount'),
|
||||
duration: const Duration(seconds: 2),
|
||||
));
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const SizedBox(
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.tap(find.byKey(tapTarget)); // queue bar1
|
||||
await tester.tap(find.byKey(tapTarget)); // queue bar2
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(); // schedule animation for bar1
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 1.50s
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 2.25s
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 3.00s // timer triggers to dismiss snackbar, reverse animation is scheduled
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 3.75s // last frame of animation, snackbar removed from build, new snack bar put in its place
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 4.50s // animation last frame; two second timer starts here
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 5.25s
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 6.00s
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 6.75s // timer triggers to dismiss snackbar, reverse animation is scheduled
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 7.50s // last frame of animation, snackbar removed from build, new snack bar put in its place
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('SnackBar twice test - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
int snackBarCount = 0;
|
||||
const Key tapTarget = Key('tap-target');
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
@ -255,92 +134,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('SnackBar cancel test', (WidgetTester tester) async {
|
||||
int snackBarCount = 0;
|
||||
const Key tapTarget = Key('tap-target');
|
||||
late int time;
|
||||
late ScaffoldFeatureController<SnackBar, SnackBarClosedReason> lastController;
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
key: tapTarget,
|
||||
onTap: () {
|
||||
snackBarCount += 1;
|
||||
lastController = Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text('bar$snackBarCount'),
|
||||
duration: Duration(seconds: time),
|
||||
));
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const SizedBox(
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
time = 1000;
|
||||
await tester.tap(find.byKey(tapTarget)); // queue bar1
|
||||
final ScaffoldFeatureController<SnackBar, SnackBarClosedReason> firstController = lastController;
|
||||
time = 2;
|
||||
await tester.tap(find.byKey(tapTarget)); // queue bar2
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(); // schedule animation for bar1
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 0.75s // animation last frame; two second timer starts here
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 1.50s
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 2.25s
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 10000)); // 12.25s
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
|
||||
firstController.close(); // snackbar is manually dismissed
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 13.00s // reverse animation is scheduled
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsOneWidget);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 13.75s // last frame of animation, snackbar removed from build, new snack bar put in its place
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 14.50s // animation last frame; two second timer starts here
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 15.25s
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 16.00s
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 16.75s // timer triggers to dismiss snackbar, reverse animation is scheduled
|
||||
await tester.pump(); // begin animation
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsOneWidget);
|
||||
await tester.pump(const Duration(milliseconds: 750)); // 17.50s // last frame of animation, snackbar removed from build, new snack bar put in its place
|
||||
expect(find.text('bar1'), findsNothing);
|
||||
expect(find.text('bar2'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('SnackBar cancel test - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
int snackBarCount = 0;
|
||||
const Key tapTarget = Key('tap-target');
|
||||
late int time;
|
||||
@ -432,48 +225,6 @@ void main() {
|
||||
late double width;
|
||||
int snackBarCount = 0;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
width = MediaQuery.of(context).size.width;
|
||||
|
||||
return GestureDetector(
|
||||
key: tapTarget,
|
||||
onTap: () {
|
||||
snackBarCount += 1;
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text('bar$snackBarCount'),
|
||||
duration: const Duration(seconds: 2),
|
||||
dismissDirection: dismissDirection,
|
||||
));
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const SizedBox(
|
||||
height: 100.0,
|
||||
width: 100.0,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
await _testSnackBarDismiss(
|
||||
tester: tester,
|
||||
tapTarget: tapTarget,
|
||||
scaffoldWidth: width,
|
||||
onDismissDirectionChange: (DismissDirection dir) => dismissDirection = dir,
|
||||
onDragGestureChange: () => snackBarCount = 0,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('SnackBar dismiss test - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
const Key tapTarget = Key('tap-target');
|
||||
late DismissDirection dismissDirection;
|
||||
late double width;
|
||||
int snackBarCount = 0;
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
@ -511,45 +262,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('SnackBar cannot be tapped twice', (WidgetTester tester) async {
|
||||
int tapCount = 0;
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: const Text('I am a snack bar.'),
|
||||
duration: const Duration(seconds: 2),
|
||||
action: SnackBarAction(
|
||||
label: 'ACTION',
|
||||
onPressed: () {
|
||||
++tapCount;
|
||||
},
|
||||
),
|
||||
));
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump(); // start animation
|
||||
await tester.pump(const Duration(milliseconds: 750));
|
||||
|
||||
expect(tapCount, equals(0));
|
||||
await tester.tap(find.text('ACTION'));
|
||||
expect(tapCount, equals(1));
|
||||
await tester.tap(find.text('ACTION'));
|
||||
expect(tapCount, equals(1));
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('ACTION'));
|
||||
expect(tapCount, equals(1));
|
||||
});
|
||||
|
||||
testWidgets('SnackBar cannot be tapped twice - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
int tapCount = 0;
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
@ -598,7 +310,7 @@ void main() {
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text('I am a snack bar.'),
|
||||
duration: const Duration(seconds: 2),
|
||||
@ -640,7 +352,7 @@ void main() {
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text('I am a snack bar.'),
|
||||
duration: const Duration(seconds: 2),
|
||||
@ -679,7 +391,7 @@ void main() {
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: const Text('I am a snack bar.'),
|
||||
duration: const Duration(seconds: 2),
|
||||
@ -809,7 +521,7 @@ void main() {
|
||||
themeBeforeSnackBar = Theme.of(context);
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Builder(
|
||||
builder: (BuildContext context) {
|
||||
@ -853,7 +565,7 @@ void main() {
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('I am a snack bar.'),
|
||||
margin: EdgeInsets.all(padding),
|
||||
@ -885,53 +597,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('SnackbarBehavior.floating is positioned within safe area', (WidgetTester tester) async {
|
||||
const double viewPadding = 50.0;
|
||||
const double floatingSnackBarDefaultBottomMargin = 10.0;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
// Simulate non-safe area.
|
||||
viewPadding: EdgeInsets.only(bottom: viewPadding),
|
||||
),
|
||||
child: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('I am a snack bar.'),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump(); // Start animation
|
||||
await tester.pump(const Duration(milliseconds: 750));
|
||||
|
||||
final Finder materialFinder = find.descendant(
|
||||
of: find.byType(SnackBar),
|
||||
matching: find.byType(Material),
|
||||
);
|
||||
final Offset snackBarBottomLeft = tester.getBottomLeft(materialFinder);
|
||||
expect(
|
||||
snackBarBottomLeft.dy,
|
||||
// Device height is 600.
|
||||
600 - viewPadding - floatingSnackBarDefaultBottomMargin,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('SnackbarBehavior.floating is positioned within safe area - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
const double viewPadding = 50.0;
|
||||
const double floatingSnackBarDefaultBottomMargin = 10.0;
|
||||
await tester.pumpWidget(
|
||||
@ -1452,47 +1117,6 @@ void main() {
|
||||
testWidgets('accessible navigation behavior with action', (WidgetTester tester) async {
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(accessibleNavigation: true),
|
||||
child: 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('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump();
|
||||
// Find action immediately
|
||||
expect(find.text('ACTION'), findsOneWidget);
|
||||
// Snackbar doesn't close
|
||||
await tester.pump(const Duration(seconds: 10));
|
||||
expect(find.text('ACTION'), findsOneWidget);
|
||||
await tester.tap(find.text('ACTION'));
|
||||
await tester.pump();
|
||||
// Snackbar closes immediately
|
||||
expect(find.text('ACTION'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('accessible navigation behavior with action - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(accessibleNavigation: true),
|
||||
@ -1537,47 +1161,6 @@ void main() {
|
||||
final SemanticsHandle handle = tester.ensureSemantics();
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(accessibleNavigation: true),
|
||||
child: 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('X'),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
));
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.getSemantics(find.text('snack')), matchesSemantics(
|
||||
isLiveRegion: true,
|
||||
hasDismissAction: true,
|
||||
hasScrollDownAction: true,
|
||||
hasScrollUpAction: true,
|
||||
label: 'snack',
|
||||
textDirection: TextDirection.ltr,
|
||||
));
|
||||
handle.dispose();
|
||||
});
|
||||
|
||||
testWidgets('contributes dismiss semantics - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
final SemanticsHandle handle = tester.ensureSemantics();
|
||||
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(accessibleNavigation: true),
|
||||
@ -1666,54 +1249,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('SnackBar handles updates to accessibleNavigation', (WidgetTester tester) async {
|
||||
Future<void> boilerplate({ required bool accessibleNavigation }) {
|
||||
return tester.pumpWidget(MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: MediaQueryData(accessibleNavigation: accessibleNavigation),
|
||||
child: Scaffold(
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: const Text('test'),
|
||||
action: SnackBarAction(label: 'foo', onPressed: () { }),
|
||||
));
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const Text('X'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
await boilerplate(accessibleNavigation: false);
|
||||
expect(find.text('test'), findsNothing);
|
||||
await tester.tap(find.text('X'));
|
||||
await tester.pump(); // schedule animation
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
await tester.pump(); // begin animation
|
||||
await tester.pump(const Duration(milliseconds: 4750)); // 4.75s
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
|
||||
// Enabled accessible navigation
|
||||
await boilerplate(accessibleNavigation: true);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 4000)); // 8.75s
|
||||
await tester.pump();
|
||||
expect(find.text('test'), findsOneWidget);
|
||||
|
||||
// disable accessible navigation
|
||||
await boilerplate(accessibleNavigation: false);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 5750));
|
||||
|
||||
expect(find.text('test'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('SnackBar handles updates to accessibleNavigation - ScaffoldMessenger', (WidgetTester tester) async {
|
||||
Future<void> boilerplate({ required bool accessibleNavigation }) {
|
||||
return tester.pumpWidget(MaterialApp(
|
||||
home: MediaQuery(
|
||||
@ -2127,7 +1662,7 @@ void main() {
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: const Text('Some content'),
|
||||
behavior: SnackBarBehavior.fixed,
|
||||
action: SnackBarAction(
|
||||
@ -2164,7 +1699,7 @@ void main() {
|
||||
builder: (BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
content: const Text('Some content'),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
action: SnackBarAction(
|
||||
@ -2260,39 +1795,6 @@ void main() {
|
||||
expect(find.text(secondHeader), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('SnackBars cannot be used by the Scaffold and ScaffoldMessenger at the same time', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const MaterialApp(
|
||||
home: Scaffold(),
|
||||
));
|
||||
|
||||
final ScaffoldMessengerState scaffoldMessengerState = tester.state(find.byType(ScaffoldMessenger));
|
||||
scaffoldMessengerState.showSnackBar(SnackBar(
|
||||
content: const Text('ScaffoldMessenger'),
|
||||
duration: const Duration(seconds: 2),
|
||||
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
));
|
||||
final ScaffoldState scaffoldState = tester.state(find.byType(Scaffold));
|
||||
scaffoldState.showSnackBar(SnackBar(
|
||||
content: const Text('Scaffold'),
|
||||
duration: const Duration(seconds: 2),
|
||||
action: SnackBarAction(label: 'ACTION', onPressed: () {}),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
));
|
||||
|
||||
final List<dynamic> exceptions = <dynamic>[];
|
||||
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
exceptions.add(details.exception);
|
||||
};
|
||||
await tester.pump();
|
||||
FlutterError.onError = oldHandler;
|
||||
|
||||
expect(exceptions.length, 1);
|
||||
final AssertionError error = exceptions.first as AssertionError;
|
||||
expect(error.message, contains('Only one API should be used to manage SnackBars.'));
|
||||
});
|
||||
|
||||
testWidgets('SnackBars should be shown above the bottomSheet', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(const MaterialApp(
|
||||
home: Scaffold(
|
||||
|
Loading…
x
Reference in New Issue
Block a user