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
a152a209
Unverified
Commit
a152a209
authored
Jan 15, 2019
by
Hans Muller
Committed by
GitHub
Jan 15, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Deprecate Scaffold resizeToAvoidBottomPadding, now resizeToAvoidBottomInset (#26259)
parent
3a694a6d
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
213 additions
and
37 deletions
+213
-37
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+108
-26
media_query.dart
packages/flutter/lib/src/widgets/media_query.dart
+39
-10
scaffold_test.dart
packages/flutter/test/material/scaffold_test.dart
+66
-1
No files found.
packages/flutter/lib/src/material/scaffold.dart
View file @
a152a209
...
...
@@ -25,6 +25,13 @@ import 'material.dart';
import
'snack_bar.dart'
;
import
'theme.dart'
;
// Examples can assume:
// TabController tabController
// void setState(VoidCallback fn) { }
// String appBarTitle
// int tabCount
// TickerProvider tickerProvider
const
FloatingActionButtonLocation
_kDefaultFloatingActionButtonLocation
=
FloatingActionButtonLocation
.
endFloat
;
const
FloatingActionButtonAnimator
_kDefaultFloatingActionButtonAnimator
=
FloatingActionButtonAnimator
.
scaling
;
...
...
@@ -110,14 +117,14 @@ class ScaffoldPrelayoutGeometry {
/// and is useful for insetting the [FloatingActionButton] to avoid features like
/// the system status bar or the keyboard.
///
/// If [Scaffold.resizeToAvoidBottom
Padding
] is set to false, [minInsets.bottom]
/// will be 0.0
instead of [MediaQuery.padding.bottom]
.
/// If [Scaffold.resizeToAvoidBottom
Inset
] is set to false, [minInsets.bottom]
/// will be 0.0.
final
EdgeInsets
minInsets
;
/// The [Size] of the whole [Scaffold].
///
/// If the [Size] of the [Scaffold]'s contents is modified by values such as
/// [Scaffold.resizeToAvoidBottom
Padding
] or the keyboard opening, then the
/// [Scaffold.resizeToAvoidBottom
Inset
] or the keyboard opening, then the
/// [scaffoldSize] will not reflect those changes.
///
/// This means that [FloatingActionButtonLocation]s designed to reposition
...
...
@@ -278,7 +285,10 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
@required
this
.
currentFloatingActionButtonLocation
,
@required
this
.
floatingActionButtonMoveAnimationProgress
,
@required
this
.
floatingActionButtonMotionAnimator
,
})
:
assert
(
previousFloatingActionButtonLocation
!=
null
),
})
:
assert
(
minInsets
!=
null
),
assert
(
textDirection
!=
null
),
assert
(
geometryNotifier
!=
null
),
assert
(
previousFloatingActionButtonLocation
!=
null
),
assert
(
currentFloatingActionButtonLocation
!=
null
);
final
EdgeInsets
minInsets
;
...
...
@@ -694,6 +704,58 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
/// ```
/// {@end-tool}
///
/// ## Scaffold layout, the keyboard, and display "notches"
///
/// The scaffold will expand to fill the available space. That usually
/// means that it will occupy its entire window or device screen. When
/// the device's keyboard appears the Scaffold's ancestor [MediaQuery]
/// widget's [MediaQueryData.viewInsets] changes and the Scaffold will
/// be rebuilt. By default the scaffold's [body] is resized to make
/// room for the keyboard. To prevent the resize set
/// [resizeToAvoidBottomInset] to false. In either case the focused
/// widget will be scrolled into view if it's within a scrollable
/// container.
///
/// The [MediaQueryData.padding] value defines areas that might
/// not be completely visible, like the display "notch" on the iPhone
/// X. The scaffold's [body] is not inset by this padding value
/// although an [appBar] or [bottomNavigationBar] will typically
/// cause the body to avoid the padding. The [SafeArea]
/// widget can be used within the scaffold's body to avoid areas
/// like display notches.
///
/// ## Troubleshooting
///
/// ### Nested Scaffolds
///
/// The Scaffold was designed to be the single top level container for
/// a [MaterialApp] and it's typically not necessary to nest
/// scaffolds. For example in a tabbed UI, where the
/// [bottomNavigationBar] is a [TabBar] and the body is a
/// [TabBarView], you might be tempted to make each tab bar view a
/// scaffold with a differently titled AppBar. It would be better to add a
/// listener to the [TabController] that updates the AppBar.
///
/// ## Sample Code
///
/// Add a listener to the app's tab controller so that the [AppBar] title of the
/// app's one and only scaffold is reset each time a new tab is selected.
///
/// ```dart
/// tabController = TabController(vsync: tickerProvider, length: tabCount)..addListener(() {
/// if (!tabController.indexIsChanging) {
/// setState(() {
/// // Rebuild the enclosing scaffold with a new AppBar title
/// appBarTitle = 'Tab ${tabController.index}';
/// });
/// }
/// });
/// ```
///
/// Although there are some use cases, like a presentation app that
/// shows embedded flutter content, where nested scaffolds are
/// appropriate, it's best to avoid nesting scaffolds.
///
/// See also:
///
/// * [AppBar], which is a horizontal bar typically shown at the top of an app
...
...
@@ -731,7 +793,8 @@ class Scaffold extends StatefulWidget {
this
.
bottomNavigationBar
,
this
.
bottomSheet
,
this
.
backgroundColor
,
this
.
resizeToAvoidBottomPadding
=
true
,
this
.
resizeToAvoidBottomPadding
,
this
.
resizeToAvoidBottomInset
,
this
.
primary
=
true
,
this
.
drawerDragStartBehavior
=
DragStartBehavior
.
start
,
})
:
assert
(
primary
!=
null
),
...
...
@@ -743,9 +806,11 @@ class Scaffold extends StatefulWidget {
/// The primary content of the scaffold.
///
/// Displayed below the app bar and behind the [floatingActionButton] and
/// [drawer]. To avoid the body being resized to avoid the window padding
/// (e.g., from the onscreen keyboard), see [resizeToAvoidBottomPadding].
/// Displayed below the [appBar], above the bottom of the ambient
/// [MediaQuery]'s [MediaQueryData.viewInsets], and behind the
/// [floatingActionButton] and [drawer]. If [resizeToAvoidBottomInset] is
/// false then the body is not resized when the onscreen keyboard appears,
/// i.e. it is not inset by `viewInsets.bottom`.
///
/// The widget in the body of the scaffold is positioned at the top-left of
/// the available space between the app bar and the bottom of the scaffold. To
...
...
@@ -850,15 +915,25 @@ class Scaffold extends StatefulWidget {
/// * [showModalBottomSheet], which displays a modal bottom sheet.
final
Widget
bottomSheet
;
/// Whether the [body] (and other floating widgets) should size themselves to
/// avoid the window's bottom padding.
/// This flag is deprecated, please use [resizeToAvoidBottomInset]
/// instead.
///
/// Originally the name referred [MediaQueryData.padding]. Now it refers
/// [MediaQueryData.viewInsets], so using [resizeToAvoidBottomInset]
/// should be clearer to readers.
@Deprecated
(
'Use resizeToAvoidBottomInset to specify if the body should resize when the keyboard appears'
)
final
bool
resizeToAvoidBottomPadding
;
/// If true the [body] and the scaffold's floating widgets should size
/// themselves to avoid the onscreen keyboard whose height is defined by the
/// ambient [MediaQuery]'s [MediaQueryData.viewInsets] `bottom` property.
///
/// For example, if there is an onscreen keyboard displayed above the
/// scaffold, the body can be resized to avoid overlapping the keyboard, which
/// prevents widgets inside the body from being obscured by the keyboard.
///
/// Defaults to true.
final
bool
resizeToAvoidBottom
Padding
;
final
bool
resizeToAvoidBottom
Inset
;
/// Whether this scaffold is being displayed at the top of the screen.
///
...
...
@@ -1399,6 +1474,12 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
_ScaffoldGeometryNotifier
_geometryNotifier
;
// Backwards compatibility for deprecated resizeToAvoidBottomPadding property
bool
get
_resizeToAvoidBottomInset
{
// ignore: deprecated_member_use
return
widget
.
resizeToAvoidBottomInset
??
widget
.
resizeToAvoidBottomPadding
??
true
;
}
@override
void
initState
()
{
super
.
initState
();
...
...
@@ -1479,19 +1560,22 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
@required
bool
removeTopPadding
,
@required
bool
removeRightPadding
,
@required
bool
removeBottomPadding
,
bool
removeBottomInset
=
false
,
})
{
MediaQueryData
data
=
MediaQuery
.
of
(
context
).
removePadding
(
removeLeft:
removeLeftPadding
,
removeTop:
removeTopPadding
,
removeRight:
removeRightPadding
,
removeBottom:
removeBottomPadding
,
);
if
(
removeBottomInset
)
data
=
data
.
removeViewInsets
(
removeBottom:
true
);
if
(
child
!=
null
)
{
children
.
add
(
LayoutId
(
id:
childId
,
child:
MediaQuery
.
removePadding
(
context:
context
,
removeLeft:
removeLeftPadding
,
removeTop:
removeTopPadding
,
removeRight:
removeRightPadding
,
removeBottom:
removeBottomPadding
,
child:
child
,
),
child:
MediaQuery
(
data:
data
,
child:
child
),
),
);
}
...
...
@@ -1580,8 +1664,8 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
removeLeftPadding:
false
,
removeTopPadding:
widget
.
appBar
!=
null
,
removeRightPadding:
false
,
removeBottomPadding:
widget
.
bottomNavigationBar
!=
null
||
widget
.
persistentFooterButtons
!=
null
,
removeBottomPadding:
widget
.
bottomNavigationBar
!=
null
||
widget
.
persistentFooterButtons
!=
null
,
removeBottomInset:
_resizeToAvoidBottomInset
,
);
if
(
widget
.
appBar
!=
null
)
{
...
...
@@ -1606,8 +1690,6 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
}
if
(
_snackBars
.
isNotEmpty
)
{
final
bool
removeBottomPadding
=
widget
.
persistentFooterButtons
!=
null
||
widget
.
bottomNavigationBar
!=
null
;
_addIfNonNull
(
children
,
_snackBars
.
first
.
_widget
,
...
...
@@ -1615,7 +1697,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
removeBottomPadding:
removeBottomPadding
,
removeBottomPadding:
widget
.
bottomNavigationBar
!=
null
||
widget
.
persistentFooterButtons
!=
null
,
);
}
...
...
@@ -1676,7 +1758,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
removeBottomPadding:
widget
.
resizeToAvoidBottomPadding
,
removeBottomPadding:
_resizeToAvoidBottomInset
,
);
}
...
...
@@ -1722,7 +1804,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
// The minimum insets for contents of the Scaffold to keep visible.
final
EdgeInsets
minInsets
=
mediaQuery
.
padding
.
copyWith
(
bottom:
widget
.
resizeToAvoidBottomPadding
?
mediaQuery
.
viewInsets
.
bottom
:
0.0
,
bottom:
_resizeToAvoidBottomInset
?
mediaQuery
.
viewInsets
.
bottom
:
0.0
,
);
return
_ScaffoldScope
(
...
...
packages/flutter/lib/src/widgets/media_query.dart
View file @
a152a209
...
...
@@ -30,6 +30,25 @@ enum Orientation {
/// If no [MediaQuery] is in scope then the [MediaQuery.of] method will throw an
/// exception, unless the `nullOk` argument is set to true, in which case it
/// returns null.
///
/// MediaQueryData includes two [EdgeInsets] values:
/// [padding] and [viewInsets]. These
/// values reflect the configuration of the device and are used by
/// many top level widgets, like [SafeArea] and the Cupertino and
/// Material scaffold widgets. The padding value defines areas that
/// might not be completely visible, like the display "notch" on the
/// iPhone X. The viewInsets value defines areas that aren't visible at
/// all, typically because they're obscured by the device's keyboard.
///
/// The viewInsets and padding values are independent, they're both
/// measured from the edges of the MediaQuery widget's bounds. The
/// bounds of the top level MediaQuery created by [WidgetsApp] are the
/// same as the window that contains the app.
///
/// Widgets whose layouts consume space defined by [viewInsets] or
/// [padding] shoud enclose their children in secondary MediaQuery
/// widgets that reduce those properties by the same amount.
/// The [removePadding] and [removeInsets] methods are useful for this.
@immutable
class
MediaQueryData
{
/// Creates data for a media query with explicit values.
...
...
@@ -67,7 +86,7 @@ class MediaQueryData {
boldText
=
window
.
accessibilityFeatures
.
boldText
,
alwaysUse24HourFormat
=
window
.
alwaysUse24HourFormat
;
/// The size of the media in logical pixel (e.g, the size of the screen).
/// The size of the media in logical pixel
s
(e.g, the size of the screen).
///
/// Logical pixels are roughly the same visual size across devices. Physical
/// pixels are the size of the actual hardware pixels on the device. The
...
...
@@ -91,17 +110,25 @@ class MediaQueryData {
/// textScaleFactor defined for a [BuildContext].
final
double
textScaleFactor
;
/// The number of physical pixels on each side of the display rectangle into
/// which the application can render, but over which the operating system
/// will likely place system UI, such as the keyboard, that fully obscures
/// any content.
/// The parts of the display that are completely obscured by system UI,
/// typically by the device's keyboard.
///
/// When a mobile device's keyboard is visible `viewInsets.bottom`
/// corresponds to the top of the keyboard.
///
/// This value is independent of the [padding]: both values are
/// measured from the edges of the [MediaQuery] widget's bounds. The
/// bounds of the top level MediaQuery created by [WidgetsApp] are the
/// same as the window (often the mobile device screen) that contains the app.
///
/// See also:
///
/// * [MediaQueryData], which provides some additional detail about this
/// property and how it differs from [padding].
final
EdgeInsets
viewInsets
;
/// The number of physical pixels on each side of the display rectangle into
/// which the application can render, but which may be partially obscured by
/// system UI (such as the system notification area), or or physical
/// intrusions in the display (e.g. overscan regions on television screens or
/// phone sensor housings).
/// The parts of the display that are partially obscured by system UI,
/// typically by the hardware display "notches" or the system status bar.
///
/// If you consumed this padding (e.g. by building a widget that envelops or
/// accounts for this padding in its layout in such a way that children are
...
...
@@ -111,6 +138,8 @@ class MediaQueryData {
///
/// See also:
///
/// * [MediaQueryData], which provides some additional detail about this
/// property and how it differs from [viewInsets].
/// * [SafeArea], a widget that consumes this padding with a [Padding] widget
/// and automatically removes it from the [MediaQuery] for its child.
final
EdgeInsets
padding
;
...
...
packages/flutter/test/material/scaffold_test.dart
View file @
a152a209
...
...
@@ -59,7 +59,20 @@ void main() {
child:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'Title'
)),
body:
Container
(
key:
bodyKey
),
resizeToAvoidBottomPadding:
false
,
resizeToAvoidBottomInset:
false
,
),
)));
bodyBox
=
tester
.
renderObject
(
find
.
byKey
(
bodyKey
));
expect
(
bodyBox
.
size
,
equals
(
const
Size
(
800.0
,
544.0
)));
// Backwards compatiblity: deprecated resizeToAvoidBottomPadding flag
await
tester
.
pumpWidget
(
boilerplate
(
MediaQuery
(
data:
const
MediaQueryData
(
viewInsets:
EdgeInsets
.
only
(
bottom:
100.0
)),
child:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'Title'
)),
body:
Container
(
key:
bodyKey
),
resizeToAvoidBottomPadding:
false
,
// ignore: deprecated_member_use
),
)));
...
...
@@ -1200,6 +1213,58 @@ void main() {
expect
(
scaffoldState
.
isDrawerOpen
,
true
);
});
});
testWidgets
(
'Nested scaffold body insets'
,
(
WidgetTester
tester
)
async
{
// Regression test for https://github.com/flutter/flutter/issues/20295
final
Key
bodyKey
=
UniqueKey
();
Widget
buildFrame
(
bool
innerResizeToAvoidBottomInset
,
bool
outerResizeToAvoidBottomInset
)
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
MediaQuery
(
data:
const
MediaQueryData
(
viewInsets:
EdgeInsets
.
only
(
bottom:
100.0
)),
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
Scaffold
(
resizeToAvoidBottomInset:
outerResizeToAvoidBottomInset
,
body:
Builder
(
builder:
(
BuildContext
context
)
{
return
Scaffold
(
resizeToAvoidBottomInset:
innerResizeToAvoidBottomInset
,
body:
Container
(
key:
bodyKey
),
);
},
),
);
},
),
),
);
}
await
tester
.
pumpWidget
(
buildFrame
(
true
,
true
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
await
tester
.
pumpWidget
(
buildFrame
(
false
,
true
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
await
tester
.
pumpWidget
(
buildFrame
(
true
,
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
// This is the only case where the body is not bottom inset.
await
tester
.
pumpWidget
(
buildFrame
(
false
,
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
600.0
));
await
tester
.
pumpWidget
(
buildFrame
(
null
,
null
));
// resizeToAvoidBottomInset default is true
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
await
tester
.
pumpWidget
(
buildFrame
(
null
,
false
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
await
tester
.
pumpWidget
(
buildFrame
(
false
,
null
));
expect
(
tester
.
getSize
(
find
.
byKey
(
bodyKey
)),
const
Size
(
800.0
,
500.0
));
});
}
class
_GeometryListener
extends
StatefulWidget
{
...
...
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