Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
135bb5d4
Unverified
Commit
135bb5d4
authored
Apr 08, 2022
by
Kate Lovett
Committed by
GitHub
Apr 08, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove deprecated Scaffold SnackBar API (#98549)
parent
1cf5c740
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
72 additions
and
773 deletions
+72
-773
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+0
-261
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+3
-3
debug_test.dart
packages/flutter/test/material/debug_test.dart
+62
-4
snack_bar_test.dart
packages/flutter/test/material/snack_bar_test.dart
+7
-505
No files found.
packages/flutter/lib/src/material/scaffold.dart
View file @
135bb5d4
...
...
@@ -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
)
{
...
...
packages/flutter/lib/src/material/snack_bar.dart
View file @
135bb5d4
...
...
@@ -122,7 +122,7 @@ class _SnackBarActionState extends State<SnackBarAction> {
_haveTriggeredAction
=
true
;
});
widget
.
onPressed
();
Scaffold
.
of
(
context
).
hideCurrentSnackBar
(
reason:
SnackBarClosedReason
.
action
);
Scaffold
Messenger
.
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
);
Scaffold
Messenger
.
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
);
Scaffold
Messenger
.
of
(
context
).
removeCurrentSnackBar
(
reason:
SnackBarClosedReason
.
swipe
);
},
child:
snackBar
,
),
...
...
packages/flutter/test/material/debug_test.dart
View file @
135bb5d4
...
...
@@ -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
'
' S
caffold-[LabeledGlobalKey<ScaffoldState>#00829]
\n
'
' S
nackBarAction
\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
'
...
...
packages/flutter/test/material/snack_bar_test.dart
View file @
135bb5d4
...
...
@@ -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
(
Scaffold
Messenger
.
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
(
Scaffold
Messenger
.
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
(
Scaffold
Messenger
.
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
(
Scaffold
Messenger
.
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
(
Scaffold
Messenger
.
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
(
Scaffold
Messenger
.
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
(
Scaffold
Messenger
.
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
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment