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

Preserve safe area (#8848)

Preserve safe area on Window regardless of insets.
上级 cbef680a
......@@ -85,9 +85,10 @@ enum AppLifecycleState {
/// 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
/// around their user interface, as exposed by [Window.viewInsets] and
/// [Window.padding]. View insets and padding are preferably read via
/// [MediaQuery.of].
/// around their user interface, as exposed by [Window.viewInsets],
/// [Window.viewPadding], and [Window.padding]. View insets and padding are
/// 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
/// [EdgeInsets] class.
......@@ -550,12 +551,22 @@ abstract class Window {
/// design applications.
WindowPadding get viewInsets => WindowPadding.zero;
WindowPadding get 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 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.
///
/// See also:
......
......@@ -21,10 +21,10 @@ dynamic _decodeJSON(String message) {
void _updateWindowMetrics(double devicePixelRatio,
double width,
double height,
double paddingTop,
double paddingRight,
double paddingBottom,
double paddingLeft,
double viewPaddingTop,
double viewPaddingRight,
double viewPaddingBottom,
double viewPaddingLeft,
double viewInsetTop,
double viewInsetRight,
double viewInsetBottom,
......@@ -32,16 +32,21 @@ void _updateWindowMetrics(double devicePixelRatio,
window
.._devicePixelRatio = devicePixelRatio
.._physicalSize = Size(width, height)
.._padding = WindowPadding._(
top: paddingTop,
right: paddingRight,
bottom: paddingBottom,
left: paddingLeft)
.._viewPadding = WindowPadding._(
top: viewPaddingTop,
right: viewPaddingRight,
bottom: viewPaddingBottom,
left: viewPaddingLeft)
.._viewInsets = WindowPadding._(
top: viewInsetTop,
right: viewInsetRight,
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);
}
......
......@@ -411,6 +411,48 @@ class Locale {
///
/// There is a single Window instance in the system, which you can
/// 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 {
Window._();
......@@ -467,6 +509,10 @@ class Window {
///
/// 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
......@@ -483,8 +529,47 @@ class Window {
/// intrusions in the display (e.g. overscan regions on television screens or
/// 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.
///
/// 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:
///
/// * [WidgetsBindingObserver], for a mechanism at the widgets layer to
......
......@@ -727,23 +727,14 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
CGFloat scale = [UIScreen mainScreen].scale;
// 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'
// the home indicator widget.
// keyboard height. The Dart side will compute a value accounting for the keyboard-consuming
// bottom padding.
_viewportMetrics.physical_view_inset_bottom = bottom * scale;
_viewportMetrics.physical_padding_bottom = 0;
[self updateViewportMetrics];
}
- (void)keyboardWillBeHidden:(NSNotification*)notification {
CGFloat scale = [UIScreen mainScreen].scale;
_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];
}
......
......@@ -266,5 +266,63 @@ void main() {
expect(runZone, same(innerZone));
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.
先完成此消息的编辑!
想要评论请 注册