| Flutter iOS Embedder |

Classes | Public Member Functions | List of all members
flutter::PlatformViewsController Class Reference

Composites Flutter UI and overlay layers alongside embedded UIViews. More...

`#include <platform_views_controller.h>`

## Public Member Functions
| PlatformViewsController ()
| ~PlatformViewsController ()=default
fml::WeakPtr< flutter::PlatformViewsController > | GetWeakPtr ()
| Retrieve a weak pointer to this controller. More...
void | SetTaskRunner (const fml::RefPtr< fml::TaskRunner > &platform_task_runner)
| Set the platform task runner used to post rendering tasks. More...
void | SetFlutterView (UIView *flutter_view) __attribute__((cf_audited_transfer))
| Set the flutter view. More...
void | SetFlutterViewController (UIViewController< FlutterViewResponder > *flutter_view_controller) __attribute__((cf_audited_transfer))
| Set the flutter view controller. More...
UIViewController< FlutterViewResponder > * | GetFlutterViewController () __attribute__((cf_audited_transfer))
| Retrieve the view controller. More...
void | RegisterViewFactory (NSObject< FlutterPlatformViewFactory > *factory, NSString *factoryId, FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) __attribute__((cf_audited_transfer))
| set the factory used to construct embedded UI Views. More...
void | BeginFrame (SkISize frame_size)
| Mark the beginning of a frame and record the size of the onscreen. More...
void | CancelFrame ()
| Cancel the current frame, indicating that no platform views are composited. More...
void | PrerollCompositeEmbeddedView (int64_t view_id, std::unique_ptr< flutter::EmbeddedViewParams > params)
| Record a platform view in the layer tree to be rendered, along with the positioning and mutator parameters. More...
FlutterTouchInterceptingView * | GetFlutterTouchInterceptingViewByID (int64_t view_id)
| Returns the`FlutterTouchInterceptingView` with the provided view_id. More...
PostPrerollResult | PostPrerollAction (const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger, bool impeller_enabled)
| Determine if thread merging is required after prerolling platform views. More...
void | EndFrame (bool should_resubmit_frame, const fml::RefPtr< fml::RasterThreadMerger > &raster_thread_merger, bool impeller_enabled)
| Mark the end of a compositor frame. More...
DlCanvas * | CompositeEmbeddedView (int64_t view_id)
| Returns the Canvas for the overlay slice for the given platform view. More...
void | Reset ()
| Discards all platform views instances and auxiliary resources. More...
bool | SubmitFrame (GrDirectContext *gr_context, const std::shared_ptr< IOSContext > &ios_context, std::unique_ptr< SurfaceFrame > frame)
| Encode rendering for the Flutter overlay views and queue up perform platform view mutations. More...
void | OnMethodCall (FlutterMethodCall *call, FlutterResult result) __attribute__((cf_audited_transfer))
| Handler for platform view message channels. More...
long | FindFirstResponderPlatformViewId ()
| Returns the platform view id if the platform view (or any of its descendant view) is the first responder. More...
void | PushFilterToVisitedPlatformViews (const std::shared_ptr< const DlImageFilter > &filter, const SkRect &filter_rect)
| Pushes backdrop filter mutation to the mutator stack of each visited platform view. More...
void | PushVisitedPlatformView (int64_t view_id)
| Pushes the view id of a visted platform view to the list of visied platform views. More...
size_t | EmbeddedViewCount () const
size_t | LayerPoolSize () const
UIView * | GetPlatformViewByID (int64_t view_id)
void | CompositeWithParams (int64_t view_id, const EmbeddedViewParams &params)
const EmbeddedViewParams & | GetCompositionParams (int64_t view_id) const

## Detailed Description

Composites Flutter UI and overlay layers alongside embedded UIViews.

Definition at line 31 of file platform_views_controller.h.

## Constructor & Destructor Documentation

## ◆ PlatformViewsController()

flutter::PlatformViewsController::PlatformViewsController | ( |  | ) |

Definition at line 113 of file platform_views_controller.mm.

