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

Commit d437810

Browse files
authored
[Embedder API] Add view (#51523)
Adds `FlutterEngineAddView` to the embedder API. This will be used to add a view. Design doc: https://flutter.dev/go/multi-view-embedder-apis Part of flutter/flutter#144806 Part of flutter/flutter#142845 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent b1dbb43 commit d437810

File tree

6 files changed

+742
-37
lines changed

6 files changed

+742
-37
lines changed

runtime/runtime_controller.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ void RuntimeController::AddView(int64_t view_id,
179179

180180
platform_data_.viewport_metrics_for_views[view_id] = view_metrics;
181181
bool added = platform_configuration->AddView(view_id, view_metrics);
182+
if (added) {
183+
ScheduleFrame();
184+
}
185+
182186
callback(added);
183187
}
184188

runtime/runtime_controller.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ class RuntimeController : public PlatformConfigurationClient {
193193
/// flushed to the isolate when it starts. Calling `RemoveView`
194194
/// before the isolate is launched cancels the add operation.
195195
///
196+
/// If the isolate is running, a frame will be scheduled.
196197
///
197198
/// @param[in] view_id The ID of the new view.
198199
/// @param[in] viewport_metrics The initial viewport metrics for the view.

shell/platform/embedder/embedder.cc

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,6 +2183,66 @@ FlutterEngineResult FlutterEngineRunInitialized(
21832183
return kSuccess;
21842184
}
21852185

2186+
FLUTTER_EXPORT
2187+
FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine)
2188+
engine,
2189+
const FlutterAddViewInfo* info) {
2190+
if (!engine) {
2191+
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
2192+
}
2193+
if (!info || !info->view_metrics || !info->add_view_callback) {
2194+
return LOG_EMBEDDER_ERROR(kInvalidArguments,
2195+
"Add view info handle was invalid.");
2196+
}
2197+
2198+
FlutterViewId view_id = info->view_id;
2199+
if (view_id == kFlutterImplicitViewId) {
2200+
return LOG_EMBEDDER_ERROR(
2201+
kInvalidArguments,
2202+
"Add view info was invalid. The implicit view cannot be added.");
2203+
}
2204+
if (SAFE_ACCESS(info->view_metrics, view_id, kFlutterImplicitViewId) !=
2205+
view_id) {
2206+
if (view_id == kFlutterImplicitViewId) {
2207+
return LOG_EMBEDDER_ERROR(kInvalidArguments,
2208+
"Add view info was invalid. The info and "
2209+
"window metric view IDs must match.");
2210+
}
2211+
}
2212+
2213+
// TODO(loicsharma): Return an error if the engine was initialized with
2214+
// callbacks that are incompatible with multiple views.
2215+
// https://github.com/flutter/flutter/issues/144806
2216+
2217+
std::variant<flutter::ViewportMetrics, std::string> metrics_or_error =
2218+
MakeViewportMetricsFromWindowMetrics(info->view_metrics);
2219+
2220+
if (const std::string* error = std::get_if<std::string>(&metrics_or_error)) {
2221+
return LOG_EMBEDDER_ERROR(kInvalidArguments, error->c_str());
2222+
}
2223+
2224+
auto metrics = std::get<flutter::ViewportMetrics>(metrics_or_error);
2225+
2226+
// The engine must be running to add a view.
2227+
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
2228+
if (!embedder_engine->IsValid()) {
2229+
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
2230+
}
2231+
2232+
flutter::Shell::AddViewCallback callback =
2233+
[c_callback = info->add_view_callback,
2234+
user_data = info->user_data](bool added) {
2235+
FlutterAddViewResult result = {};
2236+
result.struct_size = sizeof(FlutterAddViewResult);
2237+
result.added = added;
2238+
result.user_data = user_data;
2239+
c_callback(&result);
2240+
};
2241+
2242+
embedder_engine->GetShell().AddView(view_id, metrics, callback);
2243+
return kSuccess;
2244+
}
2245+
21862246
FLUTTER_EXPORT
21872247
FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine)
21882248
engine,
@@ -2201,6 +2261,10 @@ FlutterEngineResult FlutterEngineRemoveView(FLUTTER_API_SYMBOL(FlutterEngine)
22012261
"Remove view info was invalid. The implicit view cannot be removed.");
22022262
}
22032263

