Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
FlutterView.mm
Go to the documentation of this file.
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
6
11
13
14@interface FlutterView ()
15@property(nonatomic, weak) id<FlutterViewEngineDelegate> delegate;
16@property(nonatomic, weak) UIWindowScene* previousScene;
17@end
18
20@end
21
22@implementation FlutterView {
23 BOOL _isWideGamutEnabled;
25}
26
27- (instancetype)init {
28 NSAssert(NO, @"FlutterView must initWithDelegate");
29 return nil;
30}
31
32- (instancetype)initWithFrame:(CGRect)frame {
33 NSAssert(NO, @"FlutterView must initWithDelegate");
34 return nil;
35}
36
37- (instancetype)initWithCoder:(NSCoder*)aDecoder {
38 NSAssert(NO, @"FlutterView must initWithDelegate");
39 return nil;
40}
41
42- (UIScreen*)screen {
43 return self.window.windowScene.screen;
44}
45
46// iOS has a concept of "intrinsicContentSize", which indicates the size a view would like to be
47// based on its content. When an intrinsicContentSize is set, iOS will automatically add Auto Layout
48// constraints for the width and/or height. However, the constraints use a private API. There are
49// situations where we may want to filter these constraints. To avoid using a private API, Flutter
50// creates a custom constraint called FlutterAutoResizeLayoutConstraint to add a width/height
51// constraint that reflects the intrinsicContentSize.
52- (void)setIntrinsicContentSize:(CGSize)size {
53 if (!self.autoResizable) {
54 return;
55 }
56
57 UIWindow* window = self.window;
58 CGFloat scale = window ? self.window.windowScene.screen.scale : self.traitCollection.displayScale;
59 CGSize scaledSize = CGSizeMake(size.width / scale, size.height / scale);
60
61 CGSize roundedScaleSize = CGSizeMake(roundf(scaledSize.width), roundf(scaledSize.height));
62 CGSize roundedIntrinsicSize =
63 CGSizeMake(roundf(_intrinsicSize.width), roundf(_intrinsicSize.height));
64
65 // If the size has not changed, don't update constraints.
66 if (CGSizeEqualToSize(roundedIntrinsicSize, roundedScaleSize)) {
67 return;
68 }
69 _intrinsicSize = scaledSize;
70
71 self.translatesAutoresizingMaskIntoConstraints = false;
72
73 // Remove any existing FlutterAutoResizeLayoutConstraint
74 [self removeAutoResizeLayoutConstraints];
75
76 FlutterAutoResizeLayoutConstraint* widthConstraint =
77 [FlutterAutoResizeLayoutConstraint constraintWithItem:self
78 attribute:NSLayoutAttributeWidth
79 relatedBy:NSLayoutRelationEqual
80 toItem:nil
81 attribute:NSLayoutAttributeNotAnAttribute
82 multiplier:1.0
83 constant:scaledSize.width];
84
85 FlutterAutoResizeLayoutConstraint* heightConstraint =
86 [FlutterAutoResizeLayoutConstraint constraintWithItem:self
87 attribute:NSLayoutAttributeHeight
88 relatedBy:NSLayoutRelationEqual
89 toItem:nil
90 attribute:NSLayoutAttributeNotAnAttribute
91 multiplier:1.0
92 constant:scaledSize.height];
93
94 [NSLayoutConstraint activateConstraints:@[ widthConstraint, heightConstraint ]];
95 [self setNeedsLayout];
96}
97
98- (void)resetIntrinsicContentSize {
99 _intrinsicSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
100 [self removeAutoResizeLayoutConstraints];
101}
102
103- (void)removeAutoResizeLayoutConstraints {
104 for (NSLayoutConstraint* constraint in self.constraints) {
105 if ([constraint isKindOfClass:[FlutterAutoResizeLayoutConstraint class]]) {
106 constraint.active = NO;
107 }
108 }
109}
110
111- (MTLPixelFormat)pixelFormat {
112 if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
113// It is a known Apple bug that CAMetalLayer incorrectly reports its supported
114// SDKs. It is, in fact, available since iOS 8.
115#pragma clang diagnostic push
116#pragma clang diagnostic ignored "-Wunguarded-availability-new"
117 CAMetalLayer* layer = (CAMetalLayer*)self.layer;
118 return layer.pixelFormat;
119 }
120 return MTLPixelFormatBGRA8Unorm;
121}
122- (BOOL)isWideGamutSupported {
123 FML_DCHECK(self.screen);
124
125 // Wide Gamut is not supported for iOS Extensions due to memory limitations
126 // (see https://github.com/flutter/flutter/issues/165086).
128 return NO;
129 }
130
131 // This predicates the decision on the capabilities of the iOS device's
132 // display. This means external displays will not support wide gamut if the
133 // device's display doesn't support it. It practice that should be never.
134 return self.screen.traitCollection.displayGamut != UIDisplayGamutSRGB;
135}
136
137- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate
138 opaque:(BOOL)opaque
139 enableWideGamut:(BOOL)isWideGamutEnabled {
140 if (delegate == nil) {
141 NSLog(@"FlutterView delegate was nil.");
142 return nil;
143 }
144
145 self = [super initWithFrame:CGRectNull];
146
147 if (self) {
148 _delegate = delegate;
149 _isWideGamutEnabled = isWideGamutEnabled;
150 self.layer.opaque = opaque;
151 _autoResizable = NO;
152 _intrinsicSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
153 }
154
155 return self;
156}
157
158static void PrintWideGamutWarningOnce() {
159 static BOOL did_print = NO;
160 if (did_print) {
161 return;
162 }
163 FML_DLOG(WARNING) << "Rendering wide gamut colors is turned on but isn't "
164 "supported, downgrading the color gamut to sRGB.";
165 did_print = YES;
166}
167
168- (void)layoutSubviews {
169 if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
170// It is a known Apple bug that CAMetalLayer incorrectly reports its supported
171// SDKs. It is, in fact, available since iOS 8.
172#pragma clang diagnostic push
173#pragma clang diagnostic ignored "-Wunguarded-availability-new"
174 CAMetalLayer* layer = (CAMetalLayer*)self.layer;
175#pragma clang diagnostic pop
176 CGFloat screenScale = self.screen.scale;
177 layer.allowsGroupOpacity = YES;
178 layer.contentsScale = screenScale;
179 layer.rasterizationScale = screenScale;
180 layer.framebufferOnly = flutter::Settings::kSurfaceDataAccessible ? NO : YES;
181 if (_isWideGamutEnabled && self.isWideGamutSupported) {
182 fml::CFRef<CGColorSpaceRef> srgb(CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB));
183 layer.colorspace = srgb;
184 layer.pixelFormat = MTLPixelFormatBGRA10_XR;
185 } else if (_isWideGamutEnabled && !self.isWideGamutSupported) {
186 PrintWideGamutWarningOnce();
187 }
188 }
189
190 [super layoutSubviews];
191}
192
193+ (Class)layerClass {
195}
196
197- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
198 TRACE_EVENT0("flutter", "SnapshotFlutterView");
199
200 if (layer != self.layer || context == nullptr) {
201 return;
202 }
203
204 auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
205 asBase64Encoded:NO];
206
207 if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.IsEmpty()) {
208 return;
209 }
210
211 NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
212 length:screenshot.data->size()];
213
214 fml::CFRef<CGDataProviderRef> image_data_provider(
215 CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
216
217 fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
218
219 // Defaults for RGBA8888.
220 size_t bits_per_component = 8u;
221 size_t bits_per_pixel = 32u;
222 size_t bytes_per_row_multiplier = 4u;
223 CGBitmapInfo bitmap_info =
224 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
225 static_cast<uint32_t>(kCGBitmapByteOrder32Big));
226
227 switch (screenshot.pixel_format) {
230 // Assume unknown is Skia and is RGBA8888. Keep defaults.
231 break;
233 // Treat this as little endian with the alpha first so that it's read backwards.
234 bitmap_info =
235 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedFirst) |
236 static_cast<uint32_t>(kCGBitmapByteOrder32Little));
237 break;
239 bits_per_component = 16u;
240 bits_per_pixel = 64u;
241 bytes_per_row_multiplier = 8u;
242 bitmap_info =
243 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
244 static_cast<uint32_t>(kCGBitmapFloatComponents) |
245 static_cast<uint32_t>(kCGBitmapByteOrder16Little));
246 break;
247 }
248
249 fml::CFRef<CGImageRef> image(CGImageCreate(
250 screenshot.frame_size.width, // size_t width
251 screenshot.frame_size.height, // size_t height
252 bits_per_component, // size_t bitsPerComponent
253 bits_per_pixel, // size_t bitsPerPixel,
254 bytes_per_row_multiplier * screenshot.frame_size.width, // size_t bytesPerRow
255 colorspace, // CGColorSpaceRef space
256 bitmap_info, // CGBitmapInfo bitmapInfo
257 image_data_provider, // CGDataProviderRef provider
258 nullptr, // const CGFloat* decode
259 false, // bool shouldInterpolate
260 kCGRenderingIntentDefault // CGColorRenderingIntent intent
261 ));
262
263 const CGRect frame_rect =
264 CGRectMake(0.0, 0.0, screenshot.frame_size.width, screenshot.frame_size.height);
265 CGContextSaveGState(context);
266 // If the CGContext is not a bitmap based context, this returns zero.
267 CGFloat height = CGBitmapContextGetHeight(context);
268 if (height == 0) {
269 height = CGFloat(screenshot.frame_size.height);
270 }
271 CGContextTranslateCTM(context, 0.0, height);
272 CGContextScaleCTM(context, 1.0, -1.0);
273 CGContextDrawImage(context, frame_rect, image);
274 CGContextRestoreGState(context);
275}
276
277- (BOOL)isAccessibilityElement {
278 // iOS does not provide an API to query whether the voice control
279 // is turned on or off. It is likely at least one of the assitive
280 // technologies is turned on if this method is called. If we do
281 // not catch it in notification center, we will catch it here.
282 //
283 // TODO(chunhtai): Remove this workaround once iOS provides an
284 // API to query whether voice control is enabled.
285 // https://github.com/flutter/flutter/issues/76808.
286 [self.delegate flutterViewAccessibilityDidCall];
287 return NO;
288}
289
290// Enables keyboard-based navigation when the user turns on
291// full keyboard access (FKA), using existing accessibility information.
292//
293// iOS does not provide any API for monitoring or querying whether FKA is on,
294// but it does call isAccessibilityElement if FKA is on,
295// so the isAccessibilityElement implementation above will be called
296// when the view appears and the accessibility information will most likely
297// be available by the time the user starts to interact with the app using FKA.
298//
299// See SemanticsObject+UIFocusSystem.mm for more details.
300- (NSArray<id<UIFocusItem>>*)focusItemsInRect:(CGRect)rect {
301 NSObject* rootAccessibilityElement =
302 [self.accessibilityElements count] > 0 ? self.accessibilityElements[0] : nil;
303 return [rootAccessibilityElement isKindOfClass:[SemanticsObjectContainer class]]
304 ? @[ [rootAccessibilityElement accessibilityElementAtIndex:0] ]
305 : nil;
306}
307
308- (NSArray<id<UIFocusEnvironment>>*)preferredFocusEnvironments {
309 // Occasionally we add subviews to FlutterView (text fields for example).
310 // These views shouldn't be directly visible to the iOS focus engine, instead
311 // the focus engine should only interact with the designated focus items
312 // (SemanticsObjects).
313 return nil;
314}
315
316- (void)willMoveToWindow:(UIWindow*)newWindow {
317 // When a FlutterView moves windows, it may also be moving scenes. Add/remove the FlutterEngine
318 // from the FlutterSceneLifeCycleProvider.sceneLifeCycleDelegate if it changes scenes.
319 UIWindowScene* newScene = newWindow.windowScene;
320 UIWindowScene* currentScene = self.window.windowScene;
321
322 if (newScene == currentScene) {
323 return;
324 }
325
326 // Remove the engine from the previous scene if it's no longer in that window and scene.
327 FlutterPluginSceneLifeCycleDelegate* previousSceneLifeCycleDelegate =
328 [FlutterPluginSceneLifeCycleDelegate fromScene:self.previousScene];
329 if (previousSceneLifeCycleDelegate) {
330 [previousSceneLifeCycleDelegate removeFlutterManagedEngine:(FlutterEngine*)self.delegate];
331 self.previousScene = nil;
332 }
333
334 if (newScene) {
335 // Add the engine to the new scene's lifecycle delegate.
336 FlutterPluginSceneLifeCycleDelegate* newSceneLifeCycleDelegate =
337 [FlutterPluginSceneLifeCycleDelegate fromScene:newScene];
338 if (newSceneLifeCycleDelegate) {
339 [newSceneLifeCycleDelegate addFlutterManagedEngine:(FlutterEngine*)self.delegate];
340 }
341 } else {
342 // If the view is being removed from a window, store the current scene to remove the engine
343 // from it later when the view is added to a new window.
344 self.previousScene = currentScene;
345 }
346}
347@end
FlutterVulkanImage * image
GLFWwindow * window
Definition main.cc:60
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_DCHECK(condition)
Definition logging.h:122
instancetype initWithFrame
instancetype initWithCoder
CGSize _intrinsicSize
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
IOSRenderingAPI GetRenderingAPIForProcess()
Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api)
std::shared_ptr< ContextGLES > context
int32_t height
static constexpr bool kSurfaceDataAccessible
Definition settings.h:107
const uintptr_t id
#define TRACE_EVENT0(category_group, name)
int BOOL