114 : layer_pool_(std::make_unique<OverlayLayerPool>()),
115 weak_factory_(std::make_unique<fml::WeakPtrFactory<PlatformViewsController>>(this)) {
116 mask_view_pool_.reset(
117 [[FlutterClippingMaskViewPool alloc] initWithCapacity:kFlutterClippingMaskViewPoolCapacity]);
118 };

## ◆ ~PlatformViewsController()

flutter::PlatformViewsController::~PlatformViewsController | ( |  | ) |
| default

## Member Function Documentation

## ◆ BeginFrame()

void flutter::PlatformViewsController::BeginFrame | ( | SkISize | frame_size | ) |

Mark the beginning of a frame and record the size of the onscreen.

Definition at line 282 of file platform_views_controller.mm.

282 {
283 ResetFrameState();
284 frame_size_ = frame_size;
285 }

## ◆ CancelFrame()

void flutter::PlatformViewsController::CancelFrame | ( |  | ) |

Cancel the current frame, indicating that no platform views are composited.

Additionally, reverts the composition order to its original state at the beginning of the frame.

Definition at line 287 of file platform_views_controller.mm.

287 {
288 ResetFrameState();
289 }

Referenced by PostPrerollAction().

## ◆ CompositeEmbeddedView()

DlCanvas * flutter::PlatformViewsController::CompositeEmbeddedView | ( | int64_t | view_id | ) |

Returns the Canvas for the overlay slice for the given platform view.

Called from the raster thread.

Definition at line 579 of file platform_views_controller.mm.

579 {
580 return slices_[view_id]->canvas();
581 }

## ◆ CompositeWithParams()

void flutter::PlatformViewsController::CompositeWithParams | ( | int64_t | view_id,
|  | const EmbeddedViewParams & | params
| ) |  |

Definition at line 540 of file platform_views_controller.mm.

541 {
542 CGRect frame = CGRectMake(0, 0, params.sizePoints().width(), params.sizePoints().height());
543 FlutterTouchInterceptingView* touchInterceptor = platform_views_[view_id].touch_interceptor.get();
544 #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
545 FML_DCHECK(CGPointEqualToPoint([touchInterceptor embeddedView].frame.origin, CGPointZero));
546 if (non_zero_origin_views_.find(view_id) == non_zero_origin_views_.end() &&
547 !CGPointEqualToPoint([touchInterceptor embeddedView].frame.origin, CGPointZero)) {
548 non_zero_origin_views_.insert(view_id);
549 NSLog(
550 @"A Embedded PlatformView's origin is not CGPointZero.\n"
551 " View id: %@\n"
552 " View info: \n %@ \n"
553 "A non-zero origin might cause undefined behavior.\n"
554 "See https://github.com/flutter/flutter/issues/109700 for more details.\n"
555 "If you are the author of the PlatformView, please update the implementation of the "
556 "PlatformView to have a (0, 0) origin.\n"
557 "If you have a valid case of using a non-zero origin, "
558 "please leave a comment at https://github.com/flutter/flutter/issues/109700 with details.",
559 @(view_id), [touchInterceptor embeddedView]);
560 }
561 #endif
562 touchInterceptor.layer.transform = CATransform3DIdentity;
563 touchInterceptor.frame = frame;
564 touchInterceptor.alpha = 1;
565
566 const MutatorsStack& mutatorStack = params.mutatorsStack();
567 UIView* clippingView = platform_views_[view_id].root_view.get();
568 // The frame of the clipping view should be the final bounding rect.
569 // Because the translate matrix in the Mutator Stack also includes the offset,
570 // when we apply the transforms matrix in |ApplyMutators|, we need
571 // to remember to do a reverse translate.
572 const SkRect& rect = params.finalBoundingRect();
573 CGFloat screenScale = [UIScreen mainScreen].scale;
574 clippingView.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale,
575 rect.width() / screenScale, rect.height() / screenScale);
576 ApplyMutators(mutatorStack, touchInterceptor, rect);
577 }

## ◆ EmbeddedViewCount()

size_t flutter::PlatformViewsController::EmbeddedViewCount | ( |  | ) | const

Definition at line 369 of file platform_views_controller.mm.

369 {
370 return composition_order_.size();
371 }

## ◆ EndFrame()

