Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 94598c9

Browse files
author
Nurhan Turgut
committed
Finalize location changes in IOS. IOS is ready for spellcheck now. Disable spellcheck since we realized some tag/autocomplete transfer from Framework is necessary for complete spellcheck implementation.
1 parent 6b64a5c commit 94598c9

File tree

4 files changed

+89
-19
lines changed

4 files changed

+89
-19
lines changed

lib/web_ui/lib/src/engine/dom_renderer.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,11 @@ flt-glass-pane * {
316316
// apps fully specifies their text styles.
317317
setElementStyle(bodyElement, 'font', defaultCssFont);
318318
setElementStyle(bodyElement, 'color', 'red');
319-
319+
320+
// TODO(flutter_web): Disable spellcheck until changes in the framework and
321+
// engine are complete.
322+
bodyElement.spellcheck = false;
323+
320324
for (html.Element viewportMeta
321325
in html.document.head.querySelectorAll('meta[name="viewport"]')) {
322326
if (assertionsEnabled) {

lib/web_ui/lib/src/engine/pointer_binding.dart

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,11 +323,6 @@ class TouchAdapter extends BaseAdapter {
323323
event.preventDefault();
324324
_updateButtonDownState(_kPrimaryMouseButton, false);
325325
_callback(_convertEventToPointerData(ui.PointerChange.up, event));
326-
if (textEditing.needsKeyboard &&
327-
browserEngine == BrowserEngine.webkit &&
328-
operatingSystem == OperatingSystem.iOs) {
329-
textEditing.editingElement.configureInputElementForIOS();
330-
}
331326
});
332327

333328
_addEventListener('touchcancel', (html.Event event) {

lib/web_ui/lib/src/engine/semantics/text_field.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ class TextField extends RoleManager {
3838
// and autocorrect suggestion. To disable that, we have to do the following:
3939
_textFieldElement
4040
..spellcheck = false
41-
..setAttribute('spellcheck', 'false')
4241
..setAttribute('autocorrect', 'off')
4342
..setAttribute('autocomplete', 'off')
4443
..setAttribute('data-semantics-role', 'text-field');

lib/web_ui/lib/src/engine/text_editing.dart

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,22 @@ class TextEditingElement {
203203
/// See [TextEditingElement.persistent] to understand what persistent mode is.
204204
TextEditingElement(this.owner);
205205

206+
/// Timer that times when to set the location of the input text.
207+
///
208+
/// This is only used for IOS. In IOS, virtual keyboard shifts the screen.
209+
/// There is no callback to know if the keyboard is up and how much the screen
210+
/// has shifted. Therefore instead of listening to the shift and passing this
211+
/// information to Flutter Framework, we are trying to stop the shift.
212+
///
213+
/// In IOS, the virtual keyboard shifts the screen up if the focused input
214+
/// element is under the keyboard or very close to the keyboard. Before the
215+
/// focus is called we are positining it offscreen. The location of the input
216+
/// in IOS is set to correct place, 100ms after focus. We use this timer for
217+
/// timing this delay.
218+
Timer _positionInputElementTimer;
219+
static const Duration _delayBeforePositining =
220+
const Duration(milliseconds: 100);
221+
206222
final HybridTextEditing owner;
207223
bool _enabled = false;
208224

@@ -222,20 +238,22 @@ class TextEditingElement {
222238
/// On iOS, sets the location of the input element after focusing on it.
223239
///
224240
/// On iOS, keyboard causes scrolling in the UI. This scrolling does not
225-
/// trigger an event. In order to position the input element correctly, it is
241+
/// trigger an event. In order not to trigger a shift on the page, it is
226242
/// important we set it's final location after focusing on it (after keyboard
227243
/// is up).
228244
///
229-
/// This method is called in the end of the 'touchend' event, therefore it is
230-
/// called after the editing state is set.
245+
/// This method is called after a delay.
246+
/// See [_positionInputElementTimer].
231247
void configureInputElementForIOS() {
232248
if (browserEngine != BrowserEngine.webkit ||
233249
operatingSystem != OperatingSystem.iOs) {
234-
// Only relevant on Safari.
250+
// Only relevant on Safari Mobile and Chrome on IOS.
235251
return;
236252
}
253+
237254
if (domElement != null) {
238255
owner.setStyle(domElement);
256+
owner.inputPositioned = true;
239257
}
240258
}
241259

@@ -274,6 +292,32 @@ class TextEditingElement {
274292
}));
275293
}
276294

295+
if (browserEngine == BrowserEngine.webkit &&
296+
operatingSystem == OperatingSystem.iOs) {
297+
/// Position the element outside of the page before focusing on it.
298+
///
299+
/// See [_positionInputElementTimer].
300+
owner.setStyleOutsideOfScreen(domElement);
301+
302+
_subscriptions.add(domElement.onFocus.listen((_) {
303+
// Cancel previous timer if exists.
304+
_positionInputElementTimer?.cancel();
305+
_positionInputElementTimer = Timer(_delayBeforePositining, () {
306+
if (textEditing.inputElementNeedsToBePositioned) {
307+
configureInputElementForIOS();
308+
}
309+
});
310+
311+
/// When the virtual keyboard is closed on IOS, onBlur is triggered.
312+
_subscriptions.add(domElement.onBlur.listen((_) {
313+
/// Cancel the timer since there is no need to set the location of the
314+
/// input element anymore. It needs to be focused again to be editable
315+
/// by the user.
316+
_positionInputElementTimer?.cancel();
317+
}));
318+
}));
319+
}
320+
277321
domElement.focus();
278322

279323
if (_lastEditingState != null) {
@@ -299,6 +343,8 @@ class TextEditingElement {
299343
_subscriptions[i].cancel();
300344
}
301345
_subscriptions.clear();
346+
_positionInputElementTimer?.cancel();
347+
owner.inputPositioned = false;
302348
_removeDomElement();
303349
}
304350

@@ -361,10 +407,6 @@ class TextEditingElement {
361407
..addRange(_createRange(editingState));
362408
break;
363409
}
364-
365-
// Safari on iOS requires that we focus explicitly. Otherwise, the on-screen
366-
// keyboard won't show up.
367-
domElement.focus();
368410
}
369411

370412
/// Swap out the current DOM element and replace it with a new one of type
@@ -583,8 +625,19 @@ class HybridTextEditing {
583625
/// Also used to define if a keyboard is needed.
584626
bool _isEditing = false;
585627

586-
/// Flag indicating if the flutter framework requested a keyboard.
587-
bool get needsKeyboard => _isEditing;
628+
/// Flag indication the input element needs to be positioned.
629+
///
630+
/// See [TextEditingElement._delayBeforePositining].
631+
bool get inputElementNeedsToBePositioned =>
632+
!inputPositioned &&
633+
_isEditing &&
634+
browserEngine == BrowserEngine.webkit &&
635+
operatingSystem == OperatingSystem.iOs;
636+
637+
/// Flag indication wheterher the input element's position is set.
638+
///
639+
/// See [inputElementNeedsToBePositioned].
640+
bool inputPositioned = false;
588641

589642
Map<String, dynamic> _configuration;
590643

@@ -715,10 +768,16 @@ class HybridTextEditing {
715768
///
716769
/// They are changed depending on the messages coming from method calls:
717770
/// "TextInput.setStyle", "TextInput.setEditableSizeAndTransform".
771+
///
772+
/// In IOS, initial positionig of input element is not done in this method.
773+
/// This method changes the location of the input element if it is already
774+
/// positioned.
775+
/// See [TextEditingElement._delayBeforePositining].
718776
void _setDynamicStyleAttributes(html.HtmlElement domElement) {
719777
if (_editingLocationAndSize != null &&
720-
!(browserEngine == BrowserEngine.webkit &&
721-
operatingSystem == OperatingSystem.iOs)) {
778+
(inputPositioned ||
779+
!(browserEngine == BrowserEngine.webkit &&
780+
operatingSystem == OperatingSystem.iOs))) {
722781
setStyle(domElement);
723782
}
724783
}
@@ -741,6 +800,19 @@ class HybridTextEditing {
741800
..transform = transformCss;
742801
}
743802

803+
// TODO(flutter_web): When the browser closes and re-opens the virtual
804+
// shifts the page in IOS. Call this method from visibility change listener
805+
// attached to body.
806+
/// Set the dom elements location somewhere outside of the screen.
807+
///
808+
/// This is useful for not triggering a scroll when IOS virtual keyboard is
809+
/// coming up.
810+
///
811+
/// See [TextEditingElement._delayBeforePositining].
812+
void setStyleOutsideOfScreen(html.HtmlElement domElement) {
813+
domElement.style.transform = 'translate(-9999px, -9999px)';
814+
}
815+
744816
html.InputElement createInputElement() {
745817
final html.InputElement input = html.InputElement();
746818
_setStaticStyleAttributes(input);

0 commit comments

Comments
 (0)