1919import io .flutter .embedding .engine .FlutterEngine ;
2020import io .flutter .embedding .engine .FlutterShellArgs ;
2121import io .flutter .embedding .engine .dart .DartExecutor ;
22+ import io .flutter .plugin .platform .PlatformPlugin ;
2223import io .flutter .view .FlutterMain ;
2324
2425import static android .content .ComponentCallbacks2 .TRIM_MEMORY_RUNNING_LOW ;
@@ -160,6 +161,8 @@ protected static Bundle createArgsBundle(@Nullable String dartEntrypoint,
160161 private FlutterEngine flutterEngine ;
161162 @ Nullable
162163 private FlutterView flutterView ;
164+ @ Nullable
165+ private PlatformPlugin platformPlugin ;
163166
164167 public FlutterFragment () {
165168 // Ensure that we at least have an empty Bundle of arguments so that we don't
@@ -181,11 +184,30 @@ public FlutterEngine getFlutterEngine() {
181184 public void onAttach (Context context ) {
182185 super .onAttach (context );
183186
187+ initializeFlutter (getContextCompat ());
188+
184189 // When "retain instance" is true, the FlutterEngine will survive configuration
185190 // changes. Therefore, we create a new one only if one does not already exist.
186191 if (flutterEngine == null ) {
187192 createFlutterEngine ();
188193 }
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 ());
189211 }
190212
191213 /**
@@ -307,7 +329,72 @@ protected String getDartEntrypointFunctionName() {
307329 // TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if possible.
308330 public void onPostResume () {
309331 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 ;
311398 }
312399
313400 /**
0 commit comments