2264+
// TODO(loicsharma): Return an error if the engine was initialized with
2265+
// callbacks that are incompatible with multiple views.
2266+
// https://github.com/flutter/flutter/issues/144806
2267+
22042268
// The engine must be running to remove a view.
22052269
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
22062270
if (!embedder_engine->IsValid()) {

shell/platform/embedder/embedder.h

Lines changed: 116 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,86 @@ typedef struct {
831831
};
832832
} FlutterRendererConfig;
833833

834+
/// Display refers to a graphics hardware system consisting of a framebuffer,
835+
/// typically a monitor or a screen. This ID is unique per display and is
836+
/// stable until the Flutter application restarts.
837+
typedef uint64_t FlutterEngineDisplayId;
838+
839+
typedef struct {
840+
/// The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
841+
size_t struct_size;
842+
/// Physical width of the window.
843+
size_t width;
844+
/// Physical height of the window.
845+
size_t height;
846+
/// Scale factor for the physical screen.
847+
double pixel_ratio;
848+
/// Horizontal physical location of the left side of the window on the screen.
849+
size_t left;
850+
/// Vertical physical location of the top of the window on the screen.
851+
size_t top;
852+
/// Top inset of window.
853+
double physical_view_inset_top;
854+
/// Right inset of window.
855+
double physical_view_inset_right;
856+
/// Bottom inset of window.
857+
double physical_view_inset_bottom;
858+
/// Left inset of window.
859+
double physical_view_inset_left;
860+
/// The identifier of the display the view is rendering on.
861+
FlutterEngineDisplayId display_id;
862+
/// The view that this event is describing.
863+
int64_t view_id;
864+
} FlutterWindowMetricsEvent;
865+
866+
typedef struct {
867+
/// The size of this struct.
868+
/// Must be sizeof(FlutterAddViewResult).
869+
size_t struct_size;
870+
871+
/// True if the add view operation succeeded.
872+
bool added;
873+
874+
/// The |FlutterAddViewInfo.user_data|.
875+
void* user_data;
876+
} FlutterAddViewResult;
877+
878+
/// The callback invoked by the engine when the engine has attempted to add a
879+
/// view.
880+
///
881+
/// The |FlutterAddViewResult| is only guaranteed to be valid during this
882+
/// callback.
883+
typedef void (*FlutterAddViewCallback)(const FlutterAddViewResult* result);
884+
885+
typedef struct {
886+
/// The size of this struct.
887+
/// Must be sizeof(FlutterAddViewInfo).
888+
size_t struct_size;
889+
890+
/// The identifier for the view to add. This must be unique.
891+
FlutterViewId view_id;
892+
893+
/// The view's properties.
894+
///
895+
/// The metric's |view_id| must match this struct's |view_id|.
896+
const FlutterWindowMetricsEvent* view_metrics;
897+
898+
/// A baton that is not interpreted by the engine in any way. It will be given
899+
/// back to the embedder in |add_view_callback|. Embedder resources may be
900+
/// associated with this baton.
901+
void* user_data;
902+
903+
/// Called once the engine has attempted to add the view. This callback is
904+
/// required.
905+
///
906+
/// The embedder/app must not use the view until the callback is invoked with
907+
/// an `added` value of `true`.
908+
///
909+
/// This callback is invoked on an internal engine managed thread. Embedders
910+
/// must re-thread if necessary.
911+
FlutterAddViewCallback add_view_callback;
912+
} FlutterAddViewInfo;
913+
834914
typedef struct {
835915
/// The size of this struct.
836916
/// Must be sizeof(FlutterRemoveViewResult).
@@ -846,7 +926,8 @@ typedef struct {
846926
/// The callback invoked by the engine when the engine has attempted to remove
847927
/// a view.
848928
///
849-
/// The |FlutterRemoveViewResult| will be deallocated once the callback returns.
929+
/// The |FlutterRemoveViewResult| is only guaranteed to be valid during this
930+
/// callback.
850931
typedef void (*FlutterRemoveViewCallback)(
851932
const FlutterRemoveViewResult* /* result */);
852933

@@ -878,38 +959,6 @@ typedef struct {
878959
FlutterRemoveViewCallback remove_view_callback;
879960
} FlutterRemoveViewInfo;
880961

881-
/// Display refers to a graphics hardware system consisting of a framebuffer,
882-
/// typically a monitor or a screen. This ID is unique per display and is
883-
/// stable until the Flutter application restarts.
884-
typedef uint64_t FlutterEngineDisplayId;
885-
886-
typedef struct {
887-
/// The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
888-
size_t struct_size;
889-
/// Physical width of the window.
890-
size_t width;
891-
/// Physical height of the window.
892-
size_t height;
893-
/// Scale factor for the physical screen.
894-
double pixel_ratio;
895-
/// Horizontal physical location of the left side of the window on the screen.
896-
size_t left;
897-
/// Vertical physical location of the top of the window on the screen.
898-
size_t top;
899-
/// Top inset of window.
900-
double physical_view_inset_top;
901-
/// Right inset of window.
902-
double physical_view_inset_right;
903-
/// Bottom inset of window.
904-
double physical_view_inset_bottom;
905-
/// Left inset of window.
906-
double physical_view_inset_left;
907-
/// The identifier of the display the view is rendering on.
908-
FlutterEngineDisplayId display_id;
909-
/// The view that this event is describing.
910-
int64_t view_id;
911-
} FlutterWindowMetricsEvent;
912-
913962
/// The phase of the pointer event.
914963
typedef enum {
915964
kCancel,
@@ -1851,7 +1900,9 @@ typedef struct {
18511900
/// Callback invoked by the engine to composite the contents of each layer
18521901
/// onto the implicit view.
18531902
///
1854-
/// DEPRECATED: Use |present_view_callback| to support multiple views.
1903+
/// DEPRECATED: Use `present_view_callback` to support multiple views.
1904+
/// If this callback is provided, `FlutterEngineAddView` and
1905+
/// `FlutterEngineRemoveView` should not be used.
18551906
///
18561907
/// Only one of `present_layers_callback` and `present_view_callback` may be
18571908
/// provided. Providing both is an error and engine initialization will
@@ -2175,6 +2226,10 @@ typedef struct {
21752226
/// `update_semantics_callback`, and
21762227
/// `update_semantics_callback2` may be provided; the others
21772228
/// should be set to null.
2229+
///
2230+
/// This callback is incompatible with multiple views. If this
2231+
/// callback is provided, `FlutterEngineAddView` and
2232+
/// `FlutterEngineRemoveView` should not be used.
21782233
FlutterUpdateSemanticsNodeCallback update_semantics_node_callback;
21792234
/// The legacy callback invoked by the engine in order to give the embedder
21802235
/// the chance to respond to updates to semantics custom actions from the Dart
@@ -2191,6 +2246,10 @@ typedef struct {
21912246
/// `update_semantics_callback`, and
21922247
/// `update_semantics_callback2` may be provided; the others
21932248
/// should be set to null.
2249+
///
2250+
/// This callback is incompatible with multiple views. If this
2251+
/// callback is provided, `FlutterEngineAddView` and
2252+
/// `FlutterEngineRemoveView` should not be used.
21942253
FlutterUpdateSemanticsCustomActionCallback
21952254
update_semantics_custom_action_callback;
21962255
/// Path to a directory used to store data that is cached across runs of a
@@ -2340,6 +2399,10 @@ typedef struct {
23402399
/// `update_semantics_callback`, and
23412400
/// `update_semantics_callback2` may be provided; the others
23422401
/// must be set to null.
2402+
///
2403+
/// This callback is incompatible with multiple views. If this
2404+
/// callback is provided, `FlutterEngineAddView` and
2405+
/// `FlutterEngineRemoveView` should not be used.
23432406
FlutterUpdateSemanticsCallback update_semantics_callback;
23442407

23452408
/// The callback invoked by the engine in order to give the embedder the
@@ -2505,6 +2568,25 @@ FLUTTER_EXPORT
25052568
FlutterEngineResult FlutterEngineRunInitialized(
25062569
FLUTTER_API_SYMBOL(FlutterEngine) engine);
25072570

2571+
//------------------------------------------------------------------------------
2572+
/// @brief Adds a view.
2573+
///
2574+
/// This is an asynchronous operation. The view should not be used
2575+
/// until the |add_view_callback| is invoked with an `added` of
2576+
/// `true`.
2577+
///
2578+
/// @param[in] engine A running engine instance.
2579+
/// @param[in] info The add view arguments. This can be deallocated
2580+
/// once |FlutterEngineAddView| returns, before
2581+
/// |add_view_callback| is invoked.
2582+
///
2583+
/// @return The result of *starting* the asynchronous operation. If
2584+
/// `kSuccess`, the |add_view_callback| will be invoked.
2585+
FLUTTER_EXPORT
2586+
FlutterEngineResult FlutterEngineAddView(FLUTTER_API_SYMBOL(FlutterEngine)
2587+
engine,
2588+
const FlutterAddViewInfo* info);
2589+
25082590
//------------------------------------------------------------------------------
25092591
/// @brief Removes a view.
25102592
///

shell/platform/embedder/fixtures/main.dart

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,28 @@ void render_implicit_view() {
843843
PlatformDispatcher.instance.scheduleFrame();
844844
}
845845

846+
@pragma('vm:entry-point')
847+
void render_all_views() {
848+
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
849+
for (final FlutterView view in PlatformDispatcher.instance.views) {
850+
final Size size = Size(800.0, 600.0);
851+
final Color red = Color.fromARGB(127, 255, 0, 0);
852+
853+
final SceneBuilder builder = SceneBuilder();
854+
855+
builder.pushOffset(0.0, 0.0);
856+
857+
builder.addPicture(
858+
Offset(0.0, 0.0), CreateColoredBox(red, size));
859+
860+
builder.pop();
861+
862+
view.render(builder.build());
863+
}
864+
};
865+
PlatformDispatcher.instance.scheduleFrame();
866+
}
867+
846868
@pragma('vm:entry-point')
847869
void render_gradient() {
848870
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
@@ -1307,6 +1329,18 @@ void can_schedule_frame() {
13071329
signalNativeTest();
13081330
}
13091331

1332+
@pragma('vm:entry-point')
1333+
void add_view_schedules_frame() {
1334+
PlatformDispatcher.instance.onBeginFrame = (Duration beginTime) {
1335+
for (final FlutterView view in PlatformDispatcher.instance.views) {
1336+
if (view.viewId == 123) {
1337+
signalNativeCount(beginTime.inMicroseconds);
1338+
}
1339+
}
1340+
};
1341+
signalNativeTest();
1342+
}
1343+
13101344
void drawSolidColor(Color c) {
13111345
PlatformDispatcher.instance.onBeginFrame = (Duration duration) {
13121346
final SceneBuilder builder = SceneBuilder();
@@ -1399,6 +1433,20 @@ void window_metrics_event_view_id() {
13991433
signalNativeTest();
14001434
}
14011435

1436+
@pragma('vm:entry-point')
1437+
void window_metrics_event_all_view_ids() {
1438+
PlatformDispatcher.instance.onMetricsChanged = () {
1439+
final List<int> viewIds =
1440+
PlatformDispatcher.instance.views.map((view) => view.viewId).toList();
1441+
1442+
viewIds.sort();
1443+
1444+
signalNativeMessage('View IDs: [${viewIds.join(', ')}]');
1445+
};
1446+
1447+
signalNativeTest();
1448+
}
1449+
14021450
@pragma('vm:entry-point')
14031451
Future<void> channel_listener_response() async {
14041452
channelBuffers.setListener('test/listen',

0 commit comments

Comments
 (0)