Skip to content

Commit 1eea728

Browse files
authored
[web] Add views proxy and getInitialData. (flutter#49320)
Adds a Dart API so Application/Plugin programmers can retrieve the `initialData` configuration value that may be passed when adding a view from JS in a multiViewEnabled app. When adding a view to an app like this: ```js flutterApp.addView({ hostElement: someElement, initialData: { randomUUID: globalThis.crypto.randomUUID(), } }); ``` `initialData` can be accessed from Dart by defining a JS-interop class like: ```dart import 'dart:js_interop'; // The JS-interop definition of the `initialData` object passed to the views of this app. @js() @staticInterop class InitialData {} /// The attributes of the [InitialData] object. extension InitialDataExtension on InitialData { external String? get randomUUID; } ``` And then, from the code of the application: ```dart ... Widget build(BuildContext context) { final int viewId = View.of(context).viewId; final InitialData? data = ui_web.views.getInitialData(viewId) as InitialData?; return Text('${data?.randomUUID}'); } ... ``` ## Testing Will add unit tests once naming is sorted out :) [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent eb46604 commit 1eea728

File tree

5 files changed

+150
-3
lines changed

5 files changed

+150
-3
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6143,6 +6143,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/ui.dart + ../../../flutter/LICENSE
61436143
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web.dart + ../../../flutter/LICENSE
61446144
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart + ../../../flutter/LICENSE
61456145
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/benchmarks.dart + ../../../flutter/LICENSE
6146+
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/flutter_views_proxy.dart + ../../../flutter/LICENSE
61466147
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/images.dart + ../../../flutter/LICENSE
61476148
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart + ../../../flutter/LICENSE
61486149
ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/platform_location.dart + ../../../flutter/LICENSE
@@ -8997,6 +8998,7 @@ FILE: ../../../flutter/lib/web_ui/lib/ui.dart
89978998
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web.dart
89988999
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart
89999000
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/benchmarks.dart
9001+
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/flutter_views_proxy.dart
90009002
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/images.dart
90019003
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart
90029004
FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/navigation/platform_location.dart

lib/web_ui/lib/src/engine/js_interop/js_app.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ extension JsFlutterViewOptionsExtension on JsFlutterViewOptions {
2525
return _hostElement!;
2626
}
2727

28-
@JS('initialData')
29-
external JSObject? get _initialData;
30-
Object? get initialData => _initialData?.toObjectDeep;
28+
external JSAny? get initialData;
3129
}
3230

3331
/// The public JS API of a running Flutter Web App.

lib/web_ui/lib/ui_web/src/ui_web.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ library ui_web;
1010

