@@ -41,6 +41,11 @@ - (bool)engineCallbackOnMakeResourceCurrent;
4141 */
4242- (void )engineCallbackOnPlatformMessage : (const FlutterPlatformMessage*)message ;
4343
44+ /* *
45+ * Shuts the Flutter engine if it is running.
46+ */
47+ - (void )shutDownEngine ;
48+
4449@end
4550
4651#pragma mark -
@@ -120,38 +125,46 @@ @implementation FLEEngine {
120125 // The embedding-API-level engine object.
121126 FlutterEngine _engine;
122127
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+
123134 // A mapping of channel names to the registered handlers for those channels.
124135 NSMutableDictionary <NSString *, FlutterBinaryMessageHandler>* _messageHandlers;
136+
137+ // Whether the engine can continue running after the view controller is removed.
138+ BOOL _allowHeadlessExecution;
125139}
126140
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 ];
129143}
130144
131- - (instancetype )initWithViewController : (FLEViewController*)viewController
132- project : (FLEDartProject*)project {
145+ - (instancetype )initWithName : (NSString *)labelPrefix
146+ project : (FLEDartProject*)project
147+ allowHeadlessExecution : (BOOL )allowHeadlessExecution {
133148 self = [super init ];
134149 NSAssert (self, @" Super init cannot be nil" );
135150
136- _viewController = viewController;
137151 _project = project ?: [[FLEDartProject alloc ] init ];
138152 _messageHandlers = [[NSMutableDictionary alloc ] init ];
139153
140154 return self;
141155}
142156
143157- (void )dealloc {
144- if (FlutterEngineShutdown (_engine) == kSuccess ) {
145- _engine = NULL ;
146- }
158+ [self shutDownEngine ];
147159}
148160
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+ }
152165
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. " );
155168 return NO ;
156169 }
157170
@@ -175,16 +188,27 @@ - (BOOL)run {
175188 flutterArguments.command_line_argc = static_cast <int >(arguments.size ());
176189 flutterArguments.command_line_argv = &arguments[0 ];
177190 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
191+ flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String ;
178192
179193 FlutterEngineResult result = FlutterEngineRun (
180194 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void *)(self), &_engine);
181195 if (result != kSuccess ) {
182196 NSLog (@" Failed to start Flutter engine: error %d " , result);
183197 return NO ;
184198 }
199+ [self updateWindowMetrics ];
185200 return YES ;
186201}
187202
203+ - (void )setViewController : (FLEViewController*)controller {
204+ _viewController = controller;
205+ if (!controller && !_allowHeadlessExecution) {
206+ [self shutDownEngine ];
207+ _resourceContext = nil ;
208+ }
209+ [self updateWindowMetrics ];
210+ }
211+
188212- (id <FlutterBinaryMessenger>)binaryMessenger {
189213 // TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins
190214 // keeping the engine alive.
@@ -193,11 +217,33 @@ - (BOOL)run {
193217
194218#pragma mark - Framework-internal methods
195219
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+
197243 const FlutterWindowMetricsEvent event = {
198244 .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 ),
201247 .pixel_ratio = pixelRatio,
202248 };
203249 FlutterEngineSendWindowMetricsEvent (_engine, &event);
@@ -237,7 +283,7 @@ - (bool)engineCallbackOnMakeResourceCurrent {
237283 if (!_viewController.flutterView ) {
238284 return false ;
239285 }
240- [_viewController makeResourceContextCurrent ];
286+ [self .resourceContext makeCurrentContext ];
241287 return true ;
242288}
243289
@@ -269,6 +315,19 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
269315 }
270316}
271317
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+
272331#pragma mark - FlutterBinaryMessenger
273332
274333- (void )sendOnChannel : (nonnull NSString *)channel message : (nullable NSData *)message {
0 commit comments