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
bc396d1b
Unverified
Commit
bc396d1b
authored
5 years ago
by
Jonah Williams
Committed by
GitHub
5 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add onVisible callback to snackbar. (#42344)
parent
a7aa6616
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
170 additions
and
41 deletions
+170
-41
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+89
-41
snack_bar_test.dart
packages/flutter/test/material/snack_bar_test.dart
+81
-0
No files found.
packages/flutter/lib/src/material/snack_bar.dart
View file @
bc396d1b
...
...
@@ -161,7 +161,7 @@ class _SnackBarActionState extends State<SnackBarAction> {
/// * [SnackBarThemeData], to configure the default property values for
/// [SnackBar] widgets.
/// * <https://material.io/design/components/snackbars.html>
class
SnackBar
extends
State
less
Widget
{
class
SnackBar
extends
State
ful
Widget
{
/// Creates a snack bar.
///
/// The [content] argument must be non-null. The [elevation] must be null or
...
...
@@ -176,6 +176,7 @@ class SnackBar extends StatelessWidget {
this
.
action
,
this
.
duration
=
_snackBarDisplayDuration
,
this
.
animation
,
this
.
onVisible
,
})
:
assert
(
elevation
==
null
||
elevation
>=
0.0
),
assert
(
content
!=
null
),
assert
(
duration
!=
null
),
...
...
@@ -245,10 +246,86 @@ class SnackBar extends StatelessWidget {
/// The animation driving the entrance and exit of the snack bar.
final
Animation
<
double
>
animation
;
/// Called the first time that the snackbar is visible within a [Scaffold].
final
VoidCallback
onVisible
;
// API for Scaffold.showSnackBar():
/// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
static
AnimationController
createAnimationController
({
@required
TickerProvider
vsync
})
{
return
AnimationController
(
duration:
_snackBarTransitionDuration
,
debugLabel:
'SnackBar'
,
vsync:
vsync
,
);
}
/// Creates a copy of this snack bar but with the animation replaced with the given animation.
///
/// If the original snack bar lacks a key, the newly created snack bar will
/// use the given fallback key.
SnackBar
withAnimation
(
Animation
<
double
>
newAnimation
,
{
Key
fallbackKey
})
{
return
SnackBar
(
key:
key
??
fallbackKey
,
content:
content
,
backgroundColor:
backgroundColor
,
elevation:
elevation
,
shape:
shape
,
behavior:
behavior
,
action:
action
,
duration:
duration
,
animation:
newAnimation
,
onVisible:
onVisible
,
);
}
@override
State
<
SnackBar
>
createState
()
=>
_SnackBarState
();
}
class
_SnackBarState
extends
State
<
SnackBar
>
{
bool
_wasVisible
=
false
;
@override
void
initState
()
{
super
.
initState
();
widget
.
animation
.
addStatusListener
(
_onAnimationStatusChanged
);
}
@override
void
didUpdateWidget
(
SnackBar
oldWidget
)
{
if
(
widget
.
animation
!=
oldWidget
.
animation
)
{
oldWidget
.
animation
.
removeStatusListener
(
_onAnimationStatusChanged
);
widget
.
animation
.
addStatusListener
(
_onAnimationStatusChanged
);
}
super
.
didUpdateWidget
(
oldWidget
);
}
@override
void
dispose
()
{
widget
.
animation
.
removeStatusListener
(
_onAnimationStatusChanged
);
super
.
dispose
();
}
void
_onAnimationStatusChanged
(
AnimationStatus
animationStatus
)
{
switch
(
animationStatus
)
{
case
AnimationStatus
.
dismissed
:
case
AnimationStatus
.
forward
:
case
AnimationStatus
.
reverse
:
break
;
case
AnimationStatus
.
completed
:
if
(
widget
.
onVisible
!=
null
&&
!
_wasVisible
)
{
widget
.
onVisible
();
}
_wasVisible
=
true
;
}
}
@override
Widget
build
(
BuildContext
context
)
{
final
MediaQueryData
mediaQueryData
=
MediaQuery
.
of
(
context
);
assert
(
animation
!=
null
);
assert
(
widget
.
animation
!=
null
);
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
ColorScheme
colorScheme
=
theme
.
colorScheme
;
final
SnackBarThemeData
snackBarTheme
=
theme
.
snackBarTheme
;
...
...
@@ -284,14 +361,14 @@ class SnackBar extends StatelessWidget {
);
final
TextStyle
contentTextStyle
=
snackBarTheme
.
contentTextStyle
??
inverseTheme
.
textTheme
.
subhead
;
final
SnackBarBehavior
snackBarBehavior
=
behavior
??
snackBarTheme
.
behavior
??
SnackBarBehavior
.
fixed
;
final
SnackBarBehavior
snackBarBehavior
=
widget
.
behavior
??
snackBarTheme
.
behavior
??
SnackBarBehavior
.
fixed
;
final
bool
isFloatingSnackBar
=
snackBarBehavior
==
SnackBarBehavior
.
floating
;
final
double
snackBarPadding
=
isFloatingSnackBar
?
16.0
:
24.0
;
final
CurvedAnimation
heightAnimation
=
CurvedAnimation
(
parent:
animation
,
curve:
_snackBarHeightCurve
);
final
CurvedAnimation
fadeInAnimation
=
CurvedAnimation
(
parent:
animation
,
curve:
_snackBarFadeInCurve
);
final
CurvedAnimation
heightAnimation
=
CurvedAnimation
(
parent:
widget
.
animation
,
curve:
_snackBarHeightCurve
);
final
CurvedAnimation
fadeInAnimation
=
CurvedAnimation
(
parent:
widget
.
animation
,
curve:
_snackBarFadeInCurve
);
final
CurvedAnimation
fadeOutAnimation
=
CurvedAnimation
(
parent:
animation
,
parent:
widget
.
animation
,
curve:
_snackBarFadeOutCurve
,
reverseCurve:
const
Threshold
(
0.0
),
);
...
...
@@ -308,16 +385,16 @@ class SnackBar extends StatelessWidget {
padding:
const
EdgeInsets
.
symmetric
(
vertical:
_singleLineVerticalPadding
),
child:
DefaultTextStyle
(
style:
contentTextStyle
,
child:
content
,
child:
widget
.
content
,
),
),
),
if
(
action
!=
null
)
if
(
widget
.
action
!=
null
)
ButtonTheme
(
textTheme:
ButtonTextTheme
.
accent
,
minWidth:
64.0
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
snackBarPadding
),
child:
action
,
child:
widget
.
action
,
)
else
SizedBox
(
width:
snackBarPadding
),
...
...
@@ -325,9 +402,9 @@ class SnackBar extends StatelessWidget {
),
);
final
double
elevation
=
this
.
elevation
??
snackBarTheme
.
elevation
??
6.0
;
final
Color
backgroundColor
=
this
.
backgroundColor
??
snackBarTheme
.
backgroundColor
??
inverseTheme
.
backgroundColor
;
final
ShapeBorder
shape
=
this
.
shape
final
double
elevation
=
widget
.
elevation
??
snackBarTheme
.
elevation
??
6.0
;
final
Color
backgroundColor
=
widget
.
backgroundColor
??
snackBarTheme
.
backgroundColor
??
inverseTheme
.
backgroundColor
;
final
ShapeBorder
shape
=
widget
.
shape
??
snackBarTheme
.
shape
??
(
isFloatingSnackBar
?
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
4.0
))
:
null
);
...
...
@@ -394,33 +471,4 @@ class SnackBar extends StatelessWidget {
return
ClipRect
(
child:
snackBarTransition
);
}
// API for Scaffold.addSnackBar():
/// Creates an animation controller useful for driving a snack bar's entrance and exit animation.
static
AnimationController
createAnimationController
({
@required
TickerProvider
vsync
})
{
return
AnimationController
(
duration:
_snackBarTransitionDuration
,
debugLabel:
'SnackBar'
,
vsync:
vsync
,
);
}
/// Creates a copy of this snack bar but with the animation replaced with the given animation.
///
/// If the original snack bar lacks a key, the newly created snack bar will
/// use the given fallback key.
SnackBar
withAnimation
(
Animation
<
double
>
newAnimation
,
{
Key
fallbackKey
})
{
return
SnackBar
(
key:
key
??
fallbackKey
,
content:
content
,
backgroundColor:
backgroundColor
,
elevation:
elevation
,
shape:
shape
,
behavior:
behavior
,
action:
action
,
duration:
duration
,
animation:
newAnimation
,
);
}
}
This diff is collapsed.
Click to expand it.
packages/flutter/test/material/snack_bar_test.dart
View file @
bc396d1b
...
...
@@ -1065,4 +1065,85 @@ void main() {
await
tester
.
tap
(
find
.
byKey
(
tapTarget
));
expect
(
tester
.
takeException
(),
isNotNull
);
});
testWidgets
(
'Snackbar calls onVisible once'
,
(
WidgetTester
tester
)
async
{
const
Key
tapTarget
=
Key
(
'tap-target'
);
int
called
=
0
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Builder
(
builder:
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
Scaffold
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
const
Text
(
'hello'
),
duration:
const
Duration
(
seconds:
1
),
onVisible:
()
{
called
+=
1
;
},
));
},
behavior:
HitTestBehavior
.
opaque
,
child:
Container
(
height:
100.0
,
width:
100.0
,
key:
tapTarget
,
),
);
},
),
),
));
await
tester
.
tap
(
find
.
byKey
(
tapTarget
));
await
tester
.
pump
();
// start animation
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'hello'
),
findsOneWidget
);
expect
(
called
,
1
);
});
testWidgets
(
'Snackbar does not call onVisible when it is queued'
,
(
WidgetTester
tester
)
async
{
const
Key
tapTarget
=
Key
(
'tap-target'
);
int
called
=
0
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Builder
(
builder:
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
Scaffold
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
const
Text
(
'hello'
),
duration:
const
Duration
(
seconds:
1
),
onVisible:
()
{
called
+=
1
;
},
));
Scaffold
.
of
(
context
).
showSnackBar
(
SnackBar
(
content:
const
Text
(
'hello 2'
),
duration:
const
Duration
(
seconds:
1
),
onVisible:
()
{
called
+=
1
;
},
));
},
behavior:
HitTestBehavior
.
opaque
,
child:
Container
(
height:
100.0
,
width:
100.0
,
key:
tapTarget
,
),
);
},
),
),
));
await
tester
.
tap
(
find
.
byKey
(
tapTarget
));
await
tester
.
pump
();
// start animation
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'hello'
),
findsOneWidget
);
expect
(
called
,
1
);
});
}
This diff is collapsed.
Click to expand it.
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