1111
export 'ui_web/asset_manager.dart';
1212
export 'ui_web/benchmarks.dart';
13+
export 'ui_web/flutter_views_proxy.dart';
1314
export 'ui_web/images.dart';
1415
export 'ui_web/initialization.dart';
1516
export 'ui_web/navigation/platform_location.dart';
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
import 'dart:js_interop';
5+
import 'package:ui/src/engine.dart';
6+
7+
/// Exposes web-only functionality for this app's `FlutterView`s objects.
8+
final FlutterViewManagerProxy views = FlutterViewManagerProxy(
9+
viewManager: EnginePlatformDispatcher.instance.viewManager,
10+
);
11+
12+
/// This class exposes web-only attributes for the registered views in a multi-view app.
13+
class FlutterViewManagerProxy {
14+
FlutterViewManagerProxy({
15+
required FlutterViewManager viewManager,
16+
}) : _viewManager = viewManager;
17+
// The proxied viewManager instance.
18+
final FlutterViewManager _viewManager;
19+
20+
/// Returns the `hostElement` configuration value passed from JS when `viewId` was added.
21+
///
22+
/// This is useful for plugins and apps to have a safe DOM Element where they
23+
/// can add their own custom HTML elements (for example: file inputs for the
24+
/// file_selector plugin).
25+
JSAny? getHostElement(int viewId) {
26+
return _viewManager.getOptions(viewId)?.hostElement as JSAny?;
27+
}
28+
29+
/// Returns the `initialData` configuration value passed from JS when `viewId` was added.
30+
///
31+
/// Developers can access the initial data from Dart in two ways:
32+
/// * Defining their own `staticInterop` class that describes what you're
33+
/// passing to your views, to retain type safety on Dart (preferred).
34+
/// * Calling [NullableUndefineableJSAnyExtension.dartify] and accessing the
35+
/// returned object as if it were a [Map] (not recommended).
36+
JSAny? getInitialData(int viewId) {
37+
return _viewManager.getOptions(viewId)?.initialData;
38+
}
39+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:js_interop';
7+
import 'dart:math' as math;
8+
9+
import 'package:test/bootstrap/browser.dart';
10+
import 'package:test/test.dart';
11+
import 'package:ui/src/engine.dart';
12+
import 'package:ui/ui_web/src/ui_web.dart';
13+
14+
void main() {
15+
internalBootstrapBrowserTest(() => doTests);
16+
}
17+
18+
Future<void> doTests() async {
19+
group('FlutterViewManagerProxy', () {
20+
final EnginePlatformDispatcher platformDispatcher =
21+
EnginePlatformDispatcher.instance;
22+
final FlutterViewManager viewManager =
23+
FlutterViewManager(platformDispatcher);
24+
final FlutterViewManagerProxy views =
25+
FlutterViewManagerProxy(viewManager: viewManager);
26+
27+
late EngineFlutterView view;
28+
late int viewId;
29+
late DomElement hostElement;
30+
31+
int registerViewWithOptions(Map<String, Object?> options) {
32+
final JsFlutterViewOptions jsOptions =
33+
options.toJSAnyDeep as JsFlutterViewOptions;
34+
viewManager.registerView(view, jsViewOptions: jsOptions);
35+
return viewId;
36+
}
37+
38+
setUp(() {
39+
view = EngineFlutterView(platformDispatcher, createDomElement('div'));
40+
viewId = view.viewId;
41+
hostElement = createDomElement('div');
42+
});
43+
44+
tearDown(() {
45+
viewManager.unregisterView(viewId);
46+
});
47+
48+
group('getHostElement', () {
49+
test('null when viewId is unknown', () {
50+
final JSAny? element = views.getHostElement(33930);
51+
expect(element, isNull);
52+
});
53+
54+
test('can retrieve hostElement for a known view', () {
55+
final int viewId = registerViewWithOptions(<String, Object?>{
56+
'hostElement': hostElement,
57+
});
58+
59+
final JSAny? element = views.getHostElement(viewId);
60+
61+
expect(element, hostElement);
62+
});
63+
});
64+
65+
group('getInitialData', () {
66+
test('null when viewId is unknown', () {
67+
final JSAny? element = views.getInitialData(33930);
68+
expect(element, isNull);
69+
});
70+
71+
test('can retrieve initialData for a known view', () {
72+
final int viewId = registerViewWithOptions(<String, Object?>{
73+
'hostElement': hostElement,
74+
'initialData': <String, Object?>{
75+
'someInt': 42,
76+
'someString': 'A String',
77+
'decimals': <double>[math.pi, math.e],
78+
},
79+
});
80+
81+
final InitialData? element =
82+
views.getInitialData(viewId) as InitialData?;
83+
84+
expect(element, isNotNull);
85+
expect(element!.someInt, 42);
86+
expect(element.someString, 'A String');
87+
expect(element.decimals, <double>[math.pi, math.e]);
88+
});
89+
});
90+
});
91+
}
92+
93+
// The JS-interop definition of the `initialData` object passed to the views of this app.
94+
@JS()
95+
@staticInterop
96+
class InitialData {}
97+
98+
/// The attributes of the [InitialData] object.
99+
extension InitialDataExtension on InitialData {
100+
external int get someInt;
101+
external String? get someString;
102+
103+
@JS('decimals')
104+
external JSArray<JSNumber> get _decimals;
105+
List<double> get decimals =>
106+
_decimals.toDart.map((JSNumber e) => e.toDartDouble).toList();
107+
}

0 commit comments

Comments
 (0)