@@ -41,6 +41,11 @@ - (bool)engineCallbackOnMakeResourceCurrent;
41
41
*/
42
42
- (void )engineCallbackOnPlatformMessage : (const FlutterPlatformMessage*)message ;
43
43
44
+ /* *
45
+ * Shuts the Flutter engine if it is running.
46
+ */
47
+ - (void )shutDownEngine ;
48
+
44
49
@end
45
50
46
51
#pragma mark -
@@ -120,38 +125,46 @@ @implementation FLEEngine {
120
125
// The embedding-API-level engine object.
121
126
FlutterEngine _engine;
122
127
128
+ // The project being run by this engine.
129
+ FLEDartProject* _project;
130
+
131
+ // The context provided to the Flutter engine for resource loading.
132
+ NSOpenGLContext * _resourceContext;
133
+
123
134
// A mapping of channel names to the registered handlers for those channels.
124
135
NSMutableDictionary <NSString *, FlutterBinaryMessageHandler>* _messageHandlers;
136
+
137
+ // Whether the engine can continue running after the view controller is removed.
138
+ BOOL _allowHeadlessExecution;
125
139
}
126
140
127
- - (instancetype )init {
128
- return [self initWithViewController: nil project: nil ];
141
+ - (instancetype )initWithName : ( NSString *) labelPrefix project : (FLEDartProject*) project {
142
+ return [self initWithName: labelPrefix project: project allowHeadlessExecution: YES ];
129
143
}
130
144
131
- - (instancetype )initWithViewController : (FLEViewController*)viewController
132
- project : (FLEDartProject*)project {
145
+ - (instancetype )initWithName : (NSString *)labelPrefix
146
+ project : (FLEDartProject*)project
147
+ allowHeadlessExecution : (BOOL )allowHeadlessExecution {
133
148
self = [super init ];
134
149
NSAssert (self, @" Super init cannot be nil" );
135
150
136
- _viewController = viewController;
137
151
_project = project ?: [[FLEDartProject alloc ] init ];
138
152
_messageHandlers = [[NSMutableDictionary alloc ] init ];
139
153
140
154
return self;
141
155
}
142
156
143
157
- (void )dealloc {
144
- if (FlutterEngineShutdown (_engine) == kSuccess ) {
145
- _engine = NULL ;
146
- }
158
+ [self shutDownEngine ];
147
159
}
148
160
149
- - (void )setProject : (FLEDartProject*)project {
150
- _project = project ?: [[FLEDartProject alloc ] init ];
151
- }
161
+ - (BOOL )runWithEntrypoint : (NSString *)entrypoint {
162
+ if (self.running ) {
163
+ return NO ;
164
+ }
152
165
153
- - ( BOOL ) run {
154
- if (_engine != NULL ) {
166
+ if (!_allowHeadlessExecution && !_viewController) {
167
+ NSLog ( @" Attempted to run an engine with no view controller without headless mode enabled. " );
155
168
return NO ;
156
169
}
157
170
@@ -175,16 +188,27 @@ - (BOOL)run {
175
188
flutterArguments.command_line_argc = static_cast <int >(arguments.size ());
176
189
flutterArguments.command_line_argv = &arguments[0 ];
177
190
flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
191
+ flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String ;
178
192
179
193
FlutterEngineResult result = FlutterEngineRun (
180
194
FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void *)(self), &_engine);
181
195
if (result != kSuccess ) {
182
196
NSLog (@" Failed to start Flutter engine: error %d " , result);
183
197
return NO ;
184
198
}
199
+ [self updateWindowMetrics ];
185
200
return YES ;
186
201
}
187
202
203
+ - (void )setViewController : (FLEViewController*)controller {
204
+ _viewController = controller;
205
+ if (!controller && !_allowHeadlessExecution) {
206
+ [self shutDownEngine ];
207
+ _resourceContext = nil ;
208
+ }
209
+ [self updateWindowMetrics ];
210
+ }
211
+
188
212
- (id <FlutterBinaryMessenger>)binaryMessenger {
189
213
// TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins
190
214
// keeping the engine alive.
@@ -193,11 +217,33 @@ - (BOOL)run {
193
217
194
218
#pragma mark - Framework-internal methods
195
219
196
- - (void )updateWindowMetricsWithSize : (CGSize)size pixelRatio : (double )pixelRatio {
220
+ - (BOOL )running {
221
+ return _engine != nullptr ;
222
+ }
223
+
224
+ - (NSOpenGLContext *)resourceContext {
225
+ if (!_resourceContext) {
226
+ NSOpenGLPixelFormatAttribute attributes[] = {
227
+ NSOpenGLPFAColorSize , 24 , NSOpenGLPFAAlphaSize , 8 , NSOpenGLPFADoubleBuffer , 0 ,
228
+ };
229
+ NSOpenGLPixelFormat * pixelFormat = [[NSOpenGLPixelFormat alloc ] initWithAttributes: attributes];
230
+ _resourceContext = [[NSOpenGLContext alloc ] initWithFormat: pixelFormat shareContext: nil ];
231
+ }
232
+ return _resourceContext;
233
+ }
234
+
235
+ - (void )updateWindowMetrics {
236
+ if (!_engine) {
237
+ return ;
238
+ }
239
+ NSView * view = _viewController.view ;
240
+ CGSize scaledSize = [view convertRectToBacking: view.bounds].size ;
241
+ double pixelRatio = view.bounds .size .width == 0 ? 1 : scaledSize.width / view.bounds .size .width ;
242
+
197
243
const FlutterWindowMetricsEvent event = {
198
244
.struct_size = sizeof (event),
199
- .width = static_cast <size_t >(size .width ),
200
- .height = static_cast <size_t >(size .height ),
245
+ .width = static_cast <size_t >(scaledSize .width ),
246
+ .height = static_cast <size_t >(scaledSize .height ),
201
247
.pixel_ratio = pixelRatio,
202
248
};
203
249
FlutterEngineSendWindowMetricsEvent (_engine, &event);
@@ -237,7 +283,7 @@ - (bool)engineCallbackOnMakeResourceCurrent {
237
283
if (!_viewController.flutterView ) {
238
284
return false ;
239
285
}
240
- [_viewController makeResourceContextCurrent ];
286
+ [self .resourceContext makeCurrentContext ];
241
287
return true ;
242
288
}
243
289
@@ -269,6 +315,19 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
269
315
}
270
316
}
271
317
318
+ /* *
319
+ * Note: Called from dealloc. Should not use accessors or other methods.
320
+ */
321
+ - (void )shutDownEngine {
322
+ if (_engine) {
323
+ FlutterEngineResult result = FlutterEngineShutdown (_engine);
324
+ if (result != kSuccess ) {
325
+ NSLog (@" Failed to shut down Flutter engine: error %d " , result);
326
+ }
327
+ }
328
+ _engine = nullptr ;
329
+ }
330
+
272
331
#pragma mark - FlutterBinaryMessenger
273
332
274
333
- (void )sendOnChannel : (nonnull NSString *)channel message : (nullable NSData *)message {
0 commit comments