19
19
import io .flutter .embedding .engine .FlutterEngine ;
20
20
import io .flutter .embedding .engine .FlutterShellArgs ;
21
21
import io .flutter .embedding .engine .dart .DartExecutor ;
22
+ import io .flutter .plugin .platform .PlatformPlugin ;
22
23
import io .flutter .view .FlutterMain ;
23
24
24
25
import static android .content .ComponentCallbacks2 .TRIM_MEMORY_RUNNING_LOW ;
@@ -160,6 +161,8 @@ protected static Bundle createArgsBundle(@Nullable String dartEntrypoint,
160
161
private FlutterEngine flutterEngine ;
161
162
@ Nullable
162
163
private FlutterView flutterView ;
164
+ @ Nullable
165
+ private PlatformPlugin platformPlugin ;
163
166
164
167
public FlutterFragment () {
165
168
// Ensure that we at least have an empty Bundle of arguments so that we don't
@@ -181,11 +184,30 @@ public FlutterEngine getFlutterEngine() {
181
184
public void onAttach (Context context ) {
182
185
super .onAttach (context );
183
186
187
+ initializeFlutter (getContextCompat ());
188
+
184
189
// When "retain instance" is true, the FlutterEngine will survive configuration
185
190
// changes. Therefore, we create a new one only if one does not already exist.
186
191
if (flutterEngine == null ) {
187
192
createFlutterEngine ();
188
193
}
194
+
195
+ // Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin
196
+ // is bound to a specific Activity. Therefore, it needs to be created and configured
197
+ // every time this Fragment attaches to a new Activity.
198
+ // TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes
199
+ // control of the entire window. This is unacceptable for non-fullscreen
200
+ // use-cases.
201
+ platformPlugin = new PlatformPlugin (getActivity (), flutterEngine .getPlatformChannel ());
202
+ }
203
+
204
+ private void initializeFlutter (@ NonNull Context context ) {
205
+ String [] flutterShellArgsArray = getArguments ().getStringArray (ARG_FLUTTER_INITIALIZATION_ARGS );
206
+ FlutterShellArgs flutterShellArgs = new FlutterShellArgs (
207
+ flutterShellArgsArray != null ? flutterShellArgsArray : new String [] {}
208
+ );
209
+
210
+ FlutterMain .ensureInitializationComplete (context .getApplicationContext (), flutterShellArgs .toArray ());
189
211
}
190
212
191
213
/**
@@ -307,7 +329,72 @@ protected String getDartEntrypointFunctionName() {
307
329
// TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if possible.
308
330
public void onPostResume () {
309
331
Log .d (TAG , "onPostResume()" );
310
- flutterEngine .getLifecycleChannel ().appIsResumed ();
332
+ if (flutterEngine != null ) {
333
+ flutterEngine .getLifecycleChannel ().appIsResumed ();
334
+
335
+ // TODO(mattcarroll): find a better way to handle the update of UI overlays than calling through
336
+ // to platformPlugin. We're implicitly entangling the Window, Activity, Fragment,
337
+ // and engine all with this one call.
338
+ platformPlugin .onPostResume ();
339
+
340
+ // TODO(mattcarroll): consider a more abstract way to invoke this behavior. It is very strange for
341
+ // a Fragment to have a seemingly random View responsibility, but this is what
342
+ // existed in the original embedding and I don't have a good alternative yet.
343
+ flutterView .updateAccessibilityFeatures ();
344
+ } else {
345
+ Log .w (TAG , "onPostResume() invoked before FlutterFragment was attached to an Activity." );
346
+ }
347
+ }
348
+
349
+ @ Override
350
+ public void onPause () {
351
+ super .onPause ();
352
+ Log .d (TAG , "onPause()" );
353
+ flutterEngine .getLifecycleChannel ().appIsInactive ();
354
+ }
355
+
356
+ @ Override
357
+ public void onStop () {
358
+ super .onStop ();
359
+ Log .d (TAG , "onStop()" );
360
+ flutterEngine .getLifecycleChannel ().appIsPaused ();
361
+ }
362
+
363
+ @ Override
364
+ public void onDestroyView () {
365
+ super .onDestroyView ();
366
+ Log .d (TAG , "onDestroyView()" );
367
+ flutterView .detachFromFlutterEngine ();
368
+ }
369
+
370
+ @ Override
371
+ public void onDetach () {
372
+ super .onDetach ();
373
+ Log .d (TAG , "onDetach()" );
374
+
375
+ // Null out the platformPlugin to avoid a possible retain cycle between the plugin, this Fragment,
376
+ // and this Fragment's Activity.
377
+ platformPlugin = null ;
378
+
379
+ // Destroy our FlutterEngine if we're not set to retain it.
380
+ if (!retainFlutterIsolateAfterFragmentDestruction ()) {
381
+ flutterEngine .destroy ();
382
+ flutterEngine = null ;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Returns true if the {@link FlutterEngine} within this {@code FlutterFragment} should outlive
388
+ * the {@code FlutterFragment}, itself.
389
+ *
390
+ * Defaults to false. This method can be overridden in subclasses to retain the
391
+ * {@link FlutterEngine}.
392
+ */
393
+ // TODO(mattcarroll): consider a dynamic determination of this preference based on whether the
394
+ // engine was created automatically, or if the engine was provided manually.
395
+ // Manually provided engines should probably not be destroyed.
396
+ protected boolean retainFlutterIsolateAfterFragmentDestruction () {
397
+ return false ;
311
398
}
312
399
313
400
/**
0 commit comments