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

Commit 946b291

Browse files
authored
[dart:ui] Introduce PlatformDispatcher.implicitView (#39553)
This introduces `PlatformDispatcher.implicitView`, a low-level primitive for the framework's bootstrapping. Most code, including the framework after bootstrapping, will use `View.of(context)` instead of this new API. This new primitive will let us deprecate the `window` global. Goals: 1. **Enable multi-window**. The `PlatformDispatcher.implicitView` is nullable. If `null`, the app must create a window to get a view it can draw into. 2. **Backwards compatibility**. For "single window" apps, `PlatformDispatcher.instance.implicitView` should behave as similar to `window` as possible. 1. The `PlatformDispatcher.instance.implicitView.viewId` should be `0`. 1. The `PlatformDispatcher.instance.implicitView` must be available synchronously at root isolate startup. This allows the framework to determine if it can make single window assumptions at startup. 2. The `PlatformDispatcher.instance.implicitView` reference must not change after startup: if it is null at startup, it must always be null; if it is non-null at startup, it must always be non-null. If "single window" app enters headless mode, the implicit view must remain non-null. In the future, the embedder will control whether an implicit view is created: mobile & legacy desktop apps will have an implicit view, multi-window desktop apps won't have an implicit view. This requires updating the engine's embedder API and is out-of-scope for this change. For now, all apps will have an implicit view. Part of flutter/flutter#120306
1 parent 6602fc7 commit 946b291

File tree

17 files changed

+160
-9
lines changed

17 files changed

+160
-9
lines changed

lib/ui/dart_ui.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ typedef CanvasPath Path;
9595
V(IsolateNameServerNatives::RemovePortNameMapping, 1) \
9696
V(NativeStringAttribute::initLocaleStringAttribute, 4) \
9797
V(NativeStringAttribute::initSpellOutStringAttribute, 3) \
98+
V(PlatformConfigurationNativeApi::ImplicitViewEnabled, 0) \
9899
V(PlatformConfigurationNativeApi::DefaultRouteName, 0) \
99100
V(PlatformConfigurationNativeApi::ScheduleFrame, 0) \
100101
V(PlatformConfigurationNativeApi::Render, 1) \

lib/ui/platform_dispatcher.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,36 @@ class PlatformDispatcher {
168168
// A map of opaque platform view identifiers to view configurations.
169169
final Map<Object, ViewConfiguration> _viewConfigurations = <Object, ViewConfiguration>{};
170170

171+
/// The [FlutterView] provided by the engine if the platform is unable to
172+
/// create windows, or, for backwards compatibility.
173+
///
174+
/// If the platform provides an implicit view, it can be used to bootstrap
175+
/// the framework. This is common for platforms designed for single-view
176+
/// applications like mobile devices with a single display.
177+
///
178+
/// Applications and libraries must not rely on this property being set
179+
/// as it may be null depending on the engine's configuration. Instead,
180+
/// consider using [View.of] to lookup the [FlutterView] the current
181+
/// [BuildContext] is drawing into.
182+
///
183+
/// While the properties on the referenced [FlutterView] may change,
184+
/// the reference itself is guaranteed to never change over the lifetime
185+
/// of the application: if this property is null at startup, it will remain
186+
/// so throughout the entire lifetime of the application. If it points to a
187+
/// specific [FlutterView], it will continue to point to the same view until
188+
/// the application is shut down (although the engine may replace or remove
189+
/// the underlying backing surface of the view at its discretion).
190+
///
191+
/// See also:
192+
///
193+
/// * [View.of], for accessing the current view.
194+
/// * [PlatformDisptacher.views] for a list of all [FlutterView]s provided
195+
/// by the platform.
196+
FlutterView? get implicitView => _implicitViewEnabled() ? _views[0] : null;
197+
198+
@Native<Handle Function()>(symbol: 'PlatformConfigurationNativeApi::ImplicitViewEnabled')
199+
external static bool _implicitViewEnabled();
200+
171201
/// A callback that is invoked whenever the [ViewConfiguration] of any of the
172202
/// [views] changes.
173203
///

lib/ui/window.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,7 @@ enum Brightness {
896896
/// * [PlatformDispatcher.views], contains the current list of Flutter windows
897897
/// belonging to the application, including top level application windows like
898898
/// this one.
899+
/// * [PlatformDispatcher.implicitView], this window's view.
899900
final SingletonFlutterWindow window = SingletonFlutterWindow._(0, PlatformDispatcher.instance);
900901

901902
/// Additional data available on each flutter frame.

lib/ui/window/platform_configuration.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
namespace flutter {
2323
namespace {
2424

25+
constexpr int kImplicitViewId = 0;
26+
2527
Dart_Handle ToByteData(const fml::Mapping& buffer) {
2628
return tonic::DartByteData::Create(buffer.GetMapping(), buffer.GetSize());
2729
}
@@ -67,8 +69,13 @@ void PlatformConfiguration::DidCreateIsolate() {
6769
Dart_GetField(library, tonic::ToDart("_drawFrame")));
6870
report_timings_.Set(tonic::DartState::Current(),
6971
Dart_GetField(library, tonic::ToDart("_reportTimings")));
70-
windows_.insert(std::make_pair(
71-
0, std::make_unique<Window>(0, ViewportMetrics{1.0, 0.0, 0.0, -1})));
72+
73+
// TODO(loicsharma): This should only be created if the embedder enables the
74+
// implicit view.
75+
// See: https://github.com/flutter/flutter/issues/120306
76+
windows_.emplace(kImplicitViewId,
77+
std::make_unique<Window>(
78+
kImplicitViewId, ViewportMetrics{1.0, 0.0, 0.0, -1}));
7279
}
7380

7481
void PlatformConfiguration::UpdateLocales(
@@ -427,6 +434,16 @@ Dart_Handle PlatformConfigurationNativeApi::ComputePlatformResolvedLocale(
427434
return tonic::DartConverter<std::vector<std::string>>::ToDart(results);
428435
}
429436

437+
Dart_Handle PlatformConfigurationNativeApi::ImplicitViewEnabled() {
438+
UIDartState::ThrowIfUIOperationsProhibited();
439+
bool enabled = UIDartState::Current()
440+
->platform_configuration()
441+
->client()
442+
->ImplicitViewEnabled();
443+
444+
return Dart_NewBoolean(enabled);
445+
}
446+
430447
std::string PlatformConfigurationNativeApi::DefaultRouteName() {
431448
UIDartState::ThrowIfUIOperationsProhibited();
432449
return UIDartState::Current()

lib/ui/window/platform_configuration.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ enum class AccessibilityFeatureFlag : int32_t {
4949
///
5050
class PlatformConfigurationClient {
5151
public:
52+
//--------------------------------------------------------------------------
53+
/// @brief Whether the platform provides an implicit view. If true,
54+
/// the Framework may assume that it can always render into
55+
/// the view with ID 0.
56+
///
57+
/// This value must not change for the lifetime of the
58+
/// application.
59+
///
60+
virtual bool ImplicitViewEnabled() = 0;
61+
5262
//--------------------------------------------------------------------------
5363
/// @brief The route or path that the embedder requested when the
5464
/// application was launched.
@@ -71,7 +81,7 @@ class PlatformConfigurationClient {
7181
virtual void Render(Scene* scene) = 0;
7282

7383
//--------------------------------------------------------------------------
74-
/// @brief Receives a updated semantics tree from the Framework.
84+
/// @brief Receives an updated semantics tree from the Framework.
7585
///
7686
/// @param[in] update The updated semantic tree to apply.
7787
///
@@ -469,6 +479,8 @@ class PlatformMessageHandlerStorage {
469479
//----------------------------------------------------------------------------
470480
class PlatformConfigurationNativeApi {
471481
public:
482+
static Dart_Handle ImplicitViewEnabled();
483+
472484
static std::string DefaultRouteName();
473485

474486
static void ScheduleFrame();

lib/web_ui/lib/platform_dispatcher.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ abstract class PlatformDispatcher {
3333

3434
Iterable<FlutterView> get views;
3535

36+
FlutterView? get implicitView;
37+
3638
VoidCallback? get onMetricsChanged;
3739
set onMetricsChanged(VoidCallback? callback);
3840

lib/web_ui/lib/src/engine/platform_dispatcher.dart

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,34 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
148148
final Map<Object, ui.ViewConfiguration> _windowConfigurations =
149149
<Object, ui.ViewConfiguration>{};
150150

151+
/// The [FlutterView] provided by the engine if the platform is unable to
152+
/// create windows, or, for backwards compatibility.
153+
///
154+
/// If the platform provides an implicit view, it can be used to bootstrap
155+
/// the framework. This is common for platforms designed for single-view
156+
/// applications like mobile devices with a single display.
157+
///
158+
/// Applications and libraries must not rely on this property being set
159+
/// as it may be null depending on the engine's configuration. Instead,
160+
/// consider using [View.of] to lookup the [FlutterView] the current
161+
/// [BuildContext] is drawing into.
162+
///
163+
/// While the properties on the referenced [FlutterView] may change,
164+
/// the reference itself is guaranteed to never change over the lifetime
165+
/// of the application: if this property is null at startup, it will remain
166+
/// so throughout the entire lifetime of the application. If it points to a
167+
/// specific [FlutterView], it will continue to point to the same view until
168+
/// the application is shut down (although the engine may replace or remove
169+
/// the underlying backing surface of the view at its discretion).
170+
///
171+
/// See also:
172+
///
173+
/// * [View.of], for accessing the current view.
174+
/// * [PlatformDisptacher.views] for a list of all [FlutterView]s provided
175+
/// by the platform.
176+
@override
177+
ui.FlutterView? get implicitView => viewData[kImplicitViewId];
178+
151179
/// A callback that is invoked whenever the platform's [devicePixelRatio],
152180
/// [physicalSize], [padding], [viewInsets], or [systemGestureInsets]
153181
/// values change, for example when the device is rotated or when the
@@ -474,7 +502,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
474502
// TODO(a-wallen): As multi-window support expands, the pop call
475503
// will need to include the view ID. Right now only one view is
476504
// supported.
477-
(viewData[kSingletonViewId]! as EngineFlutterWindow)
505+
(viewData[kImplicitViewId]! as EngineFlutterWindow)
478506
.browserHistory
479507
.exit()
480508
.then((_) {
@@ -579,7 +607,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
579607
// TODO(a-wallen): As multi-window support expands, the navigation call
580608
// will need to include the view ID. Right now only one view is
581609
// supported.
582-
(viewData[kSingletonViewId]! as EngineFlutterWindow)
610+
(viewData[kImplicitViewId]! as EngineFlutterWindow)
583611
.handleNavigationMessage(data)
584612
.then((bool handled) {
585613
if (handled) {
@@ -1179,7 +1207,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
11791207
@override
11801208
String get defaultRouteName {
11811209
return _defaultRouteName ??=
1182-
(viewData[kSingletonViewId]! as EngineFlutterWindow).browserHistory.currentPath;
1210+
(viewData[kImplicitViewId]! as EngineFlutterWindow).browserHistory.currentPath;
11831211
}
11841212

11851213
/// Lazily initialized when the `defaultRouteName` getter is invoked.

lib/web_ui/lib/src/engine/window.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ typedef _HandleMessageCallBack = Future<bool> Function();
2727
/// When set to true, all platform messages will be printed to the console.
2828
const bool debugPrintPlatformMessages = false;
2929

30-
/// The view ID for a singleton flutter window.
31-
const int kSingletonViewId = 0;
30+
/// The view ID for the implicit flutter view provided by the platform.
31+
const int kImplicitViewId = 0;
3232

3333
/// Whether [_customUrlStrategy] has been set or not.
3434
///
@@ -349,7 +349,7 @@ class EngineSingletonFlutterWindow extends EngineFlutterWindow {
349349
/// API surface, providing Web-specific functionality that the standard
350350
/// `dart:ui` version does not.
351351
final EngineSingletonFlutterWindow window =
352-
EngineSingletonFlutterWindow(kSingletonViewId, EnginePlatformDispatcher.instance);
352+
EngineSingletonFlutterWindow(kImplicitViewId, EnginePlatformDispatcher.instance);
353353

354354
/// The Web implementation of [ui.WindowPadding].
355355
class WindowPadding implements ui.WindowPadding {

lib/web_ui/test/window_test.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ void testMain() {
4141
await window.resetHistory();
4242
});
4343

44+
// For now, web always has an implicit view provided by the web engine.
45+
test('EnginePlatformDispatcher.instance.implicitView should be non-null', () async {
46+
expect(EnginePlatformDispatcher.instance.implicitView, isNotNull);
47+
expect(EnginePlatformDispatcher.instance.implicitView?.viewId, 0);
48+
expect(window.viewId, 0);
49+
});
50+
4451
test('window.defaultRouteName should work with JsUrlStrategy', () async {
4552
dynamic state = <dynamic, dynamic>{};
4653
final JsUrlStrategy jsUrlStrategy = JsUrlStrategy(

runtime/runtime_controller.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ RuntimeController::GetPlatformConfigurationIfAvailable() {
298298
return root_isolate ? root_isolate->platform_configuration() : nullptr;
299299
}
300300

301+
// |PlatformConfigurationClient|
302+
bool RuntimeController::ImplicitViewEnabled() {
303+
return client_.ImplicitViewEnabled();
304+
}
305+
301306
// |PlatformConfigurationClient|
302307
std::string RuntimeController::DefaultRouteName() {
303308
return client_.DefaultRouteName();

0 commit comments

Comments
 (0)