void flutter::PlatformViewsController::EndFrame | ( | bool | should_resubmit_frame,
|  | const fml::RefPtr< fml::RasterThreadMerger > & | raster_thread_merger,
|  | bool | impeller_enabled
| ) |  |

Mark the end of a compositor frame.

May determine changes are required to the thread merging state. Called from the raster thread.

Definition at line 326 of file platform_views_controller.mm.

329 {
330 #if FML_OS_IOS_SIMULATOR
331 bool run_check = true;
332 #else
333 bool run_check = !impeller_enabled;
334 #endif // FML_OS_IOS_SIMULATOR
335 if (run_check && should_resubmit_frame) {
336 raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration);
337 }
338 }

## ◆ FindFirstResponderPlatformViewId()

long flutter::PlatformViewsController::FindFirstResponderPlatformViewId | ( |  | ) |

Returns the platform view id if the platform view (or any of its descendant view) is the first responder.

Returns -1 if no such platform view is found.

Definition at line 389 of file platform_views_controller.mm.

389 {
390 for (auto const& [id, platform_view_data] : platform_views_) {
391 UIView* root_view = (UIView*)platform_view_data.root_view.get();
392 if (root_view.flt_hasFirstResponderInViewHierarchySubtree) {
393 return id;
394 }
395 }
396 return -1;
397 }

## ◆ GetCompositionParams()

const EmbeddedViewParams& flutter::PlatformViewsController::GetCompositionParams | ( | int64_t | view_id | ) | const
| inline

Definition at line 151 of file platform_views_controller.h.

151 {
152 return current_composition_params_.find(view_id)->second;
153 }

## ◆ GetFlutterTouchInterceptingViewByID()

FlutterTouchInterceptingView * flutter::PlatformViewsController::GetFlutterTouchInterceptingViewByID | ( | int64_t | view_id | ) |

Returns the`FlutterTouchInterceptingView` with the provided view_id.

Returns nil if there is no platform view with the provided id. Called from the platform thread.

Definition at line 381 of file platform_views_controller.mm.

382 {
383 if (platform_views_.empty()) {
384 return nil;
385 }
386 return platform_views_[view_id].touch_interceptor.get();
387 }

Referenced by GetPlatformViewByID().

## ◆ GetFlutterViewController()

UIViewController< FlutterViewResponder > * flutter::PlatformViewsController::GetFlutterViewController | ( |  | ) |

Retrieve the view controller.

Definition at line 138 of file platform_views_controller.mm.

138 {
139 return flutter_view_controller_.get();
140 }

## ◆ GetPlatformViewByID()

UIView * flutter::PlatformViewsController::GetPlatformViewByID | ( | int64_t | view_id | ) |

Definition at line 377 of file platform_views_controller.mm.

377 {
378 return [GetFlutterTouchInterceptingViewByID(view_id) embeddedView];
379 }

References GetFlutterTouchInterceptingViewByID().

## ◆ GetWeakPtr()

fml::WeakPtr< flutter::PlatformViewsController > flutter::PlatformViewsController::GetWeakPtr | ( |  | ) |

Retrieve a weak pointer to this controller.

Definition at line 125 of file platform_views_controller.mm.

125 {
126 return weak_factory_->GetWeakPtr();
127 }

## ◆ LayerPoolSize()

size_t flutter::PlatformViewsController::LayerPoolSize | ( |  | ) | const

Definition at line 373 of file platform_views_controller.mm.

373 {
374 return layer_pool_->size();
375 }

## ◆ OnMethodCall()

void flutter::PlatformViewsController::OnMethodCall | ( | FlutterMethodCall * | call,
|  | FlutterResult | result
| ) |  |

Handler for platform view message channels.

Definition at line 142 of file platform_views_controller.mm.

142 {
143 if ([[call method] isEqualToString:@"create"]) {
144 OnCreate(call, result);
145 } else if ([[call method] isEqualToString:@"dispose"]) {
146 OnDispose(call, result);
147 } else if ([[call method] isEqualToString:@"acceptGesture"]) {
148 OnAcceptGesture(call, result);
149 } else if ([[call method] isEqualToString:@"rejectGesture"]) {
150 OnRejectGesture(call, result);
151 } else {
152 result(FlutterMethodNotImplemented);
153 }
154 }

