未验证 提交 79c6ce19 编写于 作者: D Dan Field 提交者: GitHub

Preserve safe area (#8848)

Preserve safe area on Window regardless of insets.
上级 cbef680a
...@@ -85,9 +85,10 @@ enum AppLifecycleState { ...@@ -85,9 +85,10 @@ enum AppLifecycleState {
/// A representation of distances for each of the four edges of a rectangle, /// A representation of distances for each of the four edges of a rectangle,
/// used to encode the view insets and padding that applications should place /// used to encode the view insets and padding that applications should place
/// around their user interface, as exposed by [Window.viewInsets] and /// around their user interface, as exposed by [Window.viewInsets],
/// [Window.padding]. View insets and padding are preferably read via /// [Window.viewPadding], and [Window.padding]. View insets and padding are
/// [MediaQuery.of]. /// preferably read via [MediaQuery.of], since [MediaQuery] will notify its
/// descendants when these values are updated.
/// ///
/// For a generic class that represents distances around a rectangle, see the /// For a generic class that represents distances around a rectangle, see the
/// [EdgeInsets] class. /// [EdgeInsets] class.
...@@ -550,12 +551,22 @@ abstract class Window { ...@@ -550,12 +551,22 @@ abstract class Window {
/// design applications. /// design applications.
WindowPadding get viewInsets => WindowPadding.zero; WindowPadding get viewInsets => WindowPadding.zero;
WindowPadding get viewPadding => WindowPadding.zero;
/// The number of physical pixels on each side of the display rectangle into /// 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 /// which the application can render, but which may be partially obscured by
/// system UI (such as the system notification area), or or physical /// system UI (such as the system notification area), or or physical
/// intrusions in the display (e.g. overscan regions on television screens or /// intrusions in the display (e.g. overscan regions on television screens or
/// phone sensor housings). /// phone sensor housings).
/// ///
/// This value is calculated by taking
/// `max(0.0, Window.viewPadding - Window.viewInsets)`. This will treat a
/// system IME that increases the bottm inset as consuming that much of the
/// bottom padding. For example, on an iPhone X, [Window.padding.bottom] is
/// the same as [Window.viewPadding.bottom] when the soft keyboard is not
/// drawn (to account for the bottom soft button area), but will be `0.0` when
/// the soft keyboard is visible.
///
/// When this changes, [onMetricsChanged] is called. /// When this changes, [onMetricsChanged] is called.
/// ///
/// See also: /// See also:
......
...@@ -21,10 +21,10 @@ dynamic _decodeJSON(String message) { ...@@ -21,10 +21,10 @@ dynamic _decodeJSON(String message) {
void _updateWindowMetrics(double devicePixelRatio, void _updateWindowMetrics(double devicePixelRatio,
double width, double width,
double height, double height,
double paddingTop, double viewPaddingTop,
double paddingRight, double viewPaddingRight,
double paddingBottom, double viewPaddingBottom,
double paddingLeft, double viewPaddingLeft,
double viewInsetTop, double viewInsetTop,
double viewInsetRight, double viewInsetRight,
double viewInsetBottom, double viewInsetBottom,
...@@ -32,16 +32,21 @@ void _updateWindowMetrics(double devicePixelRatio, ...@@ -32,16 +32,21 @@ void _updateWindowMetrics(double devicePixelRatio,
window window
.._devicePixelRatio = devicePixelRatio .._devicePixelRatio = devicePixelRatio
.._physicalSize = Size(width, height) .._physicalSize = Size(width, height)
.._padding = WindowPadding._( .._viewPadding = WindowPadding._(
top: paddingTop, top: viewPaddingTop,
right: paddingRight, right: viewPaddingRight,
bottom: paddingBottom, bottom: viewPaddingBottom,
left: paddingLeft) left: viewPaddingLeft)
.._viewInsets = WindowPadding._( .._viewInsets = WindowPadding._(
top: viewInsetTop, top: viewInsetTop,
right: viewInsetRight, right: viewInsetRight,
bottom: viewInsetBottom, bottom: viewInsetBottom,
left: viewInsetLeft); left: viewInsetLeft)
.._padding = WindowPadding._(
top: math.max(0.0, viewPaddingTop - viewInsetTop),
right: math.max(0.0, viewPaddingRight - viewInsetRight),
bottom: math.max(0.0, viewPaddingBottom - viewInsetBottom),
left: math.max(0.0, viewPaddingLeft - viewInsetLeft));
_invoke(window.onMetricsChanged, window._onMetricsChangedZone); _invoke(window.onMetricsChanged, window._onMetricsChangedZone);
} }
......
...@@ -411,6 +411,48 @@ class Locale { ...@@ -411,6 +411,48 @@ class Locale {
/// ///
/// There is a single Window instance in the system, which you can /// There is a single Window instance in the system, which you can
/// obtain from the [window] property. /// obtain from the [window] property.
///
/// ## Insets and Padding
///
/// {@animation 300 300 https://flutter.github.io/assets-for-api-docs/assets/widgets/window_padding.mp4}
///
/// In this diagram, the black areas represent system UI that the app cannot
/// draw over. The red area represents view padding that the application may not
/// be able to detect gestures in and may not want to draw in. The grey area
/// represents the system keyboard, which can cover over the bottom view
/// padding when visible.
///
/// The [Window.viewInsets] are the physical pixels which the operating
/// system reserves for system UI, such as the keyboard, which would fully
/// obscure any content drawn in that area.
///
/// The [Window.viewPadding] are the physical pixels on each side of the display
/// that may be partially obscured by system UI or by physical intrusions into
/// the display, such as an overscan region on a television or a "notch" on a
/// phone. Unlike the insets, these areas may have portions that show the user
/// application painted pixels without being obscured, such as a notch at the
/// top of a phone that covers only a subset of the area. Insets, on the other
/// hand, either partially or fully obscure the window, such as an opaque
/// keyboard or a partially transluscent statusbar, which cover an area without
/// gaps.
///
/// The [Window.padding] property is computed from both [Window.viewInsets] and
/// [Window.viewPadding]. It will allow a view inset to consume view padding
/// where appropriate, such as when a phone's keyboard is covering the bottom
/// view padding and so "absorbs" it.
///
/// Clients that want to position elements relative to the view padding
/// regardless of the view insets should use the [Window.viewPadding] property,
/// e.g. if you wish to draw a widget at the center of the screen with respect
/// to the iPhone "safe area" regardless of whether the keyboard is showing.
///
/// [Window.padding] is useful for clients that want to know how much padding
/// should be accounted for without concern for the current inset(s) state, e.g.
/// determining whether a gesture should be considered for scrolling purposes.
/// This value varies based on the current state of the insets. For example, a
/// visible keyboard will consume all gestures in the bottom part of the
/// [Window.viewPadding] anyway, so there is no need to account for that in the
/// [Window.padding], which is always safe to use for such calculations.
class Window { class Window {
Window._(); Window._();
...@@ -467,6 +509,10 @@ class Window { ...@@ -467,6 +509,10 @@ class Window {
/// ///
/// When this changes, [onMetricsChanged] is called. /// When this changes, [onMetricsChanged] is called.
/// ///
/// The relationship between this [Window.viewInsets], [Window.viewPadding],
/// and [Window.padding] are described in more detail in the documentation for
/// [Window].
///
/// See also: /// See also:
/// ///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
...@@ -483,8 +529,47 @@ class Window { ...@@ -483,8 +529,47 @@ class Window {
/// intrusions in the display (e.g. overscan regions on television screens or /// intrusions in the display (e.g. overscan regions on television screens or
/// phone sensor housings). /// phone sensor housings).
/// ///
/// Unlike [Window.padding], this value does not change relative to
/// [Window.viewInsets]. For example, on an iPhone X, it will not change in
/// response to the soft keyboard being visible or hidden, whereas
/// [Window.padding] will.
///
/// When this changes, [onMetricsChanged] is called. /// When this changes, [onMetricsChanged] is called.
/// ///
/// The relationship between this [Window.viewInsets], [Window.viewPadding],
/// and [Window.padding] are described in more detail in the documentation for
/// [Window].
///
/// See also:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
/// observe when this value changes.
/// * [MediaQuery.of], a simpler mechanism for the same.
/// * [Scaffold], which automatically applies the padding in material design
/// applications.
WindowPadding get viewPadding => _viewPadding;
WindowPadding _viewPadding = WindowPadding.zero;
/// 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).
///
/// This value is calculated by taking
/// `max(0.0, Window.viewPadding - Window.viewInsets)`. This will treat a
/// system IME that increases the bottom inset as consuming that much of the
/// bottom padding. For example, on an iPhone X, [Window.padding.bottom] is
/// the same as [Window.viewPadding.bottom] when the soft keyboard is not
/// drawn (to account for the bottom soft button area), but will be `0.0` when
/// the soft keyboard is visible.
///
/// When this changes, [onMetricsChanged] is called.
///
/// The relationship between this [Window.viewInsets], [Window.viewPadding],
/// and [Window.padding] are described in more detail in the documentation for
/// [Window].
///
/// See also: /// See also:
/// ///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
......
...@@ -727,23 +727,14 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch) ...@@ -727,23 +727,14 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
CGFloat scale = [UIScreen mainScreen].scale; CGFloat scale = [UIScreen mainScreen].scale;
// The keyboard is treated as an inset since we want to effectively reduce the window size by the // The keyboard is treated as an inset since we want to effectively reduce the window size by the
// keyboard height. We also eliminate any bottom safe-area padding since they keyboard 'consumes' // keyboard height. The Dart side will compute a value accounting for the keyboard-consuming
// the home indicator widget. // bottom padding.
_viewportMetrics.physical_view_inset_bottom = bottom * scale; _viewportMetrics.physical_view_inset_bottom = bottom * scale;
_viewportMetrics.physical_padding_bottom = 0;
[self updateViewportMetrics]; [self updateViewportMetrics];
} }
- (void)keyboardWillBeHidden:(NSNotification*)notification { - (void)keyboardWillBeHidden:(NSNotification*)notification {
CGFloat scale = [UIScreen mainScreen].scale;
_viewportMetrics.physical_view_inset_bottom = 0; _viewportMetrics.physical_view_inset_bottom = 0;
// Restore any safe area padding that the keyboard had consumed.
if (@available(iOS 11, *)) {
_viewportMetrics.physical_padding_bottom = self.view.safeAreaInsets.bottom * scale;
} else {
_viewportMetrics.physical_padding_top = [self statusBarPadding] * scale;
}
[self updateViewportMetrics]; [self updateViewportMetrics];
} }
......
...@@ -266,5 +266,63 @@ void main() { ...@@ -266,5 +266,63 @@ void main() {
expect(runZone, same(innerZone)); expect(runZone, same(innerZone));
expect(platformBrightness, equals(Brightness.dark)); expect(platformBrightness, equals(Brightness.dark));
}); });
test('Window padding/insets/viewPadding', () {
final double oldDPR = window.devicePixelRatio;
final Size oldSize = window.physicalSize;
final WindowPadding oldPadding = window.viewPadding;
final WindowPadding oldInsets = window.viewInsets;
_updateWindowMetrics(
1.0, // DPR
800.0, // width
600.0, // height
50.0, // padding top
0.0, // padding right
40.0, // padding bottom
0.0, // padding left
0.0, // inset top
0.0, // inset right
0.0, // inset bottom
0.0, // inset left
);
expect(window.viewInsets.bottom, 0.0);
expect(window.viewPadding.bottom, 40.0);
expect(window.padding.bottom, 40.0);
_updateWindowMetrics(
1.0, // DPR
800.0, // width
600.0, // height
50.0, // padding top
0.0, // padding right
40.0, // padding bottom
0.0, // padding left
0.0, // inset top
0.0, // inset right
400.0, // inset bottom
0.0, // inset left
);
expect(window.viewInsets.bottom, 400.0);
expect(window.viewPadding.bottom, 40.0);
expect(window.padding.bottom, 0.0);
_updateWindowMetrics(
oldDPR, // DPR
oldSize.width, // width
oldSize.height, // height
oldPadding.top, // padding top
oldPadding.right, // padding right
oldPadding.bottom, // padding bottom
oldPadding.left, // padding left
oldInsets.top, // inset top
oldInsets.right, // inset right
oldInsets.bottom, // inset bottom
oldInsets.left, // inset left
);
});
}); });
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册