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 flutter::GetRenderingAPIForProcess(/*force_software=*/false));
196}
197
198- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
199 TRACE_EVENT0("flutter", "SnapshotFlutterView");
200
201 if (layer != self.layer || context == nullptr) {
202 return;
203 }
204
205 auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
206 asBase64Encoded:NO];
207
208 if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.IsEmpty()) {
209 return;
210 }
211
212 NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
213 length:screenshot.data->size()];
214
215 fml::CFRef<CGDataProviderRef> image_data_provider(
216 CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
217
218 fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
219
220 // Defaults for RGBA8888.
221 size_t bits_per_component = 8u;
222 size_t bits_per_pixel = 32u;
223 size_t bytes_per_row_multiplier = 4u;
224 CGBitmapInfo bitmap_info =
225 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
226 static_cast<uint32_t>(kCGBitmapByteOrder32Big));
227
228 switch (screenshot.pixel_format) {
231 // Assume unknown is Skia and is RGBA8888. Keep defaults.
232 break;
234 // Treat this as little endian with the alpha first so that it's read backwards.
235 bitmap_info =
236 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedFirst) |
237 static_cast<uint32_t>(kCGBitmapByteOrder32Little));
238 break;
240 bits_per_component = 16u;
241 bits_per_pixel = 64u;
242 bytes_per_row_multiplier = 8u;
243 bitmap_info =
244 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
245 static_cast<uint32_t>(kCGBitmapFloatComponents) |
246 static_cast<uint32_t>(kCGBitmapByteOrder16Little));
247 break;
248 }
249
250 fml::CFRef<CGImageRef> image(CGImageCreate(
251 screenshot.frame_size.width, // size_t width
252 screenshot.frame_size.height, // size_t height
253 bits_per_component, // size_t bitsPerComponent
254 bits_per_pixel, // size_t bitsPerPixel,
255 bytes_per_row_multiplier * screenshot.frame_size.width, // size_t bytesPerRow
256 colorspace, // CGColorSpaceRef space
257 bitmap_info, // CGBitmapInfo bitmapInfo
258 image_data_provider, // CGDataProviderRef provider
259 nullptr, // const CGFloat* decode
260 false, // bool shouldInterpolate
261 kCGRenderingIntentDefault // CGColorRenderingIntent intent
262 ));
263
264 const CGRect frame_rect =
265 CGRectMake(0.0, 0.0, screenshot.frame_size.width, screenshot.frame_size.height);
266 CGContextSaveGState(context);
267 // If the CGContext is not a bitmap based context, this returns zero.
268 CGFloat height = CGBitmapContextGetHeight(context);
269 if (height == 0) {
270 height = CGFloat(screenshot.frame_size.height);
271 }
272 CGContextTranslateCTM(context, 0.0, height);
273 CGContextScaleCTM(context, 1.0, -1.0);
274 CGContextDrawImage(context, frame_rect, image);
275 CGContextRestoreGState(context);
276}
277
278- (BOOL)isAccessibilityElement {
279 // iOS does not provide an API to query whether the voice control
280 // is turned on or off. It is likely at least one of the assitive
281 // technologies is turned on if this method is called. If we do
282 // not catch it in notification center, we will catch it here.
283 //
284 // TODO(chunhtai): Remove this workaround once iOS provides an
285 // API to query whether voice control is enabled.
286 // https://github.com/flutter/flutter/issues/76808.
287 [self.delegate flutterViewAccessibilityDidCall];
288 return NO;
289}
290
291// Enables keyboard-based navigation when the user turns on
292// full keyboard access (FKA), using existing accessibility information.
293//
294// iOS does not provide any API for monitoring or querying whether FKA is on,
295// but it does call isAccessibilityElement if FKA is on,
296// so the isAccessibilityElement implementation above will be called
297// when the view appears and the accessibility information will most likely
298// be available by the time the user starts to interact with the app using FKA.
299//
300// See SemanticsObject+UIFocusSystem.mm for more details.
301- (NSArray<id<UIFocusItem>>*)focusItemsInRect:(CGRect)rect {
302 NSObject* rootAccessibilityElement =
303 [self.accessibilityElements count] > 0 ? self.accessibilityElements[0] : nil;
304 return [rootAccessibilityElement isKindOfClass:[SemanticsObjectContainer class]]
305 ? @[ [rootAccessibilityElement accessibilityElementAtIndex:0] ]
306 : nil;
307}
308
309- (NSArray<id<UIFocusEnvironment>>*)preferredFocusEnvironments {
310 // Occasionally we add subviews to FlutterView (text fields for example).
311 // These views shouldn't be directly visible to the iOS focus engine, instead
312 // the focus engine should only interact with the designated focus items
313 // (SemanticsObjects).
314 return nil;
315}
316
317- (void)willMoveToWindow:(UIWindow*)newWindow {
318 // When a FlutterView moves windows, it may also be moving scenes. Add/remove the FlutterEngine
319 // from the FlutterSceneLifeCycleProvider.sceneLifeCycleDelegate if it changes scenes.
320 UIWindowScene* newScene = newWindow.windowScene;
321 UIWindowScene* currentScene = self.window.windowScene;
322
323 if (newScene == currentScene) {
324 return;
325 }
326
327 // Remove the engine from the previous scene if it's no longer in that window and scene.
328 FlutterPluginSceneLifeCycleDelegate* previousSceneLifeCycleDelegate =
329 [FlutterPluginSceneLifeCycleDelegate fromScene:self.previousScene];
330 if (previousSceneLifeCycleDelegate) {
331 [previousSceneLifeCycleDelegate removeFlutterManagedEngine:(FlutterEngine*)self.delegate];
332 self.previousScene = nil;
333 }
334
335 if (newScene) {
336 // Add the engine to the new scene's lifecycle delegate.
337 FlutterPluginSceneLifeCycleDelegate* newSceneLifeCycleDelegate =
338 [FlutterPluginSceneLifeCycleDelegate fromScene:newScene];
339 if (newSceneLifeCycleDelegate) {
340 [newSceneLifeCycleDelegate addFlutterManagedEngine:(FlutterEngine*)self.delegate];
341 }
342 } else {
343 // If the view is being removed from a window, store the current scene to remove the engine
344 // from it later when the view is added to a new window.
345 self.previousScene = currentScene;
346 }
347}
348@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
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api)
int32_t height
static constexpr bool kSurfaceDataAccessible
Definition settings.h:107
std::shared_ptr< const fml::Mapping > data
const uintptr_t id
#define TRACE_EVENT0(category_group, name)
int BOOL