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
35c916d7
Unverified
Commit
35c916d7
authored
Aug 26, 2019
by
Hans Muller
Committed by
GitHub
Aug 26, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added Scaffold.extendBodyBehindAppBar (#39156)
* Co-authored-by: Brett Morgan <brettmorgan@google.com>
parent
62463a22
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
167 additions
and
8 deletions
+167
-8
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+71
-8
scaffold_test.dart
packages/flutter/test/material/scaffold_test.dart
+96
-0
No files found.
packages/flutter/lib/src/material/scaffold.dart
View file @
35c916d7
...
...
@@ -299,11 +299,15 @@ class _BodyBoxConstraints extends BoxConstraints {
double
minHeight
=
0.0
,
double
maxHeight
=
double
.
infinity
,
@required
this
.
bottomWidgetsHeight
,
@required
this
.
appBarHeight
,
})
:
assert
(
bottomWidgetsHeight
!=
null
),
assert
(
bottomWidgetsHeight
>=
0
),
assert
(
appBarHeight
!=
null
),
assert
(
appBarHeight
>=
0
),
super
(
minWidth:
minWidth
,
maxWidth:
maxWidth
,
minHeight:
minHeight
,
maxHeight:
maxHeight
);
final
double
bottomWidgetsHeight
;
final
double
appBarHeight
;
// RenderObject.layout() will only short-circuit its call to its performLayout
// method if the new layout constraints are not == to the current constraints.
...
...
@@ -314,12 +318,13 @@ class _BodyBoxConstraints extends BoxConstraints {
if
(
super
!=
other
)
return
false
;
final
_BodyBoxConstraints
typedOther
=
other
;
return
bottomWidgetsHeight
==
typedOther
.
bottomWidgetsHeight
;
return
bottomWidgetsHeight
==
typedOther
.
bottomWidgetsHeight
&&
appBarHeight
==
typedOther
.
appBarHeight
;
}
@override
int
get
hashCode
{
return
hashValues
(
super
.
hashCode
,
bottomWidgetsHeight
);
return
hashValues
(
super
.
hashCode
,
bottomWidgetsHeight
,
appBarHeight
);
}
}
...
...
@@ -330,20 +335,43 @@ class _BodyBoxConstraints extends BoxConstraints {
// The bottom widgets' height is passed along via the _BodyBoxConstraints parameter.
// The constraints parameter is constructed in_ScaffoldLayout.performLayout().
class
_BodyBuilder
extends
StatelessWidget
{
const
_BodyBuilder
({
Key
key
,
this
.
body
})
:
super
(
key:
key
);
const
_BodyBuilder
({
Key
key
,
@required
this
.
extendBody
,
@required
this
.
extendBodyBehindAppBar
,
@required
this
.
body
})
:
assert
(
extendBody
!=
null
),
assert
(
extendBodyBehindAppBar
!=
null
),
assert
(
body
!=
null
),
super
(
key:
key
);
final
Widget
body
;
final
bool
extendBody
;
final
bool
extendBodyBehindAppBar
;
@override
Widget
build
(
BuildContext
context
)
{
if
(!
extendBody
&&
!
extendBodyBehindAppBar
)
return
body
;
return
LayoutBuilder
(
builder:
(
BuildContext
context
,
BoxConstraints
constraints
)
{
final
_BodyBoxConstraints
bodyConstraints
=
constraints
;
final
MediaQueryData
metrics
=
MediaQuery
.
of
(
context
);
final
double
bottom
=
extendBody
?
math
.
max
(
metrics
.
padding
.
bottom
,
bodyConstraints
.
bottomWidgetsHeight
)
:
metrics
.
padding
.
bottom
;
final
double
top
=
extendBodyBehindAppBar
?
math
.
max
(
metrics
.
padding
.
top
,
bodyConstraints
.
appBarHeight
)
:
metrics
.
padding
.
top
;
return
MediaQuery
(
data:
metrics
.
copyWith
(
padding:
metrics
.
padding
.
copyWith
(
bottom:
math
.
max
(
metrics
.
padding
.
bottom
,
bodyConstraints
.
bottomWidgetsHeight
),
top:
top
,
bottom:
bottom
,
),
),
child:
body
,
...
...
@@ -365,14 +393,17 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
@required
this
.
floatingActionButtonMotionAnimator
,
@required
this
.
isSnackBarFloating
,
@required
this
.
extendBody
,
@required
this
.
extendBodyBehindAppBar
,
})
:
assert
(
minInsets
!=
null
),
assert
(
textDirection
!=
null
),
assert
(
geometryNotifier
!=
null
),
assert
(
previousFloatingActionButtonLocation
!=
null
),
assert
(
currentFloatingActionButtonLocation
!=
null
),
assert
(
extendBody
!=
null
);
assert
(
extendBody
!=
null
),
assert
(
extendBodyBehindAppBar
!=
null
);
final
bool
extendBody
;
final
bool
extendBodyBehindAppBar
;
final
EdgeInsets
minInsets
;
final
TextDirection
textDirection
;
final
_ScaffoldGeometryNotifier
geometryNotifier
;
...
...
@@ -397,9 +428,11 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
final
double
bottom
=
size
.
height
;
double
contentTop
=
0.0
;
double
bottomWidgetsHeight
=
0.0
;
double
appBarHeight
=
0.0
;
if
(
hasChild
(
_ScaffoldSlot
.
appBar
))
{
contentTop
=
layoutChild
(
_ScaffoldSlot
.
appBar
,
fullWidthConstraints
).
height
;
appBarHeight
=
layoutChild
(
_ScaffoldSlot
.
appBar
,
fullWidthConstraints
).
height
;
contentTop
=
extendBodyBehindAppBar
?
0.0
:
appBarHeight
;
positionChild
(
_ScaffoldSlot
.
appBar
,
Offset
.
zero
);
}
...
...
@@ -439,6 +472,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
maxWidth:
fullWidthConstraints
.
maxWidth
,
maxHeight:
bodyMaxHeight
,
bottomWidgetsHeight:
extendBody
?
bottomWidgetsHeight
:
0.0
,
appBarHeight:
appBarHeight
,
);
layoutChild
(
_ScaffoldSlot
.
body
,
bodyConstraints
);
positionChild
(
_ScaffoldSlot
.
body
,
Offset
(
0.0
,
contentTop
));
...
...
@@ -546,7 +580,9 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
||
oldDelegate
.
textDirection
!=
textDirection
||
oldDelegate
.
floatingActionButtonMoveAnimationProgress
!=
floatingActionButtonMoveAnimationProgress
||
oldDelegate
.
previousFloatingActionButtonLocation
!=
previousFloatingActionButtonLocation
||
oldDelegate
.
currentFloatingActionButtonLocation
!=
currentFloatingActionButtonLocation
;
||
oldDelegate
.
currentFloatingActionButtonLocation
!=
currentFloatingActionButtonLocation
||
oldDelegate
.
extendBody
!=
extendBody
||
oldDelegate
.
extendBodyBehindAppBar
!=
extendBodyBehindAppBar
;
}
}
...
...
@@ -966,10 +1002,12 @@ class Scaffold extends StatefulWidget {
this
.
primary
=
true
,
this
.
drawerDragStartBehavior
=
DragStartBehavior
.
start
,
this
.
extendBody
=
false
,
this
.
extendBodyBehindAppBar
=
false
,
this
.
drawerScrimColor
,
this
.
drawerEdgeDragWidth
,
})
:
assert
(
primary
!=
null
),
assert
(
extendBody
!=
null
),
assert
(
extendBodyBehindAppBar
!=
null
),
assert
(
drawerDragStartBehavior
!=
null
),
super
(
key:
key
);
...
...
@@ -987,8 +1025,28 @@ class Scaffold extends StatefulWidget {
/// adds a [FloatingActionButton] sized notch to the top edge of the bar.
/// In this case specifying `extendBody: true` ensures that that scaffold's
/// body will be visible through the bottom navigation bar's notch.
///
/// See also:
///
/// * [extendBodyBehindAppBar], which extends the height of the body
/// to the top of the scaffold.
final
bool
extendBody
;
/// If true, and an [appBar] is specified, then the height of the [body] is
/// extended to include the height of the app bar and the top of the body
/// is aligned with the top of the app bar.
///
/// This is useful if the app bar's [AppBar.backgroundColor] is not
/// completely opaque.
///
/// This property is false by default. It must not be null.
///
/// See also:
///
/// * [extendBody], which extends the height of the body to the bottom
/// of the scaffold.
final
bool
extendBodyBehindAppBar
;
/// An app bar to display at the top of the scaffold.
final
PreferredSizeWidget
appBar
;
...
...
@@ -2084,7 +2142,11 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
final
List
<
LayoutId
>
children
=
<
LayoutId
>[];
_addIfNonNull
(
children
,
widget
.
body
!=
null
&&
widget
.
extendBody
?
_BodyBuilder
(
body:
widget
.
body
)
:
widget
.
body
,
widget
.
body
==
null
?
null
:
_BodyBuilder
(
extendBody:
widget
.
extendBody
,
extendBodyBehindAppBar:
widget
.
extendBodyBehindAppBar
,
body:
widget
.
body
),
_ScaffoldSlot
.
body
,
removeLeftPadding:
false
,
removeTopPadding:
widget
.
appBar
!=
null
,
...
...
@@ -2270,6 +2332,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
children:
children
,
delegate:
_ScaffoldLayout
(
extendBody:
_extendBody
,
extendBodyBehindAppBar:
widget
.
extendBodyBehindAppBar
,
minInsets:
minInsets
,
currentFloatingActionButtonLocation:
_floatingActionButtonLocation
,
floatingActionButtonMoveAnimationProgress:
_floatingActionButtonMoveController
.
value
,
...
...
packages/flutter/test/material/scaffold_test.dart
View file @
35c916d7
...
...
@@ -732,6 +732,102 @@ void main() {
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
expect
(
mediaQueryBottom
,
0.0
);
});
testWidgets
(
'body size with extendBodyBehindAppBar'
,
(
WidgetTester
tester
)
async
{
final
Key
appBarKey
=
UniqueKey
();
final
Key
bodyKey
=
UniqueKey
();
const
double
appBarHeight
=
100
;
const
double
windowPaddingTop
=
24
;
bool
fixedHeightAppBar
;
double
mediaQueryTop
;
Widget
buildFrame
({
bool
extendBodyBehindAppBar
,
bool
hasAppBar
})
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(
padding:
EdgeInsets
.
only
(
top:
windowPaddingTop
),
),
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
Scaffold
(
extendBodyBehindAppBar:
extendBodyBehindAppBar
,
appBar:
!
hasAppBar
?
null
:
PreferredSize
(
key:
appBarKey
,
preferredSize:
const
Size
.
fromHeight
(
appBarHeight
),
child:
Container
(
constraints:
BoxConstraints
(
minHeight:
appBarHeight
,
maxHeight:
fixedHeightAppBar
?
appBarHeight
:
double
.
infinity
,
),
),
),
body:
Builder
(
builder:
(
BuildContext
context
)
{
mediaQueryTop
=
MediaQuery
.
of
(
context
).
padding
.
top
;
return
Container
(
key:
bodyKey
);
}
),
);
},
),
),
);
}
fixedHeightAppBar
=
false
;
// When an appbar is provided, the Scaffold's body is built within a
// MediaQuery with padding.top = 0, and the appBar's maxHeight is
// constrained to its preferredSize.height + the original MediaQuery
// padding.top. When extendBodyBehindAppBar is true, an additional
// inner MediaQuery is added around the Scaffold's body with padding.top
// equal to the overall height of the appBar. See _BodyBuilder in
// material/scaffold.dart.
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
true
,
hasAppBar:
true
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
expect
(
tester
.
getSize
(
find
.
byKey
(
appBarKey
)),
const
Size
(
800.0
,
appBarHeight
+
windowPaddingTop
));
expect
(
mediaQueryTop
,
appBarHeight
+
windowPaddingTop
);
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
true
,
hasAppBar:
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
expect
(
find
.
byKey
(
appBarKey
),
findsNothing
);
expect
(
mediaQueryTop
,
windowPaddingTop
);
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
false
,
hasAppBar:
true
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
-
appBarHeight
-
windowPaddingTop
));
expect
(
tester
.
getSize
(
find
.
byKey
(
appBarKey
)),
const
Size
(
800.0
,
appBarHeight
+
windowPaddingTop
));
expect
(
mediaQueryTop
,
0.0
);
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
false
,
hasAppBar:
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
expect
(
find
.
byKey
(
appBarKey
),
findsNothing
);
expect
(
mediaQueryTop
,
windowPaddingTop
);
fixedHeightAppBar
=
true
;
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
true
,
hasAppBar:
true
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
expect
(
tester
.
getSize
(
find
.
byKey
(
appBarKey
)),
const
Size
(
800.0
,
appBarHeight
));
expect
(
mediaQueryTop
,
appBarHeight
);
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
true
,
hasAppBar:
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
expect
(
find
.
byKey
(
appBarKey
),
findsNothing
);
expect
(
mediaQueryTop
,
windowPaddingTop
);
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
false
,
hasAppBar:
true
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
-
appBarHeight
));
expect
(
tester
.
getSize
(
find
.
byKey
(
appBarKey
)),
const
Size
(
800.0
,
appBarHeight
));
expect
(
mediaQueryTop
,
0.0
);
await
tester
.
pumpWidget
(
buildFrame
(
extendBodyBehindAppBar:
false
,
hasAppBar:
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
expect
(
find
.
byKey
(
appBarKey
),
findsNothing
);
expect
(
mediaQueryTop
,
windowPaddingTop
);
});
});
testWidgets
(
'Open drawer hides underlying semantics tree'
,
(
WidgetTester
tester
)
async
{
...
...
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