References FlutterMethodNotImplemented.

## ◆ PostPrerollAction()

PostPrerollResult flutter::PlatformViewsController::PostPrerollAction | ( | const fml::RefPtr< fml::RasterThreadMerger > & | raster_thread_merger,
|  | bool | impeller_enabled
| ) |  |

Determine if thread merging is required after prerolling platform views.

Called from the raster thread.

Definition at line 291 of file platform_views_controller.mm.

293 {
294 // TODO(jonahwilliams): remove this once Software backend is removed for iOS Sim.
295 #ifdef FML_OS_IOS_SIMULATOR
296 const bool merge_threads = true;
297 #else
298 const bool merge_threads = !impeller_enabled;
299 #endif // FML_OS_IOS_SIMULATOR
300
301 if (merge_threads) {
302 if (composition_order_.empty()) {
303 return PostPrerollResult::kSuccess;
304 }
305 if (!raster_thread_merger->IsMerged()) {
306 // The raster thread merger may be disabled if the rasterizer is being
307 // created or teared down.
308 //
309 // In such cases, the current frame is dropped, and a new frame is attempted
310 // with the same layer tree.
311 //
312 // Eventually, the frame is submitted once this method returns `kSuccess`.
313 // At that point, the raster tasks are handled on the platform thread.
314 CancelFrame();
315 return PostPrerollResult::kSkipAndRetryFrame;
316 }
317 // If the post preroll action is successful, we will display platform views in the current
318 // frame. In order to sync the rendering of the platform views (quartz) with skia's rendering,
319 // We need to begin an explicit CATransaction. This transaction needs to be submitted
320 // after the current frame is submitted.
321 raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration);
322 }
323 return PostPrerollResult::kSuccess;
324 }

References CancelFrame().

## ◆ PrerollCompositeEmbeddedView()

void flutter::PlatformViewsController::PrerollCompositeEmbeddedView | ( | int64_t | view_id,
|  | std::unique_ptr< flutter::EmbeddedViewParams > | params
| ) |  |

Record a platform view in the layer tree to be rendered, along with the positioning and mutator parameters.

Called from the raster thread.

Definition at line 350 of file platform_views_controller.mm.

352 {
353 SkRect view_bounds = SkRect::Make(frame_size_);
354 std::unique_ptr<EmbedderViewSlice> view;
355 view = std::make_unique<DisplayListEmbedderViewSlice>(view_bounds);
356 slices_.insert_or_assign(view_id, std::move(view));
357
358 composition_order_.push_back(view_id);
359
360 if (current_composition_params_.count(view_id) == 1 &&
361 current_composition_params_[view_id] == *params.get()) {
362 // Do nothing if the params didn't change.
363 return;
364 }
365 current_composition_params_[view_id] = EmbeddedViewParams(*params.get());
366 views_to_recomposite_.insert(view_id);
367 }

## ◆ PushFilterToVisitedPlatformViews()

void flutter::PlatformViewsController::PushFilterToVisitedPlatformViews | ( | const std::shared_ptr< const DlImageFilter > & | filter,
|  | const SkRect & | filter_rect
| ) |  |

Pushes backdrop filter mutation to the mutator stack of each visited platform view.

Definition at line 340 of file platform_views_controller.mm.

342 {
343 for (int64_t id : visited_platform_views_) {
344 EmbeddedViewParams params = current_composition_params_[id];
345 params.PushImageFilter(filter, filter_rect);
346 current_composition_params_[id] = params;
347 }
348 }

## ◆ PushVisitedPlatformView()

void flutter::PlatformViewsController::PushVisitedPlatformView | ( | int64_t | view_id | ) |
| inline

Pushes the view id of a visted platform view to the list of visied platform views.

Definition at line 131 of file platform_views_controller.h.

131 { visited_platform_views_.push_back(view_id); }

## ◆ RegisterViewFactory()

void flutter::PlatformViewsController::RegisterViewFactory | ( | NSObject< FlutterPlatformViewFactory > * | factory,
|  | NSString * | factoryId,
|  | FlutterPlatformViewGestureRecognizersBlockingPolicy | gestureRecognizerBlockingPolicy
| ) |  |

set the factory used to construct embedded UI Views.

Definition at line 272 of file platform_views_controller.mm.

275 {
276 std::string idString([factoryId UTF8String]);
277 FML_CHECK(factories_.count(idString) == 0);
278 factories_[idString] = fml::scoped_nsobject<NSObject<FlutterPlatformViewFactory>>(factory);
279 gesture_recognizers_blocking_policies_[idString] = gestureRecognizerBlockingPolicy;
280 }

## ◆ Reset()

void flutter::PlatformViewsController::Reset | ( |  | ) |

Discards all platform views instances and auxiliary resources.

Called from the raster thread.

Definition at line 583 of file platform_views_controller.mm.

583 {
584 // Reset will only be called from the raster thread or a merged raster/platform thread.
585 // platform_views_ must only be modified on the platform thread, and any operations that
586 // read or modify platform views should occur there.
587 fml::TaskRunner::RunNowOrPostTask(
588 platform_task_runner_, [&, composition_order = composition_order_]() {
589 for (int64_t view_id : composition_order_) {
590 [platform_views_[view_id].root_view.get() removeFromSuperview];
591 }
592 platform_views_.clear();
593 });
594
595 composition_order_.clear();
596 slices_.clear();
597 current_composition_params_.clear();
598 views_to_recomposite_.clear();
599 layer_pool_->RecycleLayers();
600 visited_platform_views_.clear();
601 }

## ◆ SetFlutterView()

void flutter::PlatformViewsController::SetFlutterView | ( | UIView * | flutter_view | ) |

Set the flutter view.

Definition at line 129 of file platform_views_controller.mm.

129 {
130 flutter_view_.reset(flutter_view);
131 }

## ◆ SetFlutterViewController()

void flutter::PlatformViewsController::SetFlutterViewController | ( | UIViewController< FlutterViewResponder > * | flutter_view_controller | ) |

Set the flutter view controller.

Definition at line 133 of file platform_views_controller.mm.

134 {
135 flutter_view_controller_.reset(flutter_view_controller);
136 }

## ◆ SetTaskRunner()

void flutter::PlatformViewsController::SetTaskRunner | ( | const fml::RefPtr< fml::TaskRunner > & | platform_task_runner | ) |

Set the platform task runner used to post rendering tasks.

Definition at line 120 of file platform_views_controller.mm.

121 {
122 platform_task_runner_ = platform_task_runner;
123 }

## ◆ SubmitFrame()

bool flutter::PlatformViewsController::SubmitFrame | ( | GrDirectContext * | gr_context,
|  | const std::shared_ptr< IOSContext > & | ios_context,
|  | std::unique_ptr< SurfaceFrame > | frame
| ) |  |

Encode rendering for the Flutter overlay views and queue up perform platform view mutations.

Called from the raster thread.

Definition at line 603 of file platform_views_controller.mm.

605 {
606 TRACE_EVENT0("flutter", "PlatformViewsController::SubmitFrame");
607
608 // No platform views to render; we're done.
609 if (flutter_view_ == nullptr || (composition_order_.empty() && !had_platform_views_)) {
610 had_platform_views_ = false;
611 return background_frame->Submit();
612 }
613 had_platform_views_ = !composition_order_.empty();
614
615 bool did_encode = true;
616 LayersMap platform_view_layers;
617 std::vector<std::unique_ptr<SurfaceFrame>> surface_frames;
618 surface_frames.reserve(composition_order_.size());
619 std::unordered_map<int64_t, SkRect> view_rects;
620
621 for (int64_t view_id : composition_order_) {
622 view_rects[view_id] = current_composition_params_[view_id].finalBoundingRect();
623 }
624
625 std::unordered_map<int64_t, SkRect> overlay_layers =
626 SliceViews(background_frame->Canvas(), composition_order_, slices_, view_rects);
627
628 size_t required_overlay_layers = 0;
629 for (int64_t view_id : composition_order_) {
630 std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
631 if (overlay == overlay_layers.end()) {
632 continue;
633 }
634 required_overlay_layers++;
635 }
636
637 // If there are not sufficient overlay layers, we must construct them on the platform
638 // thread, at least until we've refactored iOS surface creation to use IOSurfaces
639 // instead of CALayers.
640 CreateMissingOverlays(gr_context, ios_context, required_overlay_layers);
641
642 int64_t overlay_id = 0;
643 for (int64_t view_id : composition_order_) {
644 std::unordered_map<int64_t, SkRect>::const_iterator overlay = overlay_layers.find(view_id);
645 if (overlay == overlay_layers.end()) {
646 continue;
647 }
648 std::shared_ptr<OverlayLayer> layer = GetExistingLayer();
649 if (!layer) {
650 continue;
651 }
652
653 std::unique_ptr<SurfaceFrame> frame = layer->surface->AcquireFrame(frame_size_);
654 // If frame is null, AcquireFrame already printed out an error message.
655 if (!frame) {
656 continue;
657 }
658 DlCanvas* overlay_canvas = frame->Canvas();
659 int restore_count = overlay_canvas->GetSaveCount();
660 overlay_canvas->Save();
661 overlay_canvas->ClipRect(overlay->second);
662 overlay_canvas->Clear(DlColor::kTransparent());
663 slices_[view_id]->render_into(overlay_canvas);
664 overlay_canvas->RestoreToCount(restore_count);
665
666 // This flutter view is never the last in a frame, since we always submit the
667 // underlay view last.
668 frame->set_submit_info({.frame_boundary = false, .present_with_transaction = true});
669 layer->did_submit_last_frame = frame->Encode();
670
671 did_encode &= layer->did_submit_last_frame;
672 platform_view_layers[view_id] = LayerData{
673 .rect = overlay->second, //
674 .view_id = view_id, //
675 .overlay_id = overlay_id, //
676 .layer = layer //
677 };
678 surface_frames.push_back(std::move(frame));
679 overlay_id++;
680 }
681
682 auto previous_submit_info = background_frame->submit_info();
683 background_frame->set_submit_info({
684 .frame_damage = previous_submit_info.frame_damage,
685 .buffer_damage = previous_submit_info.buffer_damage,
686 .present_with_transaction = true,
687 });
688 background_frame->Encode();
689 surface_frames.push_back(std::move(background_frame));
690
691 // Mark all layers as available, so they can be used in the next frame.
692 std::vector<std::shared_ptr<OverlayLayer>> unused_layers = layer_pool_->RemoveUnusedLayers();
693 layer_pool_->RecycleLayers();
694
695 auto task = [&, //
696 platform_view_layers = std::move(platform_view_layers), //
697 current_composition_params = current_composition_params_, //
698 views_to_recomposite = views_to_recomposite_, //
699 composition_order = composition_order_, //
700 unused_layers = std::move(unused_layers), //
701 surface_frames = std::move(surface_frames) //
702 ]() mutable {
703 PerformSubmit(platform_view_layers, //
704 current_composition_params, //
705 views_to_recomposite, //
706 composition_order, //
707 unused_layers, //
708 surface_frames //
709 );
710 };
711
712 fml::TaskRunner::RunNowOrPostTask(platform_task_runner_, fml::MakeCopyable(std::move(task)));
713
714 return did_encode;
715 }

The documentation for this class was generated from the following files:

- shell/platform/darwin/ios/framework/Source/platform_views_controller.h
- shell/platform/darwin/ios/framework/Source/platform_views_controller.mm

FlutterMethodNotImplemented
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
FlutterClippingMaskViewPool
Definition: FlutterPlatformViews_Internal.h:66
flutter::PlatformViewsController::GetFlutterTouchInterceptingViewByID
FlutterTouchInterceptingView * GetFlutterTouchInterceptingViewByID(int64_t view_id)
Returns theFlutterTouchInterceptingView with the provided view_id.
Definition: platform_views_controller.mm:381
FlutterTouchInterceptingView
Definition: FlutterPlatformViews_Internal.h:138
flutter::PlatformViewsController::CancelFrame
void CancelFrame()
Cancel the current frame, indicating that no platform views are composited.
Definition: platform_views_controller.mm:287