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
e3d05449
Commit
e3d05449
authored
Nov 23, 2016
by
Hans Muller
Committed by
GitHub
Nov 23, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Report why a snackbar was closed (#6996)
parent
1d4292f7
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
136 additions
and
18 deletions
+136
-18
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+23
-15
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+36
-1
snack_bar_test.dart
packages/flutter/test/material/snack_bar_test.dart
+77
-2
No files found.
packages/flutter/lib/src/material/scaffold.dart
View file @
e3d05449
...
...
@@ -537,7 +537,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
// SNACKBAR API
Queue
<
ScaffoldFeatureController
<
SnackBar
,
Null
>>
_snackBars
=
new
Queue
<
ScaffoldFeatureController
<
SnackBar
,
Null
>>();
Queue
<
ScaffoldFeatureController
<
SnackBar
,
SnackBarClosedReason
>>
_snackBars
=
new
Queue
<
ScaffoldFeatureController
<
SnackBar
,
SnackBarClosedReason
>>();
AnimationController
_snackBarController
;
Timer
_snackBarTimer
;
...
...
@@ -554,23 +554,23 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
/// [removeCurrentSnackBar].
///
/// See [Scaffold.of] for information about how to obtain the [ScaffoldState].
ScaffoldFeatureController
<
SnackBar
,
Null
>
showSnackBar
(
SnackBar
snackbar
)
{
ScaffoldFeatureController
<
SnackBar
,
SnackBarClosedReason
>
showSnackBar
(
SnackBar
snackbar
)
{
_snackBarController
??=
SnackBar
.
createAnimationController
(
vsync:
this
)
..
addStatusListener
(
_handleSnackBarStatusChange
);
if
(
_snackBars
.
isEmpty
)
{
assert
(
_snackBarController
.
isDismissed
);
_snackBarController
.
forward
();
}
ScaffoldFeatureController
<
SnackBar
,
Null
>
controller
;
controller
=
new
ScaffoldFeatureController
<
SnackBar
,
Null
>.
_
(
ScaffoldFeatureController
<
SnackBar
,
SnackBarClosedReason
>
controller
;
controller
=
new
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:
new
UniqueKey
()),
new
Completer
<
Null
>(),
new
Completer
<
SnackBarClosedReason
>(),
()
{
assert
(
_snackBars
.
first
==
controller
);
_hideSnackBar
(
);
hideCurrentSnackBar
(
reason:
SnackBarClosedReason
.
hide
);
},
null
// SnackBar doesn't use a builder function so setState() wouldn't rebuild it
);
...
...
@@ -606,27 +606,31 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
///
/// The removed snack bar does not run its normal exit animation. If there are
/// any queued snack bars, they begin their entrance animation immediately.
void
removeCurrentSnackBar
()
{
void
removeCurrentSnackBar
({
SnackBarClosedReason
reason:
SnackBarClosedReason
.
remove
})
{
assert
(
reason
!=
null
);
if
(
_snackBars
.
isEmpty
)
return
;
Completer
<
Null
>
completer
=
_snackBars
.
first
.
_completer
;
final
Completer
<
SnackBarClosedReason
>
completer
=
_snackBars
.
first
.
_completer
;
if
(!
completer
.
isCompleted
)
completer
.
complete
();
completer
.
complete
(
reason
);
_snackBarTimer
?.
cancel
();
_snackBarTimer
=
null
;
_snackBarController
.
value
=
0.0
;
}
void
_hideSnackBar
()
{
assert
(
_snackBarController
.
status
==
AnimationStatus
.
forward
||
_snackBarController
.
status
==
AnimationStatus
.
completed
);
_snackBars
.
first
.
_completer
.
complete
();
/// Removes the current [SnackBar] by running its normal exit animation.
void
hideCurrentSnackBar
({
SnackBarClosedReason
reason:
SnackBarClosedReason
.
hide
})
{
assert
(
reason
!=
null
);
if
(
_snackBars
.
isEmpty
||
_snackBarController
.
status
==
AnimationStatus
.
dismissed
)
return
;
final
Completer
<
SnackBarClosedReason
>
completer
=
_snackBars
.
first
.
_completer
;
if
(!
completer
.
isCompleted
)
completer
.
complete
(
reason
);
_snackBarController
.
reverse
();
_snackBarTimer
?.
cancel
();
_snackBarTimer
=
null
;
}
// PERSISTENT BOTTOM SHEET API
final
List
<
_PersistentBottomSheet
>
_dismissedBottomSheets
=
<
_PersistentBottomSheet
>[];
...
...
@@ -922,7 +926,11 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
final
ModalRoute
<
dynamic
>
route
=
ModalRoute
.
of
(
context
);
if
(
route
==
null
||
route
.
isCurrent
)
{
if
(
_snackBarController
.
isCompleted
&&
_snackBarTimer
==
null
)
_snackBarTimer
=
new
Timer
(
_snackBars
.
first
.
_widget
.
duration
,
_hideSnackBar
);
_snackBarTimer
=
new
Timer
(
_snackBars
.
first
.
_widget
.
duration
,
()
{
assert
(
_snackBarController
.
status
==
AnimationStatus
.
forward
||
_snackBarController
.
status
==
AnimationStatus
.
completed
);
hideCurrentSnackBar
(
reason:
SnackBarClosedReason
.
timeout
);
});
}
else
{
_snackBarTimer
?.
cancel
();
_snackBarTimer
=
null
;
...
...
packages/flutter/lib/src/material/snack_bar.dart
View file @
e3d05449
...
...
@@ -30,6 +30,40 @@ const Duration _kSnackBarDisplayDuration = const Duration(milliseconds: 1500);
const
Curve
_snackBarHeightCurve
=
Curves
.
fastOutSlowIn
;
const
Curve
_snackBarFadeCurve
=
const
Interval
(
0.72
,
1.0
,
curve:
Curves
.
fastOutSlowIn
);
/// Specify how a [SnackBar] was closed.
///
/// The [showSnackBar] function returns a [ScaffoldFeatureController]. The value
/// of the controller's closed property is a Future that resolves to a
/// SnackBarClosedReason. Applications that need to know how a snackbar
/// was closed can use this value.
///
/// Example:
///
/// ```dart
/// Scaffold.of(context).showSnackBar(
/// new SnackBar( ... )
/// ).closed.then((SnackBarClosedReason reason) {
/// ...
/// });
/// ```
enum
SnackBarClosedReason
{
/// The snack bar was closed after the user tapped a [SnackBarAction].
action
,
/// The snack bar was closed by a user's swipe.
swipe
,
/// The snack bar was closed by the [ScaffoldFeatureController] close callback
/// or by calling [hideCurrentSnackBar] directly.
hide
,
/// The snack bar was closed by an call to [removeCurrentSnackBar].
remove
,
/// The snack bar was closed because its timer expired.
timeout
,
}
/// A button for a [SnackBar], known as an "action".
///
/// Snack bar actions are always enabled. If you want to disable a snack bar
...
...
@@ -77,6 +111,7 @@ class _SnackBarActionState extends State<SnackBarAction> {
_haveTriggeredAction
=
true
;
});
config
.
onPressed
();
Scaffold
.
of
(
context
).
hideCurrentSnackBar
(
reason:
SnackBarClosedReason
.
action
);
}
@override
...
...
@@ -201,7 +236,7 @@ class SnackBar extends StatelessWidget {
direction:
DismissDirection
.
down
,
resizeDuration:
null
,
onDismissed:
(
DismissDirection
direction
)
{
Scaffold
.
of
(
context
).
removeCurrentSnackBar
();
Scaffold
.
of
(
context
).
removeCurrentSnackBar
(
reason:
SnackBarClosedReason
.
swipe
);
},
child:
new
Material
(
elevation:
6
,
...
...
packages/flutter/test/material/snack_bar_test.dart
View file @
e3d05449
...
...
@@ -130,7 +130,7 @@ void main() {
int
snackBarCount
=
0
;
Key
tapTarget
=
new
Key
(
'tap-target'
);
int
time
;
ScaffoldFeatureController
<
SnackBar
,
Null
>
lastController
;
ScaffoldFeatureController
<
SnackBar
,
SnackBarClosedReason
>
lastController
;
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
new
Scaffold
(
body:
new
Builder
(
...
...
@@ -158,7 +158,7 @@ void main() {
expect
(
find
.
text
(
'bar2'
),
findsNothing
);
time
=
1000
;
await
tester
.
tap
(
find
.
byKey
(
tapTarget
));
// queue bar1
ScaffoldFeatureController
<
SnackBar
,
Null
>
firstController
=
lastController
;
ScaffoldFeatureController
<
SnackBar
,
SnackBarClosedReason
>
firstController
=
lastController
;
time
=
2
;
await
tester
.
tap
(
find
.
byKey
(
tapTarget
));
// queue bar2
expect
(
find
.
text
(
'bar1'
),
findsNothing
);
...
...
@@ -334,4 +334,79 @@ void main() {
expect
(
actionTextBottomLeft
.
x
-
textBottomRight
.
x
,
24.0
);
expect
(
snackBarBottomRight
.
x
-
actionTextBottomRight
.
x
,
24.0
);
});
testWidgets
(
'SnackBarClosedReason'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
<
ScaffoldState
>
scaffoldKey
=
new
GlobalKey
<
ScaffoldState
>();
bool
actionPressed
=
false
;
SnackBarClosedReason
closedReason
;
await
tester
.
pumpWidget
(
new
MaterialApp
(
home:
new
Scaffold
(
key:
scaffoldKey
,
body:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
GestureDetector
(
onTap:
()
{
Scaffold
.
of
(
context
).
showSnackBar
(
new
SnackBar
(
content:
new
Text
(
'snack'
),
duration:
new
Duration
(
seconds:
2
),
action:
new
SnackBarAction
(
label:
'ACTION'
,
onPressed:
()
{
actionPressed
=
true
;
}
),
)).
closed
.
then
((
SnackBarClosedReason
reason
)
{
closedReason
=
reason
;
});
},
child:
new
Text
(
'X'
)
);
},
)
)
));
// Pop up the snack bar and then press its action button.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pump
();
// start animation
await
tester
.
pump
(
const
Duration
(
milliseconds:
750
));
expect
(
actionPressed
,
isFalse
);
await
tester
.
tap
(
find
.
text
(
'ACTION'
));
expect
(
actionPressed
,
isTrue
);
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
closedReason
,
equals
(
SnackBarClosedReason
.
action
));
// Pop up the snack bar and then swipe downwards to dismiss it.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
750
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
750
));
await
tester
.
scroll
(
find
.
text
(
'snack'
),
new
Offset
(
0.0
,
50.0
));
await
tester
.
pump
();
expect
(
closedReason
,
equals
(
SnackBarClosedReason
.
swipe
));
// Pop up the snack bar and then remove it.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
750
));
scaffoldKey
.
currentState
.
removeCurrentSnackBar
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
closedReason
,
equals
(
SnackBarClosedReason
.
remove
));
// Pop up the snack bar and then hide it.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
750
));
scaffoldKey
.
currentState
.
hideCurrentSnackBar
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
closedReason
,
equals
(
SnackBarClosedReason
.
hide
));
// Pop up the snack bar and then let it time out.
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pump
(
new
Duration
(
milliseconds:
750
));
await
tester
.
pump
(
new
Duration
(
milliseconds:
750
));
await
tester
.
pump
(
new
Duration
(
milliseconds:
1500
));
await
tester
.
pump
();
// begin animation
await
tester
.
pump
(
new
Duration
(
milliseconds:
750
));
expect
(
closedReason
,
equals
(
SnackBarClosedReason
.
timeout
));
});
}
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