mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[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:
parent
7e12824978
commit
8deea8ed04
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user