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
b79efb8d
Unverified
Commit
b79efb8d
authored
Jul 13, 2020
by
rami-a
Committed by
GitHub
Jul 13, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Material] Allow for customizing Snack bar margin, padding, and width (#61180)
parent
cda6c27f
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
220 additions
and
14 deletions
+220
-14
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+12
-2
snack_bar.dart
packages/flutter/lib/src/material/snack_bar.dart
+85
-12
snack_bar_test.dart
packages/flutter/test/material/snack_bar_test.dart
+123
-0
No files found.
packages/flutter/lib/src/material/scaffold.dart
View file @
b79efb8d
...
...
@@ -409,6 +409,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
@required
this
.
floatingActionButtonMoveAnimationProgress
,
@required
this
.
floatingActionButtonMotionAnimator
,
@required
this
.
isSnackBarFloating
,
@required
this
.
snackBarWidth
,
@required
this
.
extendBody
,
@required
this
.
extendBodyBehindAppBar
,
})
:
assert
(
minInsets
!=
null
),
...
...
@@ -432,6 +433,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
final
FloatingActionButtonAnimator
floatingActionButtonMotionAnimator
;
final
bool
isSnackBarFloating
;
final
double
snackBarWidth
;
@override
void
performLayout
(
Size
size
)
{
...
...
@@ -563,8 +565,12 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
}
if
(
hasChild
(
_ScaffoldSlot
.
snackBar
))
{
final
bool
hasCustomWidth
=
snackBarWidth
!=
null
&&
snackBarWidth
<
size
.
width
;
if
(
snackBarSize
==
Size
.
zero
)
{
snackBarSize
=
layoutChild
(
_ScaffoldSlot
.
snackBar
,
fullWidthConstraints
);
snackBarSize
=
layoutChild
(
_ScaffoldSlot
.
snackBar
,
hasCustomWidth
?
looseConstraints
:
fullWidthConstraints
,
);
}
double
snackBarYOffsetBase
;
...
...
@@ -574,7 +580,8 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
snackBarYOffsetBase
=
contentBottom
;
}
positionChild
(
_ScaffoldSlot
.
snackBar
,
Offset
(
0.0
,
snackBarYOffsetBase
-
snackBarSize
.
height
));
final
double
xOffset
=
hasCustomWidth
?
(
size
.
width
-
snackBarWidth
)
/
2
:
0.0
;
positionChild
(
_ScaffoldSlot
.
snackBar
,
Offset
(
xOffset
,
snackBarYOffsetBase
-
snackBarSize
.
height
));
}
if
(
hasChild
(
_ScaffoldSlot
.
statusBar
))
{
...
...
@@ -2381,11 +2388,13 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
}
bool
isSnackBarFloating
=
false
;
double
snackBarWidth
;
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
,
...
...
@@ -2541,6 +2550,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
previousFloatingActionButtonLocation:
_previousFloatingActionButtonLocation
,
textDirection:
textDirection
,
isSnackBarFloating:
isSnackBarFloating
,
snackBarWidth:
snackBarWidth
,
),
);
}),
...
...
packages/flutter/lib/src/material/snack_bar.dart
View file @
b79efb8d
...
...
@@ -173,6 +173,9 @@ class SnackBar extends StatefulWidget {
@required
this
.
content
,
this
.
backgroundColor
,
this
.
elevation
,
this
.
margin
,
this
.
padding
,
this
.
width
,
this
.
shape
,
this
.
behavior
,
this
.
action
,
...
...
@@ -181,6 +184,18 @@ class SnackBar extends StatefulWidget {
this
.
onVisible
,
})
:
assert
(
elevation
==
null
||
elevation
>=
0.0
),
assert
(
content
!=
null
),
assert
(
margin
==
null
||
behavior
==
SnackBarBehavior
.
floating
,
'Margin can only be used with floating behavior'
,
),
assert
(
width
==
null
||
behavior
==
SnackBarBehavior
.
floating
,
'Width can only be used with floating behavior'
,
),
assert
(
width
==
null
||
margin
==
null
,
'Width and margin can not be used together'
,
),
assert
(
duration
!=
null
),
super
(
key:
key
);
...
...
@@ -189,7 +204,7 @@ class SnackBar extends StatefulWidget {
/// Typically a [Text] widget.
final
Widget
content
;
/// The
Snack
bar's background color. If not specified it will use
/// The
snack
bar's background color. If not specified it will use
/// [ThemeData.snackBarTheme.backgroundColor]. If that is not specified
/// it will default to a dark variation of [ColorScheme.surface] for light
/// themes, or [ColorScheme.onSurface] for dark themes.
...
...
@@ -204,6 +219,34 @@ class SnackBar extends StatefulWidget {
/// used, if that is also null, the default value is 6.0.
final
double
elevation
;
/// Empty space to surround the snack bar.
///
/// This property is only used when [behavior] is [SnackBarBehavior.floating].
/// It can not be used if [width] is specified.
///
/// If this property is null, then the default is
/// `EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0)`.
final
EdgeInsetsGeometry
margin
;
/// The amount of padding to apply to the snack bar's content and optional
/// action.
///
/// If this property is null, then the default depends on the [behavior] and
/// the presence of an [action]. The start padding is 24 if [behavior] is
/// [SnackBarBehavior.fixed] and 16 if it is [SnackBarBehavior.floating]. If
/// there is no [action], the same padding is added to the end.
final
EdgeInsetsGeometry
padding
;
/// The width of the snack bar.
///
/// If width is specified, the snack bar will be centered horizontally in the
/// available space. This property is only used when [behavior] is
/// [SnackBarBehavior.floating]. It can not be used if [margin] is specified.
///
/// If this property is null, then the snack bar will take up the full device
/// width less the margin.
final
double
width
;
/// The shape of the snack bar's [Material].
///
/// Defines the snack bar's [Material.shape].
...
...
@@ -272,6 +315,9 @@ class SnackBar extends StatefulWidget {
content:
content
,
backgroundColor:
backgroundColor
,
elevation:
elevation
,
margin:
margin
,
padding:
padding
,
width:
width
,
shape:
shape
,
behavior:
behavior
,
action:
action
,
...
...
@@ -365,7 +411,9 @@ class _SnackBarState extends State<SnackBar> {
final
TextStyle
contentTextStyle
=
snackBarTheme
.
contentTextStyle
??
inverseTheme
.
textTheme
.
subtitle1
;
final
SnackBarBehavior
snackBarBehavior
=
widget
.
behavior
??
snackBarTheme
.
behavior
??
SnackBarBehavior
.
fixed
;
final
bool
isFloatingSnackBar
=
snackBarBehavior
==
SnackBarBehavior
.
floating
;
final
double
snackBarPadding
=
isFloatingSnackBar
?
16.0
:
24.0
;
final
double
horizontalPadding
=
isFloatingSnackBar
?
16.0
:
24.0
;
final
EdgeInsetsGeometry
padding
=
widget
.
padding
??
EdgeInsetsDirectional
.
only
(
start:
horizontalPadding
,
end:
widget
.
action
!=
null
?
0
:
horizontalPadding
);
final
CurvedAnimation
heightAnimation
=
CurvedAnimation
(
parent:
widget
.
animation
,
curve:
_snackBarHeightCurve
);
final
CurvedAnimation
fadeInAnimation
=
CurvedAnimation
(
parent:
widget
.
animation
,
curve:
_snackBarFadeInCurve
);
...
...
@@ -375,13 +423,11 @@ class _SnackBarState extends State<SnackBar> {
reverseCurve:
const
Threshold
(
0.0
),
);
Widget
snackBar
=
SafeArea
(
top:
false
,
bottom:
!
isFloatingSnackBar
,
Widget
snackBar
=
Padding
(
padding:
padding
,
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
children:
<
Widget
>[
SizedBox
(
width:
snackBarPadding
),
Expanded
(
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
_singleLineVerticalPadding
),
...
...
@@ -395,15 +441,20 @@ class _SnackBarState extends State<SnackBar> {
ButtonTheme
(
textTheme:
ButtonTextTheme
.
accent
,
minWidth:
64.0
,
padding:
EdgeInsets
.
symmetric
(
horizontal:
snackBar
Padding
),
padding:
EdgeInsets
.
symmetric
(
horizontal:
horizontal
Padding
),
child:
widget
.
action
,
)
else
SizedBox
(
width:
snackBarPadding
),
),
],
),
);
if
(!
isFloatingSnackBar
)
{
snackBar
=
SafeArea
(
top:
false
,
child:
snackBar
,
);
}
final
double
elevation
=
widget
.
elevation
??
snackBarTheme
.
elevation
??
6.0
;
final
Color
backgroundColor
=
widget
.
backgroundColor
??
snackBarTheme
.
backgroundColor
??
inverseTheme
.
backgroundColor
;
final
ShapeBorder
shape
=
widget
.
shape
...
...
@@ -426,8 +477,30 @@ class _SnackBarState extends State<SnackBar> {
);
if
(
isFloatingSnackBar
)
{
const
double
topMargin
=
5.0
;
const
double
bottomMargin
=
10.0
;
// If width is provided, do not include horizontal margins.
if
(
widget
.
width
!=
null
)
{
snackBar
=
Container
(
margin:
const
EdgeInsets
.
only
(
top:
topMargin
,
bottom:
bottomMargin
),
width:
widget
.
width
,
child:
snackBar
,
);
}
else
{
const
double
horizontalMargin
=
15.0
;
snackBar
=
Padding
(
padding:
const
EdgeInsets
.
fromLTRB
(
15.0
,
5.0
,
15.0
,
10.0
),
padding:
widget
.
margin
??
const
EdgeInsets
.
fromLTRB
(
horizontalMargin
,
topMargin
,
horizontalMargin
,
bottomMargin
,
),
child:
snackBar
,
);
}
snackBar
=
SafeArea
(
top:
false
,
bottom:
false
,
child:
snackBar
,
);
}
...
...
packages/flutter/test/material/snack_bar_test.dart
View file @
b79efb8d
...
...
@@ -380,6 +380,129 @@ void main() {
expect
(
renderModel
.
color
,
equals
(
darkTheme
.
colorScheme
.
onSurface
));
});
testWidgets
(
'Snackbar margin can be customized'
,
(
WidgetTester
tester
)
async
{
const
double
padding
=
20.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.'
),
margin:
const
EdgeInsets
.
all
(
padding
),
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
);
final
Offset
snackBarBottomRight
=
tester
.
getBottomRight
(
materialFinder
);
expect
(
snackBarBottomLeft
.
dx
,
padding
);
expect
(
snackBarBottomLeft
.
dy
,
600
-
padding
);
// Device height is 600.
expect
(
snackBarBottomRight
.
dx
,
800
-
padding
);
// Device width is 800.
});
testWidgets
(
'Snackbar padding can be customized'
,
(
WidgetTester
tester
)
async
{
const
double
padding
=
20.0
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Builder
(
builder:
(
BuildContext
context
)
{
return
GestureDetector
(
onTap:
()
{
Scaffold
.
of
(
context
).
showSnackBar
(
const
SnackBar
(
content:
Text
(
'I am a snack bar.'
),
padding:
EdgeInsets
.
all
(
padding
),
),
);
},
child:
const
Text
(
'X'
),
);
}
),
),
),
);
await
tester
.
tap
(
find
.
text
(
'X'
));
await
tester
.
pump
();
// start animation
await
tester
.
pump
(
const
Duration
(
milliseconds:
750
));
final
Finder
textFinder
=
find
.
text
(
'I am a snack bar.'
);
final
Finder
materialFinder
=
find
.
descendant
(
of:
find
.
byType
(
SnackBar
),
matching:
find
.
byType
(
Material
),
);
final
Offset
textBottomLeft
=
tester
.
getBottomLeft
(
textFinder
);
final
Offset
textTopRight
=
tester
.
getTopRight
(
textFinder
);
final
Offset
snackBarBottomLeft
=
tester
.
getBottomLeft
(
materialFinder
);
final
Offset
snackBarTopRight
=
tester
.
getTopRight
(
materialFinder
);
expect
(
textBottomLeft
.
dx
-
snackBarBottomLeft
.
dx
,
padding
);
expect
(
snackBarTopRight
.
dx
-
textTopRight
.
dx
,
padding
);
// The text is given a vertical padding of 14 already.
expect
(
snackBarBottomLeft
.
dy
-
textBottomLeft
.
dy
,
padding
+
14
);
expect
(
textTopRight
.
dy
-
snackBarTopRight
.
dy
,
padding
+
14
);
});
testWidgets
(
'Snackbar width can be customized'
,
(
WidgetTester
tester
)
async
{
const
double
width
=
200.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.'
),
width:
width
,
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
);
final
Offset
snackBarBottomRight
=
tester
.
getBottomRight
(
materialFinder
);
expect
(
snackBarBottomLeft
.
dx
,
(
800
-
width
)
/
2
);
// Device width is 800.
expect
(
snackBarBottomRight
.
dx
,
(
800
+
width
)
/
2
);
// Device width is 800.
});
testWidgets
(
'Snackbar labels can be colored'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
...
...
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