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

Commit b7ee0fc

Browse files
committed
[web] Migrate Flutter Web DOM usage to JS static interop - 30.
1 parent f8c0dc8 commit b7ee0fc

13 files changed

+98
-60
lines changed

lib/web_ui/lib/src/engine/dom.dart

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,23 @@ extension DomElementExtension on DomElement {
220220
external void remove();
221221
external void setAttribute(String name, Object value);
222222
void appendText(String text) => append(createDomText(text));
223+
external void removeAttribute(String name);
224+
external set tabIndex(int? value);
225+
external int? get tabIndex;
226+
external void focus();
227+
228+
/// [scrollTop] and [scrollLeft] can both return non-integers when using
229+
/// display scaling.
230+
///
231+
/// The setters have a spurious round just in case the supplied [int] flowed
232+
/// from the non-static interop JS API. When all of Flutter Web has been
233+
/// migrated to static interop we can probably remove the rounds.
234+
int get scrollTop => js_util.getProperty(this, 'scrollTop').round();
235+
set scrollTop(int value) =>
236+
js_util.setProperty<num>(this, 'scrollTop', value.round());
237+
int get scrollLeft => js_util.getProperty(this, 'scrollLeft').round();
238+
set scrollLeft(int value) =>
239+
js_util.setProperty<num>(this, 'scrollLeft', value.round());
223240
}
224241

225242
@JS()
@@ -287,6 +304,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
287304
set alignItems(String value) => setProperty('align-items', value);
288305
set margin(String value) => setProperty('margin', value);
289306
set background(String value) => setProperty('background', value);
307+
set touchAction(String value) => setProperty('touch-action', value);
308+
set overflowY(String value) => setProperty('overflow-y', value);
309+
set overflowX(String value) => setProperty('overflow-x', value);
310+
set outline(String value) => setProperty('outline', value);
290311
String get width => getPropertyValue('width');
291312
String get height => getPropertyValue('height');
292313
String get position => getPropertyValue('position');
@@ -342,6 +363,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration {
342363
String get alignItems => getPropertyValue('align-items');
343364
String get margin => getPropertyValue('margin');
344365
String get background => getPropertyValue('background');
366+
String get touchAction => getPropertyValue('touch-action');
367+
String get overflowY => getPropertyValue('overflow-y');
368+
String get overflowX => getPropertyValue('overflow-x');
369+
String get outline => getPropertyValue('outline');
345370

346371
external String getPropertyValue(String property);
347372
void setProperty(String propertyName, String value, [String? priority]) {
@@ -359,7 +384,6 @@ class DomHTMLElement extends DomElement {}
359384

360385
extension DomHTMLElementExtension on DomHTMLElement {
361386
int get offsetWidth => js_util.getProperty<num>(this, 'offsetWidth') as int;
362-
external void focus();
363387
}
364388

365389
@JS()
@@ -977,6 +1001,23 @@ extension DomTouchExtension on DomTouch {
9771001
external num? get clientY;
9781002
}
9791003

1004+
@JS()
1005+
@staticInterop
1006+
class DomHTMLInputElement extends DomHTMLElement {}
1007+
1008+
extension DomHTMLInputElementExtension on DomHTMLInputElement {
1009+
external set type(String? value);
1010+
external set max(String? value);
1011+
external set min(String value);
1012+
external set value(String? value);
1013+
external String? get value;
1014+
external bool? get disabled;
1015+
external set disabled(bool? value);
1016+
}
1017+
1018+
DomHTMLInputElement createDomHTMLInputElement() =>
1019+
domDocument.createElement('input') as DomHTMLInputElement;
1020+
9801021
Object? domGetConstructor(String constructorName) =>
9811022
js_util.getProperty(domWindow, constructorName);
9821023

lib/web_ui/lib/src/engine/keyboard_binding.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:html' as html;
6-
75
import 'package:ui/ui.dart' as ui;
86

97
import '../engine.dart' show registerHotRestartListener;
@@ -117,7 +115,7 @@ class KeyboardBinding {
117115
if (_debugLogKeyEvents) {
118116
print(event.type);
119117
}
120-
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event as html.Event)) {
118+
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) {
121119
return handler(event);
122120
}
123121
return null;

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:html' as html;
65
import 'dart:math' as math;
76

87
import 'package:meta/meta.dart';
@@ -293,7 +292,7 @@ abstract class _BaseAdapter {
293292
// Report the event to semantics. This information is used to debounce
294293
// browser gestures. Semantics tells us whether it is safe to forward
295294
// the event to the framework.
296-
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event as html.Event)) {
295+
if (EngineSemanticsOwner.instance.receiveGlobalEvent(event)) {
297296
handler(event);
298297
}
299298
}

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
// framework. Currently the framework does not report the
1212
// grouping of radio buttons.
1313

14-
import 'dart:html' as html;
15-
1614
import 'package:ui/ui.dart' as ui;
1715

16+
import '../dom.dart';
1817
import 'semantics.dart';
1918

2019
/// The specific type of checkable control.
@@ -104,7 +103,7 @@ class Checkable extends RoleManager {
104103

105104
void _updateDisabledAttribute() {
106105
if (semanticsObject.enabledState() == EnabledState.disabled) {
107-
final html.Element element = semanticsObject.element;
106+
final DomElement element = semanticsObject.element;
108107
element
109108
..setAttribute('aria-disabled', 'true')
110109
..setAttribute('disabled', 'true');
@@ -114,7 +113,7 @@ class Checkable extends RoleManager {
114113
}
115114

116115
void _removeDisabledAttribute() {
117-
final html.Element element = semanticsObject.element;
116+
final DomElement element = semanticsObject.element;
118117
element..removeAttribute('aria-disabled')..removeAttribute('disabled');
119118
}
120119
}

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:html' as html;
6-
5+
import '../dom.dart';
76
import 'semantics.dart';
87

98
/// Represents semantic objects that deliver information in a visual manner.
@@ -18,13 +17,13 @@ class ImageRoleManager extends RoleManager {
1817
/// The element with role="img" and aria-label could block access to all
1918
/// children elements, therefore create an auxiliary element and describe the
2019
/// image in that if the semantic object have child nodes.
21-
html.Element? _auxiliaryImageElement;
20+
DomElement? _auxiliaryImageElement;
2221

2322
@override
2423
void update() {
2524
if (semanticsObject.isVisualOnly && semanticsObject.hasChildren) {
2625
if (_auxiliaryImageElement == null) {
27-
_auxiliaryImageElement = html.Element.tag('flt-semantics-img');
26+
_auxiliaryImageElement = domDocument.createElement('flt-semantics-img');
2827
// Absolute positioning and sizing of leaf text elements confuses
2928
// VoiceOver. So we let the browser size the value node. The node will
3029
// still have a bigger tap area. However, if the node is a parent to
@@ -54,7 +53,7 @@ class ImageRoleManager extends RoleManager {
5453
}
5554
}
5655

57-
void _setLabel(html.Element? element) {
56+
void _setLabel(DomElement? element) {
5857
if (semanticsObject.hasLabel) {
5958
element!.setAttribute('aria-label', semanticsObject.label!);
6059
}
@@ -69,7 +68,7 @@ class ImageRoleManager extends RoleManager {
6968

7069
void _cleanupElement() {
7170
semanticsObject.setAriaRole('img', false);
72-
semanticsObject.element.attributes.remove('aria-label');
71+
semanticsObject.element.removeAttribute('aria-label');
7372
}
7473

7574
@override

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:html' as html;
6-
75
import 'package:ui/ui.dart' as ui;
86

7+
import '../dom.dart';
98
import '../platform_dispatcher.dart';
9+
import '../safe_browser_api.dart';
1010
import 'semantics.dart';
1111

1212
/// Adds increment/decrement event handling to a semantics object.
@@ -20,7 +20,7 @@ import 'semantics.dart';
2020
/// gestures must be interpreted by the Flutter framework.
2121
class Incrementable extends RoleManager {
2222
/// The HTML element used to render semantics to the browser.
23-
final html.InputElement _element = html.InputElement();
23+
final DomHTMLInputElement _element = createDomHTMLInputElement();
2424

2525
/// The value used by the input element.
2626
///
@@ -49,7 +49,7 @@ class Incrementable extends RoleManager {
4949
_element.type = 'range';
5050
_element.setAttribute('role', 'slider');
5151

52-
_element.addEventListener('change', (_) {
52+
_element.addEventListener('change', allowInterop((_) {
5353
if (_element.disabled!) {
5454
return;
5555
}
@@ -64,7 +64,7 @@ class Incrementable extends RoleManager {
6464
EnginePlatformDispatcher.instance.invokeOnSemanticsAction(
6565
semanticsObject.id, ui.SemanticsAction.decrease, null);
6666
}
67-
});
67+
}));
6868

6969
// Store the callback as a closure because Dart does not guarantee that
7070
// tear-offs produce the same function object.

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:html' as html;
6-
75
import 'package:ui/ui.dart' as ui;
86

97
import '../configuration.dart';
8+
import '../dom.dart';
109
import 'semantics.dart';
1110

1211
/// Renders [_label] and [_value] to the semantics DOM.
@@ -47,7 +46,7 @@ class LabelAndValue extends RoleManager {
4746
/// its label is not reachable via accessibility focus. This happens, for
4847
/// example in popup dialogs, such as the alert dialog. The text of the
4948
/// alert is supplied as a label on the parent node.
50-
html.Element? _auxiliaryValueElement;
49+
DomElement? _auxiliaryValueElement;
5150

5251
@override
5352
void update() {
@@ -90,7 +89,7 @@ class LabelAndValue extends RoleManager {
9089
}
9190

9291
if (_auxiliaryValueElement == null) {
93-
_auxiliaryValueElement = html.Element.tag('flt-semantics-value');
92+
_auxiliaryValueElement = domDocument.createElement('flt-semantics-value');
9493
// Absolute positioning and sizing of leaf text elements confuses
9594
// VoiceOver. So we let the browser size the value node. The node will
9695
// still have a bigger tap area. However, if the node is a parent to other
@@ -119,7 +118,7 @@ class LabelAndValue extends RoleManager {
119118
_auxiliaryValueElement!.remove();
120119
_auxiliaryValueElement = null;
121120
}
122-
semanticsObject.element.attributes.remove('aria-label');
121+
semanticsObject.element.removeAttribute('aria-label');
123122
semanticsObject.setAriaRole('heading', false);
124123
}
125124

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import '../dom.dart';
56
import 'semantics.dart';
67

78
/// Manages semantics configurations that represent live regions.
@@ -31,7 +32,7 @@ class LiveRegion extends RoleManager {
3132
}
3233

3334
void _cleanupDom() {
34-
semanticsObject.element.attributes.remove('aria-live');
35+
semanticsObject.element.removeAttribute('aria-live');
3536
}
3637

3738
@override

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'dart:html' as html;
6-
75
import 'package:ui/ui.dart' as ui;
86

7+
import '../dom.dart';
98
import '../platform_dispatcher.dart';
9+
import '../safe_browser_api.dart';
1010
import 'semantics.dart';
1111

1212
/// Implements vertical and horizontal scrolling functionality for semantics
@@ -37,7 +37,7 @@ class Scrollable extends RoleManager {
3737
///
3838
/// This gesture is converted to [ui.SemanticsAction.scrollUp] or
3939
/// [ui.SemanticsAction.scrollDown], depending on the direction.
40-
html.EventListener? _scrollListener;
40+
DomEventListener? _scrollListener;
4141

4242
/// The value of the "scrollTop" or "scrollLeft" property of this object's
4343
/// [element] that has zero offset relative to the [scrollPosition].
@@ -107,9 +107,9 @@ class Scrollable extends RoleManager {
107107
};
108108
semanticsObject.owner.addGestureModeListener(_gestureModeListener);
109109

110-
_scrollListener = (_) {
110+
_scrollListener = allowInterop((_) {
111111
_recomputeScrollPosition();
112-
};
112+
});
113113
semanticsObject.element.addEventListener('scroll', _scrollListener);
114114
}
115115
}
@@ -138,7 +138,7 @@ class Scrollable extends RoleManager {
138138
// This value is arbitrary.
139139
const int _canonicalNeutralScrollPosition = 10;
140140

141-
final html.Element element = semanticsObject.element;
141+
final DomElement element = semanticsObject.element;
142142
if (semanticsObject.isVerticalScrollContainer) {
143143
element.scrollTop = _canonicalNeutralScrollPosition;
144144
// Read back because the effective value depends on the amount of content.
@@ -159,7 +159,7 @@ class Scrollable extends RoleManager {
159159
}
160160

161161
void _gestureModeDidChange() {
162-
final html.Element element = semanticsObject.element;
162+
final DomElement element = semanticsObject.element;
163163
switch (semanticsObject.owner.gestureMode) {
164164
case GestureMode.browserGestures:
165165
// overflow:scroll will cause the browser report "scroll" events when
@@ -190,7 +190,7 @@ class Scrollable extends RoleManager {
190190

191191
@override
192192
void dispose() {
193-
final html.CssStyleDeclaration style = semanticsObject.element.style;
193+
final DomCSSStyleDeclaration style = semanticsObject.element.style;
194194
assert(_gestureModeListener != null);
195195
style.removeProperty('overflowY');
196196
style.removeProperty('overflowX');

0 commit comments

Comments
 (0)