[web] change viewinsets instead of physical size for keyboard (#18328)

* changing viewInsets instead of physicalSize during text editing on mobile devices

* fix indentation

* fixing rotation issues. recalculating insets after physical size change. addressing reviewer comments

* make ui.windowpadding abstract. adding windowpadding implementation to engine.
This commit is contained in:
nturgut 2020-05-14 16:59:31 -07:00 committed by GitHub
parent 7e12824978
commit 8deea8ed04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 9 deletions

View File

@ -155,3 +155,9 @@ const Set<OperatingSystem> _desktopOperatingSystems = {
///
/// See [_desktopOperatingSystems].
bool get isDesktop => _desktopOperatingSystems.contains(operatingSystem);
/// A flag to check if the current browser is running on a mobile device.
///
/// See [_desktopOperatingSystems].
/// See [isDesktop].
bool get isMobile => !isDesktop;

View File

@ -465,9 +465,22 @@ flt-glass-pane * {
}
/// Called immediately after browser window metrics change.
///
/// When there is a text editing going on in mobile devices, do not change
/// the physicalSize, change the [window.viewInsets]. See:
/// https://api.flutter.dev/flutter/dart-ui/Window/viewInsets.html
/// https://api.flutter.dev/flutter/dart-ui/Window/physicalSize.html
///
/// Note: always check for rotations for a mobile device. Update the physical
/// size if the change is caused by a rotation.
void _metricsDidChange(html.Event event) {
window._computePhysicalSize();
if (window._onMetricsChanged != null) {
if(isMobile && !window.isRotation() && textEditing.isEditing) {
window.computeOnScreenKeyboardInsets();
window.invokeOnMetricsChanged();
} else {
window._computePhysicalSize();
// When physical size changes this value has to be recalculated.
window.computeOnScreenKeyboardInsets();
window.invokeOnMetricsChanged();
}
}

View File

@ -62,10 +62,10 @@ class EngineWindow extends ui.Window {
if (!override) {
double windowInnerWidth;
double windowInnerHeight;
if (html.window.visualViewport != null) {
windowInnerWidth = html.window.visualViewport.width * devicePixelRatio;
windowInnerHeight =
html.window.visualViewport.height * devicePixelRatio;
final html.VisualViewport viewport = html.window.visualViewport;
if (viewport != null) {
windowInnerWidth = viewport.width * devicePixelRatio;
windowInnerHeight = viewport.height * devicePixelRatio;
} else {
windowInnerWidth = html.window.innerWidth * devicePixelRatio;
windowInnerHeight = html.window.innerHeight * devicePixelRatio;
@ -77,6 +77,60 @@ class EngineWindow extends ui.Window {
}
}
void computeOnScreenKeyboardInsets() {
double windowInnerHeight;
final html.VisualViewport viewport = html.window.visualViewport;
if (viewport != null) {
windowInnerHeight = viewport.height * devicePixelRatio;
} else {
windowInnerHeight = html.window.innerHeight * devicePixelRatio;
}
final double bottomPadding = _physicalSize.height - windowInnerHeight;
_viewInsets =
WindowPadding(bottom: bottomPadding, left: 0, right: 0, top: 0);
}
/// Uses the previous physical size and current innerHeight/innerWidth
/// values to decide if a device is rotating.
///
/// During a rotation the height and width values will (almost) swap place.
/// Values can slightly differ due to space occupied by the browser header.
/// For example the following values are collected for Pixel 3 rotation:
///
/// height: 658 width: 393
/// new height: 313 new width: 738
///
/// The following values are from a changed caused by virtual keyboard.
///
/// height: 658 width: 393
/// height: 368 width: 393
bool isRotation() {
double height = 0;
double width = 0;
if (html.window.visualViewport != null) {
height = html.window.visualViewport.height * devicePixelRatio;
width = html.window.visualViewport.width * devicePixelRatio;
} else {
height = html.window.innerHeight * devicePixelRatio;
width = html.window.innerWidth * devicePixelRatio;
}
// First confirm both heught and width is effected.
if (_physicalSize.height != height && _physicalSize.width != width) {
// If prior to rotation height is bigger than width it should be the
// opposite after the rotation and vice versa.
if ((_physicalSize.height > _physicalSize.width && height < width) ||
(_physicalSize.width > _physicalSize.height && width < height)) {
// Rotation detected
return true;
}
}
return false;
}
@override
WindowPadding get viewInsets => _viewInsets;
WindowPadding _viewInsets = ui.WindowPadding.zero;
/// Lazily populated and cleared at the end of the frame.
ui.Size _physicalSize;
@ -155,7 +209,9 @@ class EngineWindow extends ui.Window {
/// Engine code should use this method instead of the callback directly.
/// Otherwise zones won't work properly.
void invokeOnMetricsChanged() {
_invoke(_onMetricsChanged, _onMetricsChangedZone);
if (window._onMetricsChanged != null) {
_invoke(_onMetricsChanged, _onMetricsChangedZone);
}
}
@override
@ -617,3 +673,18 @@ void _invoke3<A1, A2, A3>(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1
/// API surface, providing Web-specific functionality that the standard
/// `dart:ui` version does not.
final EngineWindow window = EngineWindow();
/// The Web implementation of [ui.WindowPadding].
class WindowPadding implements ui.WindowPadding {
const WindowPadding({
this.left,
this.top,
this.right,
this.bottom,
});
final double left;
final double top;
final double right;
final double bottom;
}

View File

@ -89,6 +89,8 @@ enum AppLifecycleState {
/// [Window.padding]. View insets and padding are preferably read via
/// [MediaQuery.of].
///
/// For the engine implementation of this class see the [engine.WindowPadding].
///
/// For a generic class that represents distances around a rectangle, see the
/// [EdgeInsets] class.
///
@ -99,8 +101,12 @@ enum AppLifecycleState {
/// * [MediaQuery.of], for the preferred mechanism for accessing these values.
/// * [Scaffold], which automatically applies the padding in material design
/// applications.
class WindowPadding {
const WindowPadding._({this.left, this.top, this.right, this.bottom});
abstract class WindowPadding {
const factory WindowPadding._(
{double left,
double top,
double right,
double bottom}) = engine.WindowPadding;
/// The distance from the left edge to the first unpadded pixel, in physical
/// pixels.