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
4c83ea8b
Commit
4c83ea8b
authored
Sep 29, 2017
by
Ian Hickson
Committed by
GitHub
Sep 29, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
SafeArea (#12292)
* SafeArea * AnimatedSafeArea * AppBar test * Apply feedback
parent
9646e1fb
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
687 additions
and
61 deletions
+687
-61
nav_bar.dart
packages/flutter/lib/src/cupertino/nav_bar.dart
+9
-5
page_scaffold.dart
packages/flutter/lib/src/cupertino/page_scaffold.dart
+3
-4
app_bar.dart
packages/flutter/lib/src/material/app_bar.dart
+2
-2
drawer_header.dart
packages/flutter/lib/src/material/drawer_header.dart
+5
-1
scaffold.dart
packages/flutter/lib/src/material/scaffold.dart
+147
-48
implicit_animations.dart
packages/flutter/lib/src/widgets/implicit_animations.dart
+65
-0
media_query.dart
packages/flutter/lib/src/widgets/media_query.dart
+72
-0
safe_area.dart
packages/flutter/lib/src/widgets/safe_area.dart
+93
-0
widgets.dart
packages/flutter/lib/widgets.dart
+1
-0
app_bar_test.dart
packages/flutter/test/material/app_bar_test.dart
+61
-1
scaffold_test.dart
packages/flutter/test/material/scaffold_test.dart
+96
-0
animated_padding_test.dart
packages/flutter/test/widgets/animated_padding_test.dart
+61
-0
safe_area_test.dart
packages/flutter/test/widgets/safe_area_test.dart
+72
-0
No files found.
packages/flutter/lib/src/cupertino/nav_bar.dart
View file @
4c83ea8b
...
...
@@ -337,6 +337,9 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
left:
_kNavBarEdgePadding
,
right:
_kNavBarEdgePadding
,
),
child:
new
MediaQuery
.
removePadding
(
context:
context
,
removeTop:
true
,
child:
new
NavigationToolbar
(
leading:
styledLeading
,
middle:
animatedStyledMiddle
,
...
...
@@ -345,6 +348,7 @@ class _CupertinoPersistentNavigationBar extends StatelessWidget implements Prefe
),
),
),
),
);
}
}
...
...
packages/flutter/lib/src/cupertino/page_scaffold.dart
View file @
4c83ea8b
...
...
@@ -54,10 +54,9 @@ class CupertinoPageScaffold extends StatelessWidget {
if
(
topPadding
>
0.0
)
{
final
EdgeInsets
mediaQueryPadding
=
MediaQuery
.
of
(
context
).
padding
;
topPadding
+=
mediaQueryPadding
.
top
;
childWithMediaQuery
=
new
MediaQuery
(
data:
MediaQuery
.
of
(
context
).
copyWith
(
padding:
mediaQueryPadding
.
copyWith
(
top:
0.0
),
),
childWithMediaQuery
=
new
MediaQuery
.
removePadding
(
context:
context
,
removeTop:
true
,
child:
child
,
);
}
...
...
packages/flutter/lib/src/material/app_bar.dart
View file @
4c83ea8b
...
...
@@ -441,8 +441,8 @@ class _AppBarState extends State<AppBar> {
// The padding applies to the toolbar and tabbar, not the flexible space.
if
(
widget
.
primary
)
{
appBar
=
new
Padding
(
padding:
new
EdgeInsets
.
only
(
top:
MediaQuery
.
of
(
context
).
padding
.
top
)
,
appBar
=
new
SafeArea
(
top:
true
,
child:
appBar
,
);
}
...
...
packages/flutter/lib/src/material/drawer_header.dart
View file @
4c83ea8b
...
...
@@ -93,9 +93,13 @@ class DrawerHeader extends StatelessWidget {
curve:
curve
,
child:
child
==
null
?
null
:
new
DefaultTextStyle
(
style:
theme
.
textTheme
.
body2
,
child:
new
MediaQuery
.
removePadding
(
context:
context
,
removeTop:
true
,
child:
child
,
),
),
),
);
}
}
packages/flutter/lib/src/material/scaffold.dart
View file @
4c83ea8b
...
...
@@ -37,13 +37,15 @@ enum _ScaffoldSlot {
class
_ScaffoldLayout
extends
MultiChildLayoutDelegate
{
_ScaffoldLayout
({
@required
this
.
padding
,
@required
this
.
statusBarHeight
,
@required
this
.
bottomPadding
,
@required
this
.
endPadding
,
// for floating action button
@required
this
.
textDirection
,
});
final
EdgeInsets
padding
;
final
double
statusBarHeight
;
final
double
bottomPadding
;
final
double
endPadding
;
final
TextDirection
textDirection
;
@override
...
...
@@ -56,7 +58,7 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
// so the app bar's shadow is drawn on top of the body.
final
BoxConstraints
fullWidthConstraints
=
looseConstraints
.
tighten
(
width:
size
.
width
);
final
double
bottom
=
math
.
max
(
0.0
,
size
.
height
-
padding
.
bottom
);
final
double
bottom
=
math
.
max
(
0.0
,
size
.
height
-
bottomPadding
);
double
contentTop
=
0.0
;
double
contentBottom
=
bottom
;
...
...
@@ -72,7 +74,11 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
}
if
(
hasChild
(
_ScaffoldSlot
.
persistentFooter
))
{
final
double
persistentFooterHeight
=
layoutChild
(
_ScaffoldSlot
.
persistentFooter
,
fullWidthConstraints
.
copyWith
(
maxHeight:
contentBottom
-
contentTop
)).
height
;
final
BoxConstraints
footerConstraints
=
new
BoxConstraints
(
maxWidth:
fullWidthConstraints
.
maxWidth
,
maxHeight:
math
.
max
(
0.0
,
contentBottom
-
contentTop
),
);
final
double
persistentFooterHeight
=
layoutChild
(
_ScaffoldSlot
.
persistentFooter
,
footerConstraints
).
height
;
contentBottom
-=
persistentFooterHeight
;
positionChild
(
_ScaffoldSlot
.
persistentFooter
,
new
Offset
(
0.0
,
contentBottom
));
}
...
...
@@ -102,7 +108,11 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
Size
snackBarSize
=
Size
.
zero
;
if
(
hasChild
(
_ScaffoldSlot
.
bottomSheet
))
{
bottomSheetSize
=
layoutChild
(
_ScaffoldSlot
.
bottomSheet
,
fullWidthConstraints
.
copyWith
(
maxHeight:
contentBottom
-
contentTop
));
final
BoxConstraints
bottomSheetConstraints
=
new
BoxConstraints
(
maxWidth:
fullWidthConstraints
.
maxWidth
,
maxHeight:
math
.
max
(
0.0
,
contentBottom
-
contentTop
),
);
bottomSheetSize
=
layoutChild
(
_ScaffoldSlot
.
bottomSheet
,
bottomSheetConstraints
);
positionChild
(
_ScaffoldSlot
.
bottomSheet
,
new
Offset
((
size
.
width
-
bottomSheetSize
.
width
)
/
2.0
,
bottom
-
bottomSheetSize
.
height
));
}
...
...
@@ -117,10 +127,10 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
assert
(
textDirection
!=
null
);
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
fabX
=
_kFloatingActionButtonMargin
;
fabX
=
_kFloatingActionButtonMargin
+
endPadding
;
break
;
case
TextDirection
.
ltr
:
fabX
=
size
.
width
-
fabSize
.
width
-
_kFloatingActionButtonMargin
;
fabX
=
size
.
width
-
fabSize
.
width
-
_kFloatingActionButtonMargin
-
endPadding
;
break
;
}
double
fabY
=
contentBottom
-
fabSize
.
height
-
_kFloatingActionButtonMargin
;
...
...
@@ -144,8 +154,9 @@ class _ScaffoldLayout extends MultiChildLayoutDelegate {
@override
bool
shouldRelayout
(
_ScaffoldLayout
oldDelegate
)
{
return
oldDelegate
.
padding
!=
padding
||
oldDelegate
.
statusBarHeight
!=
statusBarHeight
return
oldDelegate
.
statusBarHeight
!=
statusBarHeight
||
oldDelegate
.
bottomPadding
!=
bottomPadding
||
oldDelegate
.
endPadding
!=
endPadding
||
oldDelegate
.
textDirection
!=
textDirection
;
}
}
...
...
@@ -336,10 +347,13 @@ class Scaffold extends StatefulWidget {
/// A set of buttons that are displayed at the bottom of the scaffold.
///
/// Typically this is a list of [FlatButton] widgets. These buttons are
/// persistently visible, even
o
f the [body] of the scaffold scrolls.
/// persistently visible, even
i
f the [body] of the scaffold scrolls.
///
/// These widgets will be wrapped in a [ButtonBar].
///
/// The [persistentFooterButtons] are rendered above the
/// [bottomNavigationBar] but below the [body].
///
/// See also:
///
/// * <https://material.google.com/components/buttons.html#buttons-persistent-footer-buttons>
...
...
@@ -363,6 +377,9 @@ class Scaffold extends StatefulWidget {
///
/// Snack bars slide from underneath the bottom navigation bar while bottom
/// sheets are stacked on top.
///
/// The [bottomNavigationBar] is rendered below the [persistentFooterButtons]
/// and the [body].
final
Widget
bottomNavigationBar
;
/// Whether the [body] (and other floating widgets) should size themselves to
...
...
@@ -747,18 +764,36 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
super
.
dispose
();
}
void
_addIfNonNull
(
List
<
LayoutId
>
children
,
Widget
child
,
Object
childId
)
{
if
(
child
!=
null
)
children
.
add
(
new
LayoutId
(
child:
child
,
id:
childId
));
void
_addIfNonNull
(
List
<
LayoutId
>
children
,
Widget
child
,
Object
childId
,
{
@required
bool
removeLeftPadding
,
@required
bool
removeTopPadding
,
@required
bool
removeRightPadding
,
bool
removeBottomPadding
,
// defaults to widget.resizeToAvoidBottomPadding
})
{
if
(
child
!=
null
)
{
children
.
add
(
new
LayoutId
(
id:
childId
,
child:
new
MediaQuery
.
removePadding
(
context:
context
,
removeLeft:
removeLeftPadding
,
removeTop:
removeTopPadding
,
removeRight:
removeRightPadding
,
removeBottom:
removeBottomPadding
??
widget
.
resizeToAvoidBottomPadding
,
child:
child
,
),
),
);
}
}
@override
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMediaQuery
(
context
));
EdgeInsets
padding
=
MediaQuery
.
of
(
context
).
padding
;
assert
(
debugCheckHasDirectionality
(
context
));
final
EdgeInsets
padding
=
MediaQuery
.
of
(
context
).
padding
;
final
ThemeData
themeData
=
Theme
.
of
(
context
);
if
(!
widget
.
resizeToAvoidBottomPadding
)
padding
=
new
EdgeInsets
.
fromLTRB
(
padding
.
left
,
padding
.
top
,
padding
.
right
,
0.0
);
final
TextDirection
textDirection
=
Directionality
.
of
(
context
);
if
(
_snackBars
.
isNotEmpty
)
{
final
ModalRoute
<
dynamic
>
route
=
ModalRoute
.
of
(
context
);
...
...
@@ -777,7 +812,14 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
final
List
<
LayoutId
>
children
=
<
LayoutId
>[];
_addIfNonNull
(
children
,
widget
.
body
,
_ScaffoldSlot
.
body
);
_addIfNonNull
(
children
,
widget
.
body
,
_ScaffoldSlot
.
body
,
removeLeftPadding:
false
,
removeTopPadding:
widget
.
appBar
!=
null
,
removeRightPadding:
false
,
);
if
(
widget
.
appBar
!=
null
)
{
final
double
topPadding
=
widget
.
primary
?
padding
.
top
:
0.0
;
...
...
@@ -793,16 +835,28 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
),
),
_ScaffoldSlot
.
appBar
,
removeLeftPadding:
false
,
removeTopPadding:
false
,
removeRightPadding:
false
,
removeBottomPadding:
true
,
);
}
if
(
_snackBars
.
isNotEmpty
)
_addIfNonNull
(
children
,
_snackBars
.
first
.
_widget
,
_ScaffoldSlot
.
snackBar
);
if
(
_snackBars
.
isNotEmpty
)
{
_addIfNonNull
(
children
,
_snackBars
.
first
.
_widget
,
_ScaffoldSlot
.
snackBar
,
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
);
}
if
(
widget
.
persistentFooterButtons
!=
null
)
{
children
.
add
(
new
LayoutId
(
id:
_ScaffoldSlot
.
persistentFooter
,
child:
new
Container
(
_addIfNonNull
(
children
,
new
Container
(
decoration:
new
BoxDecoration
(
border:
new
Border
(
top:
new
BorderSide
(
...
...
@@ -810,20 +864,30 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
),
),
),
child:
new
SafeArea
(
child:
new
ButtonTheme
.
bar
(
child:
new
ButtonBar
(
children:
widget
.
persistentFooterButtons
),
),
),
));
),
_ScaffoldSlot
.
persistentFooter
,
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
);
}
if
(
widget
.
bottomNavigationBar
!=
null
)
{
children
.
add
(
new
LayoutId
(
id:
_ScaffoldSlot
.
bottomNavigationBar
,
child:
widget
.
bottomNavigationBar
,
));
_addIfNonNull
(
children
,
widget
.
bottomNavigationBar
,
_ScaffoldSlot
.
bottomNavigationBar
,
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
);
}
if
(
_currentBottomSheet
!=
null
||
_dismissedBottomSheets
.
isNotEmpty
)
{
...
...
@@ -836,38 +900,72 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
children:
bottomSheets
,
alignment:
FractionalOffset
.
bottomCenter
,
);
_addIfNonNull
(
children
,
stack
,
_ScaffoldSlot
.
bottomSheet
);
_addIfNonNull
(
children
,
stack
,
_ScaffoldSlot
.
bottomSheet
,
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
);
}
children
.
add
(
new
LayoutId
(
id:
_ScaffoldSlot
.
floatingActionButto
n
,
child:
new
_FloatingActionButtonTransition
(
_addIfNonNull
(
childre
n
,
new
_FloatingActionButtonTransition
(
child:
widget
.
floatingActionButton
,
)
));
),
_ScaffoldSlot
.
floatingActionButton
,
removeLeftPadding:
true
,
removeTopPadding:
true
,
removeRightPadding:
true
,
removeBottomPadding:
true
,
);
if
(
themeData
.
platform
==
TargetPlatform
.
iOS
)
{
children
.
add
(
new
LayoutId
(
id:
_ScaffoldSlot
.
statusBar
,
child:
new
GestureDetector
(
_addIfNonNull
(
children
,
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
_handleStatusBarTap
,
// iOS accessibility automatically adds scroll-to-top to the clock in the status bar
excludeFromSemantics:
true
,
)
));
),
_ScaffoldSlot
.
statusBar
,
removeLeftPadding:
false
,
removeTopPadding:
true
,
removeRightPadding:
false
,
removeBottomPadding:
true
,
);
}
if
(
widget
.
drawer
!=
null
)
{
assert
(
hasDrawer
);
children
.
add
(
new
LayoutId
(
id:
_ScaffoldSlot
.
drawer
,
child:
new
DrawerController
(
_addIfNonNull
(
children
,
new
DrawerController
(
key:
_drawerKey
,
child:
widget
.
drawer
,
)
));
),
_ScaffoldSlot
.
drawer
,
// remove the side padding from the side we're not touching
removeLeftPadding:
textDirection
==
TextDirection
.
rtl
,
removeTopPadding:
false
,
removeRightPadding:
textDirection
==
TextDirection
.
ltr
,
removeBottomPadding:
false
,
);
}
double
endPadding
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
endPadding
=
padding
.
left
;
break
;
case
TextDirection
.
ltr
:
endPadding
=
padding
.
right
;
break
;
}
assert
(
endPadding
!=
null
);
return
new
_ScaffoldScope
(
hasDrawer:
hasDrawer
,
...
...
@@ -878,9 +976,10 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
child:
new
CustomMultiChildLayout
(
children:
children
,
delegate:
new
_ScaffoldLayout
(
padding:
padding
,
statusBarHeight:
padding
.
top
,
textDirection:
Directionality
.
of
(
context
),
bottomPadding:
widget
.
resizeToAvoidBottomPadding
?
padding
.
bottom
:
0.0
,
endPadding:
endPadding
,
textDirection:
textDirection
,
),
),
),
...
...
packages/flutter/lib/src/widgets/implicit_animations.dart
View file @
4c83ea8b
...
...
@@ -336,6 +336,11 @@ abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> exten
/// For more complex animations, you'll likely want to use a subclass of
/// [AnimatedWidget] such as the [DecoratedBoxTransition] or use your own
/// [AnimationController].
///
/// See also:
///
/// * [AnimatedPadding], which is a subset of this widget that only
/// supports animating the [padding].
class
AnimatedContainer
extends
ImplicitlyAnimatedWidget
{
/// Creates a container that animates its parameters implicitly.
///
...
...
@@ -479,6 +484,66 @@ class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer>
}
}
/// Animated version of [Padding] which automatically transitions the
/// indentation over a given duration whenever the given inset changes.
///
/// See also:
///
/// * [AnimatedContainer], which can transition more values at once.
class
AnimatedPadding
extends
ImplicitlyAnimatedWidget
{
/// Creates a widget that insets its child by a value that animates
/// implicitly.
///
/// The [padding], [curve], and [duration] arguments must not be null.
AnimatedPadding
({
Key
key
,
@required
this
.
padding
,
this
.
child
,
Curve
curve:
Curves
.
linear
,
@required
Duration
duration
,
})
:
assert
(
padding
!=
null
),
assert
(
padding
.
isNonNegative
),
super
(
key:
key
,
curve:
curve
,
duration:
duration
);
/// The amount of space by which to inset the child.
final
EdgeInsetsGeometry
padding
;
/// The widget below this widget in the tree.
final
Widget
child
;
@override
_AnimatedPaddingState
createState
()
=>
new
_AnimatedPaddingState
();
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
description
)
{
super
.
debugFillProperties
(
description
);
description
.
add
(
new
DiagnosticsProperty
<
EdgeInsetsGeometry
>(
'padding'
,
padding
));
}
}
class
_AnimatedPaddingState
extends
AnimatedWidgetBaseState
<
AnimatedPadding
>
{
EdgeInsetsGeometryTween
_padding
;
@override
void
forEachTween
(
TweenVisitor
<
dynamic
>
visitor
)
{
_padding
=
visitor
(
_padding
,
widget
.
padding
,
(
dynamic
value
)
=>
new
EdgeInsetsGeometryTween
(
begin:
value
));
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
Padding
(
padding:
_padding
.
evaluate
(
animation
),
child:
widget
.
child
,
);
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
description
)
{
super
.
debugFillProperties
(
description
);
description
.
add
(
new
DiagnosticsProperty
<
EdgeInsetsGeometryTween
>(
'padding'
,
_padding
,
defaultValue:
null
));
}
}
/// Animated version of [Positioned] which automatically transitions the child's
/// position over a given duration whenever the given position changes.
///
...
...
packages/flutter/lib/src/widgets/media_query.dart
View file @
4c83ea8b
...
...
@@ -98,6 +98,40 @@ class MediaQueryData {
);
}
/// Creates a copy of this media query data but with the given paddings
/// replaced with zero.
///
/// The `removeLeft`, `removeTop`, `removeRight`, and `removeBottom` arguments
/// must not be null. If all four are false (the default) then this
/// [MediaQueryData] is returned unmodified.
///
/// See also:
///
/// * [new MediaQuery.removePadding], which uses this method to remove padding
/// from the ambient [MediaQuery].
/// * [SafeArea], which both removes the padding from the [MediaQuery] and
/// adds a [Padding] widget.
MediaQueryData
removePadding
({
bool
removeLeft:
false
,
bool
removeTop:
false
,
bool
removeRight:
false
,
bool
removeBottom:
false
,
})
{
if
(!(
removeLeft
||
removeTop
||
removeRight
||
removeBottom
))
return
this
;
return
new
MediaQueryData
(
size:
size
,
devicePixelRatio:
devicePixelRatio
,
textScaleFactor:
textScaleFactor
,
padding:
padding
.
copyWith
(
left:
removeLeft
?
0.0
:
null
,
top:
removeTop
?
0.0
:
null
,
right:
removeRight
?
0.0
:
null
,
bottom:
removeBottom
?
0.0
:
null
,
),
);
}
@override
bool
operator
==(
Object
other
)
{
if
(
other
.
runtimeType
!=
runtimeType
)
...
...
@@ -148,6 +182,44 @@ class MediaQuery extends InheritedWidget {
assert
(
data
!=
null
),
super
(
key:
key
,
child:
child
);
/// Creates a new [MediaQuery] that inherits from the ambient [MediaQuery] from
/// the given context, but removes the specified paddings.
///
/// The [context] argument is required, must not be null, and must have a
/// [MediaQuery] in scope.
///
/// The `removeLeft`, `removeTop`, `removeRight`, and `removeBottom` arguments
/// must not be null. If all four are false (the default) then the returned
/// [MediaQuery] reuses the ambient [MediaQueryData] unmodified, which is not
/// particularly useful.
///
/// The [child] argument is required and must not be null.
///
/// See also:
///
/// * [SafeArea], which both removes the padding from the [MediaQuery] and
/// adds a [Padding] widget.
factory
MediaQuery
.
removePadding
({
Key
key
,
@required
BuildContext
context
,
bool
removeLeft:
false
,
bool
removeTop:
false
,
bool
removeRight:
false
,
bool
removeBottom:
false
,
@required
Widget
child
,
})
{
return
new
MediaQuery
(
key:
key
,
data:
MediaQuery
.
of
(
context
).
removePadding
(
removeLeft:
removeLeft
,
removeTop:
removeTop
,
removeRight:
removeRight
,
removeBottom:
removeBottom
,
),
child:
child
,
);
}
/// Contains information about the current media.
///
/// For example, the [MediaQueryData.size] property contains the width and
...
...
packages/flutter/lib/src/widgets/safe_area.dart
0 → 100644
View file @
4c83ea8b
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter/foundation.dart'
;
import
'basic.dart'
;
import
'debug.dart'
;
import
'framework.dart'
;
import
'media_query.dart'
;
/// A widget that insets its child by sufficient padding to avoid
/// intrusions by the operating system.
///
/// For example, this will indent the child by enough to avoid the status bar at
/// the top of the screen.
///
/// It will also indent the child by the amount necessary to avoid The Notch on
/// the iPhone X, or other similar creative physical features of the display.
///
/// See also:
///
/// * [Padding], for insetting widgets in general.
/// * [MediaQuery], from which the window padding is obtained.
/// * [dart:ui.Window.padding], which reports the padding from the operating
/// system.
class
SafeArea
extends
StatelessWidget
{
/// Creates a widget that avoids operating system interfaces.
///
/// The [left], [top], [right], and [bottom] arguments must not be null.
const
SafeArea
({
Key
key
,
this
.
left
:
true
,
this
.
top
:
true
,
this
.
right
:
true
,
this
.
bottom
:
true
,
@required
this
.
child
,
})
:
assert
(
left
!=
null
),
assert
(
top
!=
null
),
assert
(
right
!=
null
),
assert
(
bottom
!=
null
),
super
(
key:
key
);
/// Whether to avoid system intrusions on the left.
final
bool
left
;
/// Whether to avoid system intrusions at the top of the screen, typically the
/// system status bar.
final
bool
top
;
/// Whether to avoid system intrusions on the right.
final
bool
right
;
/// Whether to avoid system intrusions on the bottom side of the screen.
final
bool
bottom
;
/// The widget below this widget in the tree.
///
/// The padding on the [MediaQuery] for the [child] will be suitably adjusted
/// to zero out any sides that were avoided by this widget.
final
Widget
child
;
@override
Widget
build
(
BuildContext
context
)
{
assert
(
debugCheckHasMediaQuery
(
context
));
final
EdgeInsets
padding
=
MediaQuery
.
of
(
context
).
padding
;
return
new
Padding
(
padding:
new
EdgeInsets
.
only
(
left:
left
?
padding
.
left
:
0.0
,
top:
top
?
padding
.
top
:
0.0
,
right:
right
?
padding
.
right
:
0.0
,
bottom:
bottom
?
padding
.
bottom
:
0.0
,
),
child:
new
MediaQuery
.
removePadding
(
context:
context
,
removeLeft:
left
,
removeTop:
top
,
removeRight:
right
,
removeBottom:
bottom
,
child:
child
,
),
);
}
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
description
)
{
super
.
debugFillProperties
(
description
);
description
.
add
(
new
FlagProperty
(
'left'
,
value:
left
,
ifTrue:
'avoid left padding'
));
description
.
add
(
new
FlagProperty
(
'top'
,
value:
left
,
ifTrue:
'avoid top padding'
));
description
.
add
(
new
FlagProperty
(
'right'
,
value:
left
,
ifTrue:
'avoid right padding'
));
description
.
add
(
new
FlagProperty
(
'bottom'
,
value:
left
,
ifTrue:
'avoid bottom padding'
));
}
}
packages/flutter/lib/widgets.dart
View file @
4c83ea8b
...
...
@@ -64,6 +64,7 @@ export 'src/widgets/preferred_size.dart';
export
'src/widgets/primary_scroll_controller.dart'
;
export
'src/widgets/raw_keyboard_listener.dart'
;
export
'src/widgets/routes.dart'
;
export
'src/widgets/safe_area.dart'
;
export
'src/widgets/scroll_activity.dart'
;
export
'src/widgets/scroll_configuration.dart'
;
export
'src/widgets/scroll_context.dart'
;
...
...
packages/flutter/test/material/app_bar_test.dart
View file @
4c83ea8b
...
...
@@ -278,7 +278,7 @@ void main() {
expect
(
tester
.
getTopRight
(
find
.
text
(
'X'
)).
dx
,
800.0
-
72.0
);
});
testWidgets
(
'AppBar centerTitle:false title overflow OK
'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'AppBar centerTitle:false title overflow OK'
,
(
WidgetTester
tester
)
async
{
// The app bar's title should be constrained to fit within the available space
// between the leading and actions widgets.
...
...
@@ -1085,4 +1085,64 @@ void main() {
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byKey
(
key
)).
localToGlobal
(
Offset
.
zero
),
const
Offset
(
0.0
,
0.0
));
expect
(
tester
.
renderObject
<
RenderBox
>(
find
.
byKey
(
key
)).
size
,
const
Size
(
56.0
,
56.0
));
});
testWidgets
(
'AppBar positioning of leading and trailing widgets with top padding'
,
(
WidgetTester
tester
)
async
{
const
MediaQueryData
topPadding100
=
const
MediaQueryData
(
padding:
const
EdgeInsets
.
only
(
top:
100.0
));
final
Key
leadingKey
=
new
UniqueKey
();
final
Key
titleKey
=
new
UniqueKey
();
final
Key
trailingKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
MediaQuery
(
data:
topPadding100
,
child:
new
Scaffold
(
primary:
false
,
appBar:
new
AppBar
(
leading:
new
Placeholder
(
key:
leadingKey
),
title:
new
Placeholder
(
key:
titleKey
),
actions:
<
Widget
>[
new
Placeholder
(
key:
trailingKey
)
],
),
),
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
AppBar
)),
const
Offset
(
0.0
,
0.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
leadingKey
)),
const
Offset
(
800.0
-
56.0
,
100.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
titleKey
)),
const
Offset
(
420.0
,
100.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
trailingKey
)),
const
Offset
(
4.0
,
100.0
));
});
testWidgets
(
'SliverAppBar positioning of leading and trailing widgets with top padding'
,
(
WidgetTester
tester
)
async
{
const
MediaQueryData
topPadding100
=
const
MediaQueryData
(
padding:
const
EdgeInsets
.
only
(
top:
100.0
));
final
Key
leadingKey
=
new
UniqueKey
();
final
Key
titleKey
=
new
UniqueKey
();
final
Key
trailingKey
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
MediaQuery
(
data:
topPadding100
,
child:
new
CustomScrollView
(
primary:
true
,
slivers:
<
Widget
>[
new
SliverAppBar
(
leading:
new
Placeholder
(
key:
leadingKey
),
title:
new
Placeholder
(
key:
titleKey
),
actions:
<
Widget
>[
new
Placeholder
(
key:
trailingKey
)
],
),
],
),
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
AppBar
)),
const
Offset
(
0.0
,
0.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
leadingKey
)),
const
Offset
(
800.0
-
56.0
,
100.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
titleKey
)),
const
Offset
(
420.0
,
100.0
));
expect
(
tester
.
getTopLeft
(
find
.
byKey
(
trailingKey
)),
const
Offset
(
4.0
,
100.0
));
});
}
packages/flutter/test/material/scaffold_test.dart
View file @
4c83ea8b
...
...
@@ -527,4 +527,100 @@ void main() {
semantics
.
dispose
();
});
testWidgets
(
'Scaffold and extreme window padding'
,
(
WidgetTester
tester
)
async
{
final
Key
appBar
=
new
UniqueKey
();
final
Key
body
=
new
UniqueKey
();
final
Key
floatingActionButton
=
new
UniqueKey
();
final
Key
persistentFooterButton
=
new
UniqueKey
();
final
Key
drawer
=
new
UniqueKey
();
final
Key
bottomNavigationBar
=
new
UniqueKey
();
final
Key
insideAppBar
=
new
UniqueKey
();
final
Key
insideBody
=
new
UniqueKey
();
final
Key
insideFloatingActionButton
=
new
UniqueKey
();
final
Key
insidePersistentFooterButton
=
new
UniqueKey
();
final
Key
insideDrawer
=
new
UniqueKey
();
final
Key
insideBottomNavigationBar
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
MediaQuery
(
data:
const
MediaQueryData
(
padding:
const
EdgeInsets
.
only
(
left:
20.0
,
top:
30.0
,
right:
50.0
,
bottom:
70.0
,
),
),
child:
new
Scaffold
(
appBar:
new
PreferredSize
(
preferredSize:
const
Size
(
11.0
,
13.0
),
child:
new
Container
(
key:
appBar
,
child:
new
SafeArea
(
child:
new
Placeholder
(
key:
insideAppBar
),
),
),
),
body:
new
Container
(
key:
body
,
child:
new
SafeArea
(
child:
new
Placeholder
(
key:
insideBody
),
),
),
floatingActionButton:
new
SizedBox
(
key:
floatingActionButton
,
width:
77.0
,
height:
77.0
,
child:
new
SafeArea
(
child:
new
Placeholder
(
key:
insideFloatingActionButton
),
),
),
persistentFooterButtons:
<
Widget
>[
new
SizedBox
(
key:
persistentFooterButton
,
width:
100.0
,
height:
90.0
,
child:
new
SafeArea
(
child:
new
Placeholder
(
key:
insidePersistentFooterButton
),
),
),
],
drawer:
new
Container
(
key:
drawer
,
width:
204.0
,
child:
new
SafeArea
(
child:
new
Placeholder
(
key:
insideDrawer
),
),
),
bottomNavigationBar:
new
SizedBox
(
key:
bottomNavigationBar
,
height:
55.0
,
child:
new
SafeArea
(
child:
new
Placeholder
(
key:
insideBottomNavigationBar
),
),
),
),
),
),
);
// open drawer
await
tester
.
flingFrom
(
const
Offset
(
795.0
,
5.0
),
const
Offset
(-
200.0
,
0.0
),
10.0
);
await
tester
.
pump
();
await
tester
.
pump
(
const
Duration
(
seconds:
1
));
expect
(
tester
.
getRect
(
find
.
byKey
(
appBar
)),
new
Rect
.
fromLTRB
(
0.0
,
0.0
,
800.0
,
43.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
body
)),
new
Rect
.
fromLTRB
(
0.0
,
43.0
,
800.0
,
368.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
floatingActionButton
)),
new
Rect
.
fromLTRB
(
36.0
,
275.0
,
113.0
,
352.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
persistentFooterButton
)),
new
Rect
.
fromLTRB
(
28.0
,
377.0
,
128.0
,
467.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
drawer
)),
new
Rect
.
fromLTRB
(
596.0
,
0.0
,
800.0
,
600.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
bottomNavigationBar
)),
new
Rect
.
fromLTRB
(
0.0
,
475.0
,
800.0
,
530.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
insideAppBar
)),
new
Rect
.
fromLTRB
(
20.0
,
30.0
,
750.0
,
43.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
insideBody
)),
new
Rect
.
fromLTRB
(
20.0
,
43.0
,
750.0
,
368.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
insideFloatingActionButton
)),
new
Rect
.
fromLTRB
(
36.0
,
275.0
,
113.0
,
352.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
insidePersistentFooterButton
)),
new
Rect
.
fromLTRB
(
28.0
,
377.0
,
128.0
,
467.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
insideDrawer
)),
new
Rect
.
fromLTRB
(
596.0
,
30.0
,
750.0
,
530.0
));
expect
(
tester
.
getRect
(
find
.
byKey
(
insideBottomNavigationBar
)),
new
Rect
.
fromLTRB
(
20.0
,
475.0
,
750.0
,
530.0
));
});
}
packages/flutter/test/widgets/animated_padding_test.dart
0 → 100644
View file @
4c83ea8b
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
void
main
(
)
{
testWidgets
(
'AnimatedPadding.debugFillProperties'
,
(
WidgetTester
tester
)
async
{
final
AnimatedPadding
padding
=
new
AnimatedPadding
(
padding:
const
EdgeInsets
.
all
(
7.0
),
curve:
Curves
.
ease
,
duration:
const
Duration
(
milliseconds:
200
),
);
expect
(
padding
,
hasOneLineDescription
);
});
testWidgets
(
'AnimatedPadding padding visual-to-directional animation'
,
(
WidgetTester
tester
)
async
{
final
Key
target
=
new
UniqueKey
();
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
AnimatedPadding
(
duration:
const
Duration
(
milliseconds:
200
),
padding:
const
EdgeInsets
.
only
(
right:
50.0
),
child:
new
SizedBox
.
expand
(
key:
target
),
),
),
);
expect
(
tester
.
getSize
(
find
.
byKey
(
target
)),
const
Size
(
750.0
,
600.0
));
expect
(
tester
.
getTopRight
(
find
.
byKey
(
target
)),
const
Offset
(
750.0
,
0.0
));
await
tester
.
pumpWidget
(
new
Directionality
(
textDirection:
TextDirection
.
rtl
,
child:
new
AnimatedPadding
(
duration:
const
Duration
(
milliseconds:
200
),
padding:
const
EdgeInsetsDirectional
.
only
(
start:
100.0
),
child:
new
SizedBox
.
expand
(
key:
target
),
),
),
);
expect
(
tester
.
getSize
(
find
.
byKey
(
target
)),
const
Size
(
750.0
,
600.0
));
expect
(
tester
.
getTopRight
(
find
.
byKey
(
target
)),
const
Offset
(
750.0
,
0.0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
expect
(
tester
.
getSize
(
find
.
byKey
(
target
)),
const
Size
(
725.0
,
600.0
));
expect
(
tester
.
getTopRight
(
find
.
byKey
(
target
)),
const
Offset
(
725.0
,
0.0
));
await
tester
.
pump
(
const
Duration
(
milliseconds:
500
));
expect
(
tester
.
getSize
(
find
.
byKey
(
target
)),
const
Size
(
700.0
,
600.0
));
expect
(
tester
.
getTopRight
(
find
.
byKey
(
target
)),
const
Offset
(
700.0
,
0.0
));
});
}
packages/flutter/test/widgets/safe_area_test.dart
0 → 100644
View file @
4c83ea8b
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
void
main
(
)
{
testWidgets
(
'SafeArea - basic'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
MediaQuery
(
data:
const
MediaQueryData
(
padding:
const
EdgeInsets
.
all
(
20.0
)),
child:
const
SafeArea
(
left:
false
,
child:
const
Placeholder
(),
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Placeholder
)),
const
Offset
(
0.0
,
20.0
));
expect
(
tester
.
getBottomRight
(
find
.
byType
(
Placeholder
)),
const
Offset
(
780.0
,
580.0
));
});
testWidgets
(
'SafeArea - nested'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
MediaQuery
(
data:
const
MediaQueryData
(
padding:
const
EdgeInsets
.
all
(
20.0
)),
child:
const
SafeArea
(
top:
false
,
child:
const
SafeArea
(
right:
false
,
child:
const
Placeholder
(),
),
),
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Placeholder
)),
const
Offset
(
20.0
,
20.0
));
expect
(
tester
.
getBottomRight
(
find
.
byType
(
Placeholder
)),
const
Offset
(
780.0
,
580.0
));
});
testWidgets
(
'SafeArea - changing'
,
(
WidgetTester
tester
)
async
{
final
Widget
child
=
const
SafeArea
(
bottom:
false
,
child:
const
SafeArea
(
left:
false
,
bottom:
false
,
child:
const
Placeholder
(),
),
);
await
tester
.
pumpWidget
(
new
MediaQuery
(
data:
const
MediaQueryData
(
padding:
const
EdgeInsets
.
all
(
20.0
)),
child:
child
,
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Placeholder
)),
const
Offset
(
20.0
,
20.0
));
expect
(
tester
.
getBottomRight
(
find
.
byType
(
Placeholder
)),
const
Offset
(
780.0
,
600.0
));
await
tester
.
pumpWidget
(
new
MediaQuery
(
data:
const
MediaQueryData
(
padding:
const
EdgeInsets
.
only
(
left:
100.0
,
top:
30.0
,
right:
0.0
,
bottom:
40.0
,
)),
child:
child
,
),
);
expect
(
tester
.
getTopLeft
(
find
.
byType
(
Placeholder
)),
const
Offset
(
100.0
,
30.0
));
expect
(
tester
.
getBottomRight
(
find
.
byType
(
Placeholder
)),
const
Offset
(
800.0
,
600.0
));
});
}
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