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

Commit 9d66e44

Browse files
committed
Address PR comments.
1 parent 315adad commit 9d66e44

10 files changed

+70
-57
lines changed

lib/web_ui/lib/src/engine/embedder.dart

+5-11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import 'dart:async';
66

77
import 'package:ui/ui.dart' as ui;
88

9-
import '../engine.dart' show buildMode, registerHotRestartListener, renderer, window;
9+
import '../engine.dart' show buildMode, renderer, window;
1010
import 'browser_detection.dart';
1111
import 'configuration.dart';
1212
import 'dom.dart';
@@ -45,7 +45,7 @@ class FlutterViewEmbedder {
4545
/// The hostElement is abstracted by an [EmbeddingStrategy] instance, which has
4646
/// different behavior depending on the `hostElement` value:
4747
///
48-
/// - A `null` `hostElement` will allow Flutter to take over the whole screen.
48+
/// - A `null` `hostElement` will cause Flutter to take over the whole page.
4949
/// - A non-`null` `hostElement` will render flutter inside that element.
5050
FlutterViewEmbedder({DomElement? hostElement}) {
5151
// Create an appropriate EmbeddingStrategy using its factory...
@@ -58,16 +58,9 @@ class FlutterViewEmbedder {
5858
));
5959

6060
reset();
61-
62-
assert(() {
63-
// _embeddingStrategy needs to clean-up stuff in the page on hot restart.
64-
registerHotRestartListener(_embeddingStrategy.onHotRestart);
65-
return true;
66-
}());
6761
}
6862

69-
/// The [_embeddingStrategy] abstracts all the DOM manipulations required to
70-
/// embed a Flutter app in the user-supplied `hostElement`.
63+
/// Abstracts all the DOM manipulations required to embed a Flutter app in an user-supplied `hostElement`.
7164
late EmbeddingStrategy _embeddingStrategy;
7265

7366
// The tag name for the root view of the flutter app (glass-pane)
@@ -147,7 +140,7 @@ class FlutterViewEmbedder {
147140

148141
// Initializes the embeddingStrategy so it can host a single-view Flutter app.
149142
_embeddingStrategy.initialize(
150-
embedderMetadata: <String, String>{
143+
hostElementAttributes: <String, String>{
151144
'flt-renderer': '${renderer.rendererTag} ($rendererSelection)',
152145
'flt-build-mode': buildMode,
153146
// TODO(mdebbar): Disable spellcheck until changes in the framework and
@@ -247,6 +240,7 @@ class FlutterViewEmbedder {
247240
/// size if the change is caused by a rotation.
248241
void _metricsDidChange(ui.Size? newSize) {
249242
updateSemanticsScreenProperties();
243+
// TODO(dit): Do not computePhysicalSize twice, https://github.com/flutter/flutter/issues/117036
250244
if (isMobile && !window.isRotation() && textEditing.isEditing) {
251245
window.computeOnScreenKeyboardInsets(true);
252246
EnginePlatformDispatcher.instance.invokeOnMetricsChanged();

lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart

+6-4
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ import 'package:ui/ui.dart' as ui show Size;
1010

1111
import 'dimensions_provider.dart';
1212

13-
/// This class provides the real-time dimensions of a "hostElement".
13+
/// This class provides observable, real-time dimensions of a host element.
1414
///
15-
/// Note: all the measurements returned from this class are potentially
16-
/// *expensive*, and should be cached as needed. Every call to every method on
17-
/// this class WILL perform actual DOM measurements.
15+
/// All the measurements returned from this class are potentially *expensive*,
16+
/// and should be cached as needed. Every call to every method on this class
17+
/// WILL perform actual DOM measurements.
1818
class CustomElementDimensionsProvider extends DimensionsProvider {
19+
/// Creates a [CustomElementDimensionsProvider] from a [_hostElement].
1920
CustomElementDimensionsProvider(this._hostElement) {
2021
// Hook up a resize observer on the hostElement (if supported!).
2122
_hostElementResizeObserver = createDomResizeObserver((
@@ -39,6 +40,7 @@ class CustomElementDimensionsProvider extends DimensionsProvider {
3940
_hostElementResizeObserver?.observe(_hostElement);
4041
}
4142

43+
// The host element that will be used to retrieve (and observe) app size measurements.
4244
final DomElement _hostElement;
4345

4446
// Handle resize events

lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ import 'full_page_dimensions_provider.dart';
1313

1414
/// This class provides the dimensions of the "viewport" in which the app is rendered.
1515
///
16-
///
1716
/// Similarly to the `EmbeddingStrategy`, this class is specialized to handle
1817
/// different sources of information:
1918
///
2019
/// * [FullPageDimensionsProvider] - The default behavior, uses the VisualViewport
2120
/// API to measure, and react to, the dimensions of the full browser window.
2221
/// * [CustomElementDimensionsProvider] - Uses a custom html Element as the source
2322
/// of dimensions, and the ResizeObserver to notify the app of changes.
23+
///
24+
/// All the measurements returned from this class are potentially *expensive*,
25+
/// and should be cached as needed. Every call to every method on this class
26+
/// WILL perform actual DOM measurements.
2427
abstract class DimensionsProvider {
2528
DimensionsProvider();
2629

@@ -40,6 +43,9 @@ abstract class DimensionsProvider {
4043
}
4144

4245
/// Returns the [ui.Size] of the "viewport".
46+
///
47+
/// This function is expensive. It triggers browser layout if there are
48+
/// pending DOM writes.
4349
ui.Size computePhysicalSize();
4450

4551
/// Returns the [WindowPadding] of the keyboard insets (if present).

lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart

+16-5
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,22 @@ import 'dimensions_provider.dart';
1414

1515
/// This class provides the real-time dimensions of a "full page" viewport.
1616
///
17-
/// Note: all the measurements returned from this class are potentially
18-
/// *expensive*, and should be cached as needed. Every call to every method on
19-
/// this class WILL perform actual DOM measurements.
17+
/// All the measurements returned from this class are potentially *expensive*,
18+
/// and should be cached as needed. Every call to every method on this class
19+
/// WILL perform actual DOM measurements.
2020
class FullPageDimensionsProvider extends DimensionsProvider {
21+
/// Constructs a global [FullPageDimensionsProvider].
22+
///
23+
/// Doesn't need any parameters, because all the measurements come from the
24+
/// globally available [DomVisualViewport].
2125
FullPageDimensionsProvider() {
2226
// Determine what 'resize' event we'll be listening to.
2327
// This is needed for older browsers (Firefox < 91, Safari < 13)
28+
// TODO(dit): Clean this up, https://github.com/flutter/flutter/issues/117105
2429
final DomEventTarget resizeEventTarget =
2530
domWindow.visualViewport ?? domWindow;
2631

27-
// Subscribe to the 'resize' event, and convert it to a ui.Size stream...
32+
// Subscribe to the 'resize' event, and convert it to a ui.Size stream.
2833
_domResizeSubscription = DomSubscription(
2934
resizeEventTarget,
3035
'resize',
@@ -37,7 +42,13 @@ class FullPageDimensionsProvider extends DimensionsProvider {
3742
StreamController<ui.Size?>.broadcast();
3843

3944
void _onVisualViewportResize(DomEvent event) {
40-
// This could return [computePhysicalSize]. Is it too costly to compute?
45+
// `event` doesn't contain any size information (as opposed to the custom
46+
// element resize observer). If it did, we could broadcast the physical
47+
// dimensions here and never have to re-measure the app, until the next
48+
// resize event triggers.
49+
// Would it be too costly to broadcast the computed physical size from here,
50+
// and then never re-measure the app?
51+
// Related: https://github.com/flutter/flutter/issues/117036
4152
_onResizeStreamController.add(null);
4253
}
4354

lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy.dart

+9-6
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,25 @@ import 'package:ui/src/engine/dom.dart';
66

77
import 'embedding_strategy.dart';
88

9+
/// An [EmbeddingStrategy] that renders flutter inside a target host element.
10+
///
11+
/// This strategy attempts to minimize DOM modifications outside of the host
12+
/// element, so it plays "nice" with other web frameworks.
913
class CustomElementEmbeddingStrategy extends EmbeddingStrategy {
14+
/// Creates a [CustomElementEmbeddingStrategy] to embed a Flutter view into [_hostElement].
1015
CustomElementEmbeddingStrategy(this._hostElement) {
11-
// Clear children...
12-
while (_hostElement.firstChild != null) {
13-
_hostElement.removeChild(_hostElement.lastChild!);
14-
}
16+
_hostElement.clearChildren();
1517
}
1618

19+
/// The target element in which this strategy will embedd Flutter.
1720
final DomElement _hostElement;
1821

1922
@override
2023
void initialize({
21-
Map<String, String>? embedderMetadata,
24+
Map<String, String>? hostElementAttributes,
2225
}) {
2326
// ignore:avoid_function_literals_in_foreach_calls
24-
embedderMetadata?.entries.forEach((MapEntry<String, String> entry) {
27+
hostElementAttributes?.entries.forEach((MapEntry<String, String> entry) {
2528
_setHostAttribute(entry.key, entry.value);
2629
});
2730
_setHostAttribute('flt-embedding', 'custom-element');

lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/embedding_strategy.dart

+6-16
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,19 @@ import 'package:ui/src/engine/view_embedder/hot_restart_cache_handler.dart';
1010
import 'custom_element_embedding_strategy.dart';
1111
import 'full_page_embedding_strategy.dart';
1212

13-
/// Provides the API that the FlutterViewEmbedder uses to interact with the DOM.
13+
/// Controls how a Flutter app is placed, sized and measured on the page.
1414
///
15-
/// The base class handles "global" stuff that is shared across implementations,
16-
/// like handling hot-restart cleanup.
17-
///
18-
/// This class is specialized to handle different types of DOM embeddings:
15+
/// The base class handles general behavior (like hot-restart cleanup), and then
16+
/// each specialization enables different types of DOM embeddings:
1917
///
2018
/// * [FullPageEmbeddingStrategy] - The default behavior, where flutter takes
21-
/// control of the whole web page. This is how Flutter Web used to operate.
19+
/// control of the whole page.
2220
/// * [CustomElementEmbeddingStrategy] - Flutter is rendered inside a custom host
2321
/// element, provided by the web app programmer through the engine
2422
/// initialization.
2523
abstract class EmbeddingStrategy {
2624
EmbeddingStrategy() {
27-
// Prepare some global stuff...
25+
// Initialize code to handle hot-restart (debug only).
2826
assert(() {
2927
_hotRestartCache = HotRestartCacheHandler();
3028
return true;
@@ -43,7 +41,7 @@ abstract class EmbeddingStrategy {
4341
HotRestartCacheHandler? _hotRestartCache;
4442

4543
void initialize({
46-
Map<String, String>? embedderMetadata,
44+
Map<String, String>? hostElementAttributes,
4745
});
4846

4947
/// Attaches the glassPane element into the hostElement.
@@ -52,14 +50,6 @@ abstract class EmbeddingStrategy {
5250
/// Attaches the resourceHost element into the hostElement.
5351
void attachResourcesHost(DomElement resourceHost, {DomElement? nextTo});
5452

55-
/// A callback that runs when hot restart is triggered.
56-
///
57-
/// This should "clean" up anything handled by the [EmbeddingStrategy] instance.
58-
@mustCallSuper
59-
void onHotRestart() {
60-
// Elements on the [_hotRestartCache] are cleaned up *after* hot-restart.
61-
}
62-
6353
/// Registers a [DomElement] to be cleaned up after hot restart.
6454
@mustCallSuper
6555
void registerElementForCleanup(DomElement element) {

lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@ import 'package:ui/src/engine/util.dart' show assertionsEnabled, setElementStyle
77

88
import 'embedding_strategy.dart';
99

10+
/// An [EmbeddingStrategy] that takes over the whole web page.
11+
///
12+
/// This strategy takes over the <body> element, modifies the viewport meta-tag,
13+
/// and ensures that the root Flutter view covers the whole screen.
1014
class FullPageEmbeddingStrategy extends EmbeddingStrategy {
1115
@override
1216
void initialize({
13-
Map<String, String>? embedderMetadata,
17+
Map<String, String>? hostElementAttributes,
1418
}) {
1519
// ignore:avoid_function_literals_in_foreach_calls
16-
embedderMetadata?.entries.forEach((MapEntry<String, String> entry) {
20+
hostElementAttributes?.entries.forEach((MapEntry<String, String> entry) {
1721
_setHostAttribute(entry.key, entry.value);
1822
});
1923
_setHostAttribute('flt-embedding', 'full-page');
@@ -61,8 +65,6 @@ class FullPageEmbeddingStrategy extends EmbeddingStrategy {
6165
setElementStyle(bodyElement, 'padding', '0');
6266
setElementStyle(bodyElement, 'margin', '0');
6367

64-
// TODO(yjbanov): fix this when KVM I/O support is added. Currently scroll
65-
// using drag, and text selection interferes.
6668
setElementStyle(bodyElement, 'user-select', 'none');
6769
setElementStyle(bodyElement, '-webkit-user-select', 'none');
6870

lib/web_ui/lib/src/engine/view_embedder/hot_restart_cache_handler.dart

+11-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import 'package:meta/meta.dart';
77
import '../dom.dart';
88
import '../safe_browser_api.dart';
99

10-
/// Handles elements that need to be cleared after a hot-restart.
10+
/// Handles [DomElement]s that need to be removed after a hot-restart.
11+
///
12+
/// Elements are stored in an [_elements] list, backed by a global JS variable,
13+
/// named [defaultCacheName].
14+
///
15+
/// When the app hot-restarts (and a new instance of this class is created),
16+
/// everything in [_elements] is removed from the DOM.
1117
class HotRestartCacheHandler {
1218
HotRestartCacheHandler() {
1319
if (_elements.isNotEmpty) {
@@ -16,16 +22,13 @@ class HotRestartCacheHandler {
1622
}
1723
}
1824

19-
/// This is state persistent across hot restarts that indicates what
20-
/// to clear. Delay removal of old visible state to make the
21-
/// transition appear smooth.
25+
/// The name for the JS global variable backing this cache.
2226
@visibleForTesting
2327
static const String defaultCacheName = '__flutter_state';
2428

2529
/// The js-interop layer backing [_elements].
2630
///
27-
/// They're stored in a js global with name [storeName], and removed from the
28-
/// DOM when the app repaints...
31+
/// Elements are stored in a JS global array named [defaultCacheName].
2932
late List<DomElement?>? _jsElements;
3033

3134
/// The elements that need to be cleaned up after hot-restart.
@@ -39,13 +42,15 @@ class HotRestartCacheHandler {
3942
return _jsElements!;
4043
}
4144

45+
/// Removes every element from [_elements] and empties the list.
4246
void _clearAllElements() {
4347
for (final DomElement? element in _elements) {
4448
element?.remove();
4549
}
4650
_elements.clear();
4751
}
4852

53+
/// Registers a [DomElement] to be removed after hot-restart.
4954
void registerElement(DomElement element) {
5055
_elements.add(element);
5156
}

lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ void doTests() {
3030

3131
test('Prepares target environment', () {
3232
strategy.initialize(
33-
embedderMetadata: <String, String>{
33+
hostElementAttributes: <String, String>{
3434
'key-for-testing': 'value-for-testing',
3535
},
3636
);
3737

3838
expect(target.getAttribute('key-for-testing'), 'value-for-testing',
3939
reason:
40-
'Should add embedderMetadata as key=value into target element.');
40+
'Should add attributes as key=value into target element.');
4141
expect(target.getAttribute('flt-embedding'), 'custom-element',
4242
reason:
4343
'Should identify itself as a specific key=value into the target element.');

lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ void doTests() {
3737
expect(userMeta, isNotNull);
3838

3939
strategy.initialize(
40-
embedderMetadata: <String, String>{
40+
hostElementAttributes: <String, String>{
4141
'key-for-testing': 'value-for-testing',
4242
},
4343
);
4444

4545
expect(target.getAttribute('key-for-testing'), 'value-for-testing',
4646
reason:
47-
'Should add embedderMetadata as key=value into target element.');
47+
'Should add attributes as key=value into target element.');
4848
expect(target.getAttribute('flt-embedding'), 'full-page',
4949
reason:
5050
'Should identify itself as a specific key=value into the target element.');

0 commit comments

Comments
 (0)