Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FlutterActivityAndFragmentDelegate.java
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
5package io.flutter.embedding.android;
6
7import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
8import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_INITIAL_ROUTE;
9
10import android.annotation.TargetApi;
11import android.app.Activity;
12import android.content.Context;
13import android.content.Intent;
14import android.net.Uri;
15import android.os.Bundle;
16import android.view.LayoutInflater;
17import android.view.View;
18import android.view.ViewGroup;
19import android.view.ViewTreeObserver.OnPreDrawListener;
20import android.window.BackEvent;
21import android.window.OnBackAnimationCallback;
22import androidx.annotation.NonNull;
23import androidx.annotation.Nullable;
24import androidx.annotation.RequiresApi;
25import androidx.annotation.VisibleForTesting;
26import androidx.lifecycle.Lifecycle;
27import io.flutter.Build.API_LEVELS;
28import io.flutter.FlutterInjector;
29import io.flutter.Log;
30import io.flutter.embedding.engine.FlutterEngine;
31import io.flutter.embedding.engine.FlutterEngineCache;
32import io.flutter.embedding.engine.FlutterEngineGroup;
33import io.flutter.embedding.engine.FlutterEngineGroupCache;
34import io.flutter.embedding.engine.FlutterShellArgs;
35import io.flutter.embedding.engine.dart.DartExecutor;
36import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
37import io.flutter.plugin.platform.PlatformPlugin;
38import java.util.Arrays;
39import java.util.List;
40
41/**
42 * Delegate that implements all Flutter logic that is the same between a {@link FlutterActivity} and
43 * a {@link FlutterFragment}.
44 *
45 * <p><strong>Why does this class exist?</strong>
46 *
47 * <p>One might ask why an {@code Activity} and {@code Fragment} delegate needs to exist. Given that
48 * a {@code Fragment} can be placed within an {@code Activity}, it would make more sense to use a
49 * {@link FlutterFragment} within a {@link FlutterActivity}.
50 *
51 * <p>The {@code Fragment} support library adds 100k of binary size to an app, and full-Flutter apps
52 * do not otherwise require that binary hit. Therefore, it was concluded that Flutter must provide a
53 * {@link FlutterActivity} based on the AOSP {@code Activity}, and an independent {@link
54 * FlutterFragment} for add-to-app developers.
55 *
56 * <p>If a time ever comes where the inclusion of {@code Fragment}s in a full-Flutter app is no
57 * longer deemed an issue, this class should be immediately decomposed between {@link
58 * FlutterActivity} and {@link FlutterFragment} and then eliminated.
59 *
60 * <p><strong>Caution when modifying this class</strong>
61 *
62 * <p>Any time that a "delegate" is created with the purpose of encapsulating the internal behaviors
63 * of another object, that delegate is highly susceptible to degeneration. It is easy to tack new
64 * responsibilities on to the delegate which would not otherwise be added to the original object. It
65 * is also easy to begin hanging listeners and callbacks on a delegate object that likewise would
66 * not be added to the original object. A delegate can quickly become a complex web of dependencies
67 * and optional references that are very difficult to track.
68 *
69 * <p>Maintainers of this class should take care to only place code in this delegate that would
70 * otherwise be placed in either {@link FlutterActivity} or {@link FlutterFragment}, and in exactly
71 * the same form. <strong>Do not use this class as a convenient shortcut for any other
72 * behavior.</strong>
73 */
74/* package */ class FlutterActivityAndFragmentDelegate implements ExclusiveAppComponent<Activity> {
75 private static final String TAG = "FlutterActivityAndFragmentDelegate";
76 private static final String FRAMEWORK_RESTORATION_BUNDLE_KEY = "framework";
77 private static final String PLUGINS_RESTORATION_BUNDLE_KEY = "plugins";
78 private static final int FLUTTER_SPLASH_VIEW_FALLBACK_ID = 486947586;
79
80 /** Factory to obtain a FlutterActivityAndFragmentDelegate instance. */
84
85 // The FlutterActivity or FlutterFragment that is delegating most of its calls
86 // to this FlutterActivityAndFragmentDelegate.
87 @NonNull private Host host;
88 @Nullable private FlutterEngine flutterEngine;
89 @VisibleForTesting @Nullable FlutterView flutterView;
90 @Nullable private PlatformPlugin platformPlugin;
91 @VisibleForTesting @Nullable OnPreDrawListener activePreDrawListener;
92 private boolean isFlutterEngineFromHost;
93 private boolean isFlutterUiDisplayed;
94 private boolean isFirstFrameRendered;
95 private boolean isAttached;
96 private Integer previousVisibility;
97 @Nullable private FlutterEngineGroup engineGroup;
98
99 @NonNull
100 private final FlutterUiDisplayListener flutterUiDisplayListener =
101 new FlutterUiDisplayListener() {
102 @Override
103 public void onFlutterUiDisplayed() {
104 host.onFlutterUiDisplayed();
105 isFlutterUiDisplayed = true;
106 isFirstFrameRendered = true;
107 }
108
109 @Override
110 public void onFlutterUiNoLongerDisplayed() {
111 host.onFlutterUiNoLongerDisplayed();
112 isFlutterUiDisplayed = false;
113 }
114 };
115
117 this(host, null);
118 }
119
120 FlutterActivityAndFragmentDelegate(@NonNull Host host, @Nullable FlutterEngineGroup engineGroup) {
121 this.host = host;
122 this.isFirstFrameRendered = false;
123 this.engineGroup = engineGroup;
124 }
125
126 /**
127 * Disconnects this {@code FlutterActivityAndFragmentDelegate} from its host {@code Activity} or
128 * {@code Fragment}.
129 *
130 * <p>No further method invocations may occur on this {@code FlutterActivityAndFragmentDelegate}
131 * after invoking this method. If a method is invoked, an exception will occur.
132 *
133 * <p>This method only clears out references. It does not destroy its {@link
134 * io.flutter.embedding.engine.FlutterEngine}. The behavior that destroys a {@link
135 * io.flutter.embedding.engine.FlutterEngine} can be found in {@link #onDetach()}.
136 */
137 void release() {
138 this.host = null;
139 this.flutterEngine = null;
140 this.flutterView = null;
141 this.platformPlugin = null;
142 }
143
144 /**
145 * Returns the {@link io.flutter.embedding.engine.FlutterEngine} that is owned by this delegate
146 * and its host {@code Activity} or {@code Fragment}.
147 */
148 @Nullable
150 return flutterEngine;
151 }
152
153 /**
154 * Returns true if the host {@code Activity}/{@code Fragment} provided a {@code FlutterEngine}, as
155 * opposed to this delegate creating a new one.
156 */
157 /* package */ boolean isFlutterEngineFromHost() {
158 return isFlutterEngineFromHost;
159 }
160
161 /**
162 * Whether or not this {@code FlutterActivityAndFragmentDelegate} is attached to a {@code
163 * FlutterEngine}.
164 */
165 /* package */ boolean isAttached() {
166 return isAttached;
167 }
168
169 /**
170 * Invoke this method from {@code Activity#onCreate(Bundle)} or {@code
171 * Fragment#onAttach(Context)}.
172 *
173 * <p>This method does the following:
174 *
175 * <p>
176 *
177 * <ol>
178 * <li>Initializes the Flutter system.
179 * <li>Obtains or creates a {@link io.flutter.embedding.engine.FlutterEngine}.
180 * <li>Creates and configures a {@link PlatformPlugin}.
181 * <li>Attaches the {@link io.flutter.embedding.engine.FlutterEngine} to the surrounding {@code
182 * Activity}, if desired.
183 * <li>Configures the {@link io.flutter.embedding.engine.FlutterEngine} via {@link
184 * Host#configureFlutterEngine(FlutterEngine)}.
185 * </ol>
186 */
187 void onAttach(@NonNull Context context) {
188 ensureAlive();
189
190 // When "retain instance" is true, the FlutterEngine will survive configuration
191 // changes. Therefore, we create a new one only if one does not already exist.
192 if (flutterEngine == null) {
194 }
195
196 if (host.shouldAttachEngineToActivity()) {
197 // Notify any plugins that are currently attached to our FlutterEngine that they
198 // are now attached to an Activity.
199 //
200 // Passing this Fragment's Lifecycle should be sufficient because as long as this Fragment
201 // is attached to its Activity, the lifecycles should be in sync. Once this Fragment is
202 // detached from its Activity, that Activity will be detached from the FlutterEngine, too,
203 // which means there shouldn't be any possibility for the Fragment Lifecycle to get out of
204 // sync with the Activity. We use the Fragment's Lifecycle because it is possible that the
205 // attached Activity is not a LifecycleOwner.
206 Log.v(TAG, "Attaching FlutterEngine to the Activity that owns this delegate.");
207 flutterEngine.getActivityControlSurface().attachToActivity(this, host.getLifecycle());
208 }
209
210 // Regardless of whether or not a FlutterEngine already existed, the PlatformPlugin
211 // is bound to a specific Activity. Therefore, it needs to be created and configured
212 // every time this Fragment attaches to a new Activity.
213 // TODO(mattcarroll): the PlatformPlugin needs to be reimagined because it implicitly takes
214 // control of the entire window. This is unacceptable for non-fullscreen
215 // use-cases.
216 platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
217
218 host.configureFlutterEngine(flutterEngine);
219 isAttached = true;
220 }
221
222 @Override
223 public @NonNull Activity getAppComponent() {
224 final Activity activity = host.getActivity();
225 if (activity == null) {
226 throw new AssertionError(
227 "FlutterActivityAndFragmentDelegate's getAppComponent should only "
228 + "be queried after onAttach, when the host's activity should always be non-null");
229 }
230 return activity;
231 }
232
233 private FlutterEngineGroup.Options addEntrypointOptions(FlutterEngineGroup.Options options) {
234 String appBundlePathOverride = host.getAppBundlePath();
235 if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
236 appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
237 }
238
239 DartExecutor.DartEntrypoint dartEntrypoint =
240 new DartExecutor.DartEntrypoint(
241 appBundlePathOverride, host.getDartEntrypointFunctionName());
242 String initialRoute = host.getInitialRoute();
243 if (initialRoute == null) {
244 initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
245 if (initialRoute == null) {
246 initialRoute = DEFAULT_INITIAL_ROUTE;
247 }
248 }
249 return options
250 .setDartEntrypoint(dartEntrypoint)
251 .setInitialRoute(initialRoute)
252 .setDartEntrypointArgs(host.getDartEntrypointArgs());
253 }
254
255 /**
256 * Obtains a reference to a FlutterEngine to back this delegate and its {@code host}.
257 *
258 * <p>
259 *
260 * <p>First, the {@code host} is asked if it would like to use a cached {@link
261 * io.flutter.embedding.engine.FlutterEngine}, and if so, the cached {@link
262 * io.flutter.embedding.engine.FlutterEngine} is retrieved.
263 *
264 * <p>Second, the {@code host} is given an opportunity to provide a {@link
265 * io.flutter.embedding.engine.FlutterEngine} via {@link Host#provideFlutterEngine(Context)}.
266 *
267 * <p>Third, the {@code host} is asked if it would like to use a cached {@link
268 * io.flutter.embedding.engine.FlutterEngineGroup} to create a new {@link FlutterEngine} by {@link
269 * FlutterEngineGroup#createAndRunEngine}
270 *
271 * <p>If the {@code host} does not provide a {@link io.flutter.embedding.engine.FlutterEngine},
272 * then a new {@link FlutterEngine} is instantiated.
273 */
274 @VisibleForTesting
275 /* package */ void setUpFlutterEngine() {
276 Log.v(TAG, "Setting up FlutterEngine.");
277
278 // First, check if the host wants to use a cached FlutterEngine.
279 String cachedEngineId = host.getCachedEngineId();
280 if (cachedEngineId != null) {
281 flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
282 isFlutterEngineFromHost = true;
283 if (flutterEngine == null) {
284 throw new IllegalStateException(
285 "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"
286 + cachedEngineId
287 + "'");
288 }
289 return;
290 }
291
292 // Second, defer to subclasses for a custom FlutterEngine.
293 flutterEngine = host.provideFlutterEngine(host.getContext());
294 if (flutterEngine != null) {
295 isFlutterEngineFromHost = true;
296 return;
297 }
298
299 // Third, check if the host wants to use a cached FlutterEngineGroup
300 // and create new FlutterEngine using FlutterEngineGroup#createAndRunEngine
301 String cachedEngineGroupId = host.getCachedEngineGroupId();
302 if (cachedEngineGroupId != null) {
303 FlutterEngineGroup flutterEngineGroup =
304 FlutterEngineGroupCache.getInstance().get(cachedEngineGroupId);
305 if (flutterEngineGroup == null) {
306 throw new IllegalStateException(
307 "The requested cached FlutterEngineGroup did not exist in the FlutterEngineGroupCache: '"
308 + cachedEngineGroupId
309 + "'");
310 }
311
312 flutterEngine =
313 flutterEngineGroup.createAndRunEngine(
314 addEntrypointOptions(new FlutterEngineGroup.Options(host.getContext())));
315 isFlutterEngineFromHost = false;
316 return;
317 }
318
319 // Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our
320 // FlutterView.
321 Log.v(
322 TAG,
323 "No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
324 + " this FlutterFragment.");
325
326 FlutterEngineGroup group =
327 engineGroup == null
328 ? new FlutterEngineGroup(host.getContext(), host.getFlutterShellArgs().toArray())
329 : engineGroup;
330 flutterEngine =
331 group.createAndRunEngine(
332 addEntrypointOptions(
333 new FlutterEngineGroup.Options(host.getContext())
334 .setAutomaticallyRegisterPlugins(false)
335 .setWaitForRestorationData(host.shouldRestoreAndSaveState())));
336 isFlutterEngineFromHost = false;
337 }
338
339 /**
340 * Invoke this method from {@code Activity#onCreate(Bundle)} to create the content {@code View},
341 * or from {@code Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
342 *
343 * <p>{@code inflater} and {@code container} may be null when invoked from an {@code Activity}.
344 *
345 * <p>{@code shouldDelayFirstAndroidViewDraw} determines whether to set up an {@link
346 * android.view.ViewTreeObserver.OnPreDrawListener}, which will defer the current drawing pass
347 * till after the Flutter UI has been displayed. This results in more accurate timings reported
348 * with Android tools, such as "Displayed" timing printed with `am start`.
349 *
350 * <p>Note that it should only be set to true when {@code Host#getRenderMode()} is {@code
351 * RenderMode.surface}.
352 *
353 * <p>This method:
354 *
355 * <ol>
356 * <li>creates a new {@link FlutterView} in a {@code View} hierarchy
357 * <li>adds a {@link FlutterUiDisplayListener} to it
358 * <li>attaches a {@link io.flutter.embedding.engine.FlutterEngine} to the new {@link
359 * FlutterView}
360 * <li>returns the new {@code View} hierarchy
361 * </ol>
362 */
363 @NonNull
365 LayoutInflater inflater,
366 @Nullable ViewGroup container,
367 @Nullable Bundle savedInstanceState,
368 int flutterViewId,
369 boolean shouldDelayFirstAndroidViewDraw) {
370 Log.v(TAG, "Creating FlutterView.");
371 ensureAlive();
372
373 if (host.getRenderMode() == RenderMode.surface) {
374 FlutterSurfaceView flutterSurfaceView =
376 host.getContext(), host.getTransparencyMode() == TransparencyMode.transparent);
377
378 // Allow our host to customize FlutterSurfaceView, if desired.
379 host.onFlutterSurfaceViewCreated(flutterSurfaceView);
380
381 // Create the FlutterView that owns the FlutterSurfaceView.
382 flutterView = new FlutterView(host.getContext(), flutterSurfaceView);
383 } else {
384 FlutterTextureView flutterTextureView = new FlutterTextureView(host.getContext());
385
386 flutterTextureView.setOpaque(host.getTransparencyMode() == TransparencyMode.opaque);
387
388 // Allow our host to customize FlutterSurfaceView, if desired.
389 host.onFlutterTextureViewCreated(flutterTextureView);
390
391 // Create the FlutterView that owns the FlutterTextureView.
392 flutterView = new FlutterView(host.getContext(), flutterTextureView);
393 }
394
395 // Add listener to be notified when Flutter renders its first frame.
396 flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
397
398 if (host.attachToEngineAutomatically()) {
399 Log.v(TAG, "Attaching FlutterEngine to FlutterView.");
400 flutterView.attachToFlutterEngine(flutterEngine);
401 }
402 flutterView.setId(flutterViewId);
403
404 if (shouldDelayFirstAndroidViewDraw) {
405 delayFirstAndroidViewDraw(flutterView);
406 }
407 return flutterView;
408 }
409
410 void onRestoreInstanceState(@Nullable Bundle bundle) {
411 Log.v(
412 TAG,
413 "onRestoreInstanceState. Giving framework and plugins an opportunity to restore state.");
414 ensureAlive();
415
416 Bundle pluginState = null;
417 byte[] frameworkState = null;
418 if (bundle != null) {
419 pluginState = bundle.getBundle(PLUGINS_RESTORATION_BUNDLE_KEY);
420 frameworkState = bundle.getByteArray(FRAMEWORK_RESTORATION_BUNDLE_KEY);
421 }
422
423 if (host.shouldRestoreAndSaveState()) {
424 flutterEngine.getRestorationChannel().setRestorationData(frameworkState);
425 }
426
427 if (host.shouldAttachEngineToActivity()) {
428 flutterEngine.getActivityControlSurface().onRestoreInstanceState(pluginState);
429 }
430 }
431
432 /**
433 * Invoke this from {@code Activity#onStart()} or {@code Fragment#onStart()}.
434 *
435 * <p>This method:
436 *
437 * <p>
438 *
439 * <ol>
440 * <li>Begins executing Dart code, if it is not already executing.
441 * </ol>
442 */
443 void onStart() {
444 Log.v(TAG, "onStart()");
445 ensureAlive();
446 doInitialFlutterViewRun();
447 // This is a workaround for a bug on some OnePlus phones. The visibility of the application
448 // window is still true after locking the screen on some OnePlus phones, and shows a black
449 // screen when unlocked. We can work around this by changing the visibility of FlutterView in
450 // onStart and onStop.
451 // See https://github.com/flutter/flutter/issues/93276
452 if (previousVisibility != null) {
453 flutterView.setVisibility(previousVisibility);
454 }
455 }
456
457 /**
458 * Starts running Dart within the FlutterView for the first time.
459 *
460 * <p>Reloading/restarting Dart within a given FlutterView is not supported. If this method is
461 * invoked while Dart is already executing then it does nothing.
462 *
463 * <p>{@code flutterEngine} must be non-null when invoking this method.
464 */
465 private void doInitialFlutterViewRun() {
466 // Don't attempt to start a FlutterEngine if we're using a cached FlutterEngine.
467 if (host.getCachedEngineId() != null) {
468 return;
469 }
470
471 if (flutterEngine.getDartExecutor().isExecutingDart()) {
472 // No warning is logged because this situation will happen on every config
473 // change if the developer does not choose to retain the Fragment instance.
474 // So this is expected behavior in many cases.
475 return;
476 }
477 String initialRoute = host.getInitialRoute();
478 if (initialRoute == null) {
479 initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
480 if (initialRoute == null) {
481 initialRoute = DEFAULT_INITIAL_ROUTE;
482 }
483 }
484 @Nullable String libraryUri = host.getDartEntrypointLibraryUri();
485 Log.v(
486 TAG,
487 "Executing Dart entrypoint: "
488 + host.getDartEntrypointFunctionName()
489 + ", library uri: "
490 + libraryUri
491 == null
492 ? "\"\""
493 : libraryUri + ", and sending initial route: " + initialRoute);
494
495 // The engine needs to receive the Flutter app's initial route before executing any
496 // Dart code to ensure that the initial route arrives in time to be applied.
497 flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);
498
499 String appBundlePathOverride = host.getAppBundlePath();
500 if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
501 appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
502 }
503
504 // Configure the Dart entrypoint and execute it.
505 DartExecutor.DartEntrypoint entrypoint =
506 libraryUri == null
507 ? new DartExecutor.DartEntrypoint(
508 appBundlePathOverride, host.getDartEntrypointFunctionName())
509 : new DartExecutor.DartEntrypoint(
510 appBundlePathOverride, libraryUri, host.getDartEntrypointFunctionName());
511 flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint, host.getDartEntrypointArgs());
512 }
513
514 private String maybeGetInitialRouteFromIntent(Intent intent) {
515 if (host.shouldHandleDeeplinking()) {
516 Uri data = intent.getData();
517 if (data != null) {
518 return data.toString();
519 }
520 }
521 return null;
522 }
523
524 /**
525 * Delays the first drawing of the {@code flutterView} until the Flutter first has been displayed.
526 */
527 private void delayFirstAndroidViewDraw(FlutterView flutterView) {
528 if (host.getRenderMode() != RenderMode.surface) {
529 // Using a TextureView will cause a deadlock, where the underlying SurfaceTexture is never
530 // available since it will wait for drawing to be completed first. At the same time, the
531 // preDraw listener keeps returning false since the Flutter Engine waits for the
532 // SurfaceTexture to be available.
533 throw new IllegalArgumentException(
534 "Cannot delay the first Android view draw when the render mode is not set to"
535 + " `RenderMode.surface`.");
536 }
537
538 if (activePreDrawListener != null) {
539 flutterView.getViewTreeObserver().removeOnPreDrawListener(activePreDrawListener);
540 }
541
543 new OnPreDrawListener() {
544 @Override
545 public boolean onPreDraw() {
546 if (isFlutterUiDisplayed && activePreDrawListener != null) {
547 flutterView.getViewTreeObserver().removeOnPreDrawListener(this);
549 }
550 return isFlutterUiDisplayed;
551 }
552 };
553 flutterView.getViewTreeObserver().addOnPreDrawListener(activePreDrawListener);
554 }
555
556 /**
557 * Invoke this from {@code Activity#onResume()} or {@code Fragment#onResume()}.
558 *
559 * <p>This method notifies the running Flutter app that it is "resumed" as per the Flutter app
560 * lifecycle.
561 */
562 void onResume() {
563 Log.v(TAG, "onResume()");
564 ensureAlive();
565 if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
566 flutterEngine.getLifecycleChannel().appIsResumed();
567 }
568 }
569
570 /**
571 * Invoke this from {@code Activity#onPostResume()}.
572 *
573 * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
574 * the {@code Fragment} can then invoke this method.
575 *
576 * <p>This method informs the {@link PlatformPlugin} that {@code onPostResume()} has run, which is
577 * used to update system UI overlays.
578 */
579 // TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if
580 // possible.
582 Log.v(TAG, "onPostResume()");
583 ensureAlive();
584 if (flutterEngine != null) {
586 } else {
587 Log.w(TAG, "onPostResume() invoked before FlutterFragment was attached to an Activity.");
588 }
589 }
590
591 /**
592 * Refreshes Android's window system UI (AKA system chrome) to match Flutter's desired system
593 * chrome style.
594 */
596 if (platformPlugin != null) {
597 // TODO(mattcarroll): find a better way to handle the update of UI overlays than calling
598 // through to platformPlugin. We're implicitly entangling the Window, Activity,
599 // Fragment, and engine all with this one call.
600 platformPlugin.updateSystemUiOverlays();
601 }
602 }
603
604 /**
605 * Invoke this from {@code Activity#onPause()} or {@code Fragment#onPause()}.
606 *
607 * <p>This method notifies the running Flutter app that it is "inactive" as per the Flutter app
608 * lifecycle.
609 */
610 void onPause() {
611 Log.v(TAG, "onPause()");
612 ensureAlive();
613 if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
614 flutterEngine.getLifecycleChannel().appIsInactive();
615 }
616 }
617
618 /**
619 * Invoke this from {@code Activity#onStop()} or {@code Fragment#onStop()}.
620 *
621 * <p>This method:
622 *
623 * <p>
624 *
625 * <ol>
626 * <li>This method notifies the running Flutter app that it is "paused" as per the Flutter app
627 * lifecycle.
628 * <li>Detaches this delegate's {@link io.flutter.embedding.engine.FlutterEngine} from this
629 * delegate's {@link FlutterView}.
630 * </ol>
631 */
632 void onStop() {
633 Log.v(TAG, "onStop()");
634 ensureAlive();
635
636 if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
637 flutterEngine.getLifecycleChannel().appIsPaused();
638 }
639
640 // This is a workaround for a bug on some OnePlus phones. The visibility of the application
641 // window is still true after locking the screen on some OnePlus phones, and shows a black
642 // screen when unlocked. We can work around this by changing the visibility of FlutterView in
643 // onStart and onStop.
644 // See https://github.com/flutter/flutter/issues/93276
645 previousVisibility = flutterView.getVisibility();
646 flutterView.setVisibility(View.GONE);
647 }
648
649 /**
650 * Invoke this from {@code Activity#onDestroy()} or {@code Fragment#onDestroyView()}.
651 *
652 * <p>This method removes this delegate's {@link FlutterView}'s {@link FlutterUiDisplayListener}.
653 */
655 Log.v(TAG, "onDestroyView()");
656 ensureAlive();
657
658 if (activePreDrawListener != null) {
659 flutterView.getViewTreeObserver().removeOnPreDrawListener(activePreDrawListener);
661 }
662
663 // flutterView can be null in instances where a delegate.onDestroyView is called without
664 // onCreateView being called. See https://github.com/flutter/engine/pull/41082 for more detail.
665 if (flutterView != null) {
667 flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
668 }
669 }
670
671 void onSaveInstanceState(@Nullable Bundle bundle) {
672 Log.v(TAG, "onSaveInstanceState. Giving framework and plugins an opportunity to save state.");
673 ensureAlive();
674
675 if (host.shouldRestoreAndSaveState()) {
676 bundle.putByteArray(
677 FRAMEWORK_RESTORATION_BUNDLE_KEY,
678 flutterEngine.getRestorationChannel().getRestorationData());
679 }
680
681 if (host.shouldAttachEngineToActivity()) {
682 final Bundle plugins = new Bundle();
683 flutterEngine.getActivityControlSurface().onSaveInstanceState(plugins);
684 bundle.putBundle(PLUGINS_RESTORATION_BUNDLE_KEY, plugins);
685 }
686 }
687
688 @Override
690 if (host.shouldDestroyEngineWithHost()) {
691 // The host owns the engine and should never have its engine taken by another exclusive
692 // activity.
693 throw new AssertionError(
694 "The internal FlutterEngine created by "
695 + host
696 + " has been attached to by another activity. To persist a FlutterEngine beyond the "
697 + "ownership of this activity, explicitly create a FlutterEngine");
698 }
699
700 // Default, but customizable, behavior is for the host to call {@link #onDetach}
701 // deterministically as to not mix more events during the lifecycle of the next exclusive
702 // activity.
703 host.detachFromFlutterEngine();
704 }
705
706 /**
707 * Invoke this from {@code Activity#onDestroy()} or {@code Fragment#onDetach()}.
708 *
709 * <p>This method:
710 *
711 * <p>
712 *
713 * <ol>
714 * <li>Detaches this delegate's {@link io.flutter.embedding.engine.FlutterEngine} from its
715 * surrounding {@code Activity}, if it was previously attached.
716 * <li>Destroys this delegate's {@link PlatformPlugin}.
717 * <li>Destroys this delegate's {@link io.flutter.embedding.engine.FlutterEngine} if {@link
718 * Host#shouldDestroyEngineWithHost()} ()} returns true.
719 * </ol>
720 */
721 void onDetach() {
722 if (!isAttached) {
723 // Already detached.
724 return;
725 }
726 Log.v(TAG, "onDetach()");
727 ensureAlive();
728
729 // Give the host an opportunity to cleanup any references that were created in
730 // configureFlutterEngine().
731 host.cleanUpFlutterEngine(flutterEngine);
732
733 if (host.shouldAttachEngineToActivity()) {
734 // Notify plugins that they are no longer attached to an Activity.
735 Log.v(TAG, "Detaching FlutterEngine from the Activity that owns this Fragment.");
736 if (host.getActivity().isChangingConfigurations()) {
737 flutterEngine.getActivityControlSurface().detachFromActivityForConfigChanges();
738 } else {
739 flutterEngine.getActivityControlSurface().detachFromActivity();
740 }
741 }
742
743 // Null out the platformPlugin to avoid a possible retain cycle between the plugin, this
744 // Fragment,
745 // and this Fragment's Activity.
746 if (platformPlugin != null) {
747 platformPlugin.destroy();
748 platformPlugin = null;
749 }
750
751 if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
752 flutterEngine.getLifecycleChannel().appIsDetached();
753 }
754
755 // Destroy our FlutterEngine if we're not set to retain it.
756 if (host.shouldDestroyEngineWithHost()) {
757 flutterEngine.destroy();
758
759 if (host.getCachedEngineId() != null) {
760 FlutterEngineCache.getInstance().remove(host.getCachedEngineId());
761 }
762
763 flutterEngine = null;
764 }
765
766 isAttached = false;
767 }
768
769 /**
770 * Invoke this from {@link android.app.Activity#onBackPressed()}.
771 *
772 * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
773 * the {@code Fragment} can then invoke this method.
774 *
775 * <p>This method instructs Flutter's navigation system to "pop route".
776 */
778 ensureAlive();
779 if (flutterEngine != null) {
780 Log.v(TAG, "Forwarding onBackPressed() to FlutterEngine.");
781 flutterEngine.getNavigationChannel().popRoute();
782 } else {
783 Log.w(TAG, "Invoked onBackPressed() before FlutterFragment was attached to an Activity.");
784 }
785 }
786
787 /**
788 * Invoke this from {@link OnBackAnimationCallback#onBackStarted(BackEvent)}.
789 *
790 * <p>This method should be called when the back gesture is initiated. It should be invoked as
791 * part of the implementation of {@link OnBackAnimationCallback}.
792 *
793 * <p>This method delegates the handling of the start of a back gesture to the Flutter framework,
794 * which is responsible for the appropriate response, such as initiating animations or preparing
795 * the UI for the back navigation process.
796 *
797 * @param backEvent The BackEvent object containing information about the touch.
798 */
799 @TargetApi(API_LEVELS.API_34)
800 @RequiresApi(API_LEVELS.API_34)
801 void startBackGesture(@NonNull BackEvent backEvent) {
802 ensureAlive();
803 if (flutterEngine != null) {
804 Log.v(TAG, "Forwarding startBackGesture() to FlutterEngine.");
805 flutterEngine.getBackGestureChannel().startBackGesture(backEvent);
806 } else {
807 Log.w(TAG, "Invoked startBackGesture() before FlutterFragment was attached to an Activity.");
808 }
809 }
810
811 /**
812 * Invoke this from {@link OnBackAnimationCallback#onBackProgressed(BackEvent)}.
813 *
814 * <p>This method should be called in response to progress in a back gesture, as part of the
815 * implementation of {@link OnBackAnimationCallback}.
816 *
817 * <p>This method delegates to the Flutter framework to update UI elements or animations based on
818 * the progression of the back gesture.
819 *
820 * @param backEvent An BackEvent object describing the progress event.
821 */
822 @TargetApi(API_LEVELS.API_34)
823 @RequiresApi(API_LEVELS.API_34)
824 void updateBackGestureProgress(@NonNull BackEvent backEvent) {
825 ensureAlive();
826 if (flutterEngine != null) {
827 Log.v(TAG, "Forwarding updateBackGestureProgress() to FlutterEngine.");
828 flutterEngine.getBackGestureChannel().updateBackGestureProgress(backEvent);
829 } else {
830 Log.w(
831 TAG,
832 "Invoked updateBackGestureProgress() before FlutterFragment was attached to an Activity.");
833 }
834 }
835
836 /**
837 * Invoke this from {@link OnBackAnimationCallback#onBackInvoked()}.
838 *
839 * <p>This method is called to signify the completion of a back gesture and commits the navigation
840 * action initiated by the gesture. It should be invoked as the final step in handling a back
841 * gesture.
842 *
843 * <p>This method indicates to the Flutter framework that it should proceed with the back
844 * navigation, including finalizing animations and updating the UI to reflect the navigation
845 * outcome.
846 */
847 @TargetApi(API_LEVELS.API_34)
848 @RequiresApi(API_LEVELS.API_34)
850 ensureAlive();
851 if (flutterEngine != null) {
852 Log.v(TAG, "Forwarding commitBackGesture() to FlutterEngine.");
853 flutterEngine.getBackGestureChannel().commitBackGesture();
854 } else {
855 Log.w(TAG, "Invoked commitBackGesture() before FlutterFragment was attached to an Activity.");
856 }
857 }
858
859 /**
860 * Invoke this from {@link OnBackAnimationCallback#onBackCancelled()}.
861 *
862 * <p>This method should be called when a back gesture is cancelled or the back button is pressed.
863 * It informs the Flutter framework about the cancellation.
864 *
865 * <p>This method enables the Flutter framework to rollback any UI changes or animations initiated
866 * in response to the back gesture. This includes resetting UI elements to their state prior to
867 * the gesture's start.
868 */
869 @TargetApi(API_LEVELS.API_34)
870 @RequiresApi(API_LEVELS.API_34)
872 ensureAlive();
873 if (flutterEngine != null) {
874 Log.v(TAG, "Forwarding cancelBackGesture() to FlutterEngine.");
875 flutterEngine.getBackGestureChannel().cancelBackGesture();
876 } else {
877 Log.w(TAG, "Invoked cancelBackGesture() before FlutterFragment was attached to an Activity.");
878 }
879 }
880
881 /**
882 * Invoke this from {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])}
883 * or {@code Fragment#onRequestPermissionsResult(int, String[], int[])}.
884 *
885 * <p>This method forwards to interested Flutter plugins.
886 */
888 int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
889 ensureAlive();
890 if (flutterEngine != null) {
891 Log.v(
892 TAG,
893 "Forwarding onRequestPermissionsResult() to FlutterEngine:\n"
894 + "requestCode: "
895 + requestCode
896 + "\n"
897 + "permissions: "
898 + Arrays.toString(permissions)
899 + "\n"
900 + "grantResults: "
901 + Arrays.toString(grantResults));
902 flutterEngine
903 .getActivityControlSurface()
904 .onRequestPermissionsResult(requestCode, permissions, grantResults);
905 } else {
906 Log.w(
907 TAG,
908 "onRequestPermissionResult() invoked before FlutterFragment was attached to an Activity.");
909 }
910 }
911
912 /**
913 * Invoke this from {@code Activity#onNewIntent(Intent)}.
914 *
915 * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
916 * the {@code Fragment} can then invoke this method.
917 *
918 * <p>This method forwards to interested Flutter plugins.
919 */
920 void onNewIntent(@NonNull Intent intent) {
921 ensureAlive();
922 if (flutterEngine != null) {
923 Log.v(
924 TAG,
925 "Forwarding onNewIntent() to FlutterEngine and sending pushRouteInformation message.");
926 flutterEngine.getActivityControlSurface().onNewIntent(intent);
927 String initialRoute = maybeGetInitialRouteFromIntent(intent);
928 if (initialRoute != null && !initialRoute.isEmpty()) {
929 flutterEngine.getNavigationChannel().pushRouteInformation(initialRoute);
930 }
931 } else {
932 Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity.");
933 }
934 }
935
936 /**
937 * Invoke this from {@code Activity#onActivityResult(int, int, Intent)} or {@code
938 * Fragment#onActivityResult(int, int, Intent)}.
939 *
940 * <p>This method forwards to interested Flutter plugins.
941 */
942 void onActivityResult(int requestCode, int resultCode, Intent data) {
943 ensureAlive();
944 if (flutterEngine != null) {
945 Log.v(
946 TAG,
947 "Forwarding onActivityResult() to FlutterEngine:\n"
948 + "requestCode: "
949 + requestCode
950 + "\n"
951 + "resultCode: "
952 + resultCode
953 + "\n"
954 + "data: "
955 + data);
956 flutterEngine.getActivityControlSurface().onActivityResult(requestCode, resultCode, data);
957 } else {
958 Log.w(TAG, "onActivityResult() invoked before FlutterFragment was attached to an Activity.");
959 }
960 }
961
962 /**
963 * Invoke this from {@code Activity#onUserLeaveHint()}.
964 *
965 * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
966 * the {@code Fragment} can then invoke this method.
967 *
968 * <p>This method forwards to interested Flutter plugins.
969 */
971 ensureAlive();
972 if (flutterEngine != null) {
973 Log.v(TAG, "Forwarding onUserLeaveHint() to FlutterEngine.");
974 flutterEngine.getActivityControlSurface().onUserLeaveHint();
975 } else {
976 Log.w(TAG, "onUserLeaveHint() invoked before FlutterFragment was attached to an Activity.");
977 }
978 }
979
980 /**
981 * Invoke this from {@code Activity#onWindowFocusChanged()}.
982 *
983 * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
984 * the {@code Fragment} can then invoke this method.
985 */
986 void onWindowFocusChanged(boolean hasFocus) {
987 ensureAlive();
988 Log.v(TAG, "Received onWindowFocusChanged: " + (hasFocus ? "true" : "false"));
989 if (host.shouldDispatchAppLifecycleState() && flutterEngine != null) {
990 // TODO(gspencergoog): Once we have support for multiple windows/views,
991 // this code will need to consult the list of windows/views to determine if
992 // any windows in the app are focused and call the appropriate function.
993 if (hasFocus) {
994 flutterEngine.getLifecycleChannel().aWindowIsFocused();
995 } else {
996 flutterEngine.getLifecycleChannel().noWindowsAreFocused();
997 }
998 }
999 }
1000
1001 /**
1002 * Invoke this from {@link android.app.Activity#onTrimMemory(int)}.
1003 *
1004 * <p>A {@code Fragment} host must have its containing {@code Activity} forward this call so that
1005 * the {@code Fragment} can then invoke this method.
1006 *
1007 * <p>This method sends a "memory pressure warning" message to Flutter over the "system channel".
1008 */
1009 void onTrimMemory(int level) {
1010 ensureAlive();
1011 if (flutterEngine != null) {
1012 // Use a trim level delivered while the application is running so the
1013 // framework has a chance to react to the notification.
1014 // Avoid being too aggressive before the first frame is rendered. If it is
1015 // not at least running critical, we should avoid delaying the frame for
1016 // an overly aggressive GC.
1017 boolean trim = isFirstFrameRendered && level >= TRIM_MEMORY_RUNNING_LOW;
1018 if (trim) {
1019 flutterEngine.getDartExecutor().notifyLowMemoryWarning();
1020 flutterEngine.getSystemChannel().sendMemoryPressureWarning();
1021 }
1022 flutterEngine.getRenderer().onTrimMemory(level);
1023 }
1024 }
1025
1026 /**
1027 * Ensures that this delegate has not been {@link #release()}'ed.
1028 *
1029 * <p>An {@code IllegalStateException} is thrown if this delegate has been {@link #release()}'ed.
1030 */
1031 private void ensureAlive() {
1032 if (host == null) {
1033 throw new IllegalStateException(
1034 "Cannot execute method on a destroyed FlutterActivityAndFragmentDelegate.");
1035 }
1036 }
1037
1038 /**
1039 * The {@link FlutterActivity} or {@link FlutterFragment} that owns this {@code
1040 * FlutterActivityAndFragmentDelegate}.
1041 */
1042 /* package */ interface Host
1043 extends FlutterEngineProvider,
1045 PlatformPlugin.PlatformPluginDelegate {
1046 /**
1047 * Returns the {@link Context} that backs the host {@link android.app.Activity} or {@code
1048 * Fragment}.
1049 */
1050 @NonNull
1052
1053 /** Returns true if the delegate should retrieve the initial route from the {@link Intent}. */
1054 @Nullable
1056
1057 /**
1058 * Returns the host {@link android.app.Activity} or the {@code Activity} that is currently
1059 * attached to the host {@code Fragment}.
1060 */
1061 @Nullable
1062 Activity getActivity();
1063
1064 /**
1065 * Returns the {@link Lifecycle} that backs the host {@link android.app.Activity} or {@code
1066 * Fragment}.
1067 */
1068 @NonNull
1069 Lifecycle getLifecycle();
1070
1071 /** Returns the {@link FlutterShellArgs} that should be used when initializing Flutter. */
1072 @NonNull
1073 FlutterShellArgs getFlutterShellArgs();
1074
1075 /**
1076 * Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngine} to
1077 * use within this delegate's host, or {@code null} if this delegate's host does not want to use
1078 * a cached {@link FlutterEngine}.
1079 */
1080 @Nullable
1082
1083 @Nullable
1085
1086 /**
1087 * Returns true if the {@link io.flutter.embedding.engine.FlutterEngine} used in this delegate
1088 * should be destroyed when the host/delegate are destroyed.
1089 *
1090 * <p>The default value is {@code true} in cases where {@code FlutterFragment} created its own
1091 * {@link io.flutter.embedding.engine.FlutterEngine}, and {@code false} in cases where a cached
1092 * {@link io.flutter.embedding.engine.FlutterEngine} was provided.
1093 */
1095
1096 /**
1097 * Callback called when the {@link io.flutter.embedding.engine.FlutterEngine} has been attached
1098 * to by another activity before this activity was destroyed.
1099 *
1100 * <p>The expected behavior is for this activity to synchronously stop using the {@link
1101 * FlutterEngine} to avoid lifecycle crosstalk with the new activity.
1102 */
1104
1105 /**
1106 * Returns the Dart entrypoint that should run when a new {@link
1107 * io.flutter.embedding.engine.FlutterEngine} is created.
1108 */
1109 @NonNull
1111
1112 /**
1113 * Returns the URI of the Dart library which contains the entrypoint method (example
1114 * "package:foo_package/main.dart"). If null, this will default to the same library as the
1115 * `main()` function in the Dart program.
1116 */
1117 @Nullable
1119
1120 /** Returns arguments that passed as a list of string to Dart's entrypoint function. */
1121 @Nullable
1123
1124 /** Returns the path to the app bundle where the Dart code exists. */
1125 @NonNull
1127
1128 /** Returns the initial route that Flutter renders. */
1129 @Nullable
1131
1132 /**
1133 * Returns the {@link RenderMode} used by the {@link FlutterView} that displays the {@link
1134 * FlutterEngine}'s content.
1135 */
1136 @NonNull
1138
1139 /**
1140 * Returns the {@link TransparencyMode} used by the {@link FlutterView} that displays the {@link
1141 * FlutterEngine}'s content.
1142 */
1143 @NonNull
1145
1146 /**
1147 * Returns the {@link ExclusiveAppComponent<Activity>} that is associated with {@link
1148 * io.flutter.embedding.engine.FlutterEngine}.
1149 *
1150 * <p>In the scenario where multiple {@link FlutterActivity} or {@link FlutterFragment} share
1151 * the same {@link FlutterEngine}, to attach/re-attache a {@link FlutterActivity} or {@link
1152 * FlutterFragment} to the shared {@link FlutterEngine}, we MUST manually invoke {@link
1153 * ActivityControlSurface#attachToActivity(ExclusiveAppComponent, Lifecycle)}.
1154 *
1155 * <p>The {@link ExclusiveAppComponent} is exposed here so that subclasses of {@link
1156 * FlutterActivity} or {@link FlutterFragment} can access it.
1157 */
1159
1160 /**
1161 * Returns the {@link io.flutter.embedding.engine.FlutterEngine} that should be rendered to a
1162 * {@link FlutterView}.
1163 *
1164 * <p>If {@code null} is returned, a new {@link io.flutter.embedding.engine.FlutterEngine} will
1165 * be created automatically.
1166 */
1167 @Nullable
1169
1170 /**
1171 * Hook for the host to create/provide a {@link PlatformPlugin} if the associated Flutter
1172 * experience should control system chrome.
1173 */
1174 @Nullable
1176 @Nullable Activity activity, @NonNull FlutterEngine flutterEngine);
1177
1178 /**
1179 * Hook for the host to configure the {@link io.flutter.embedding.engine.FlutterEngine} as
1180 * desired.
1181 */
1182 void configureFlutterEngine(@NonNull FlutterEngine flutterEngine);
1183
1184 /**
1185 * Hook for the host to cleanup references that were established in {@link
1186 * #configureFlutterEngine(FlutterEngine)} before the host is destroyed or detached.
1187 */
1188 void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine);
1189
1190 /**
1191 * Returns true if the {@link io.flutter.embedding.engine.FlutterEngine}'s plugin system should
1192 * be connected to the host {@link android.app.Activity}, allowing plugins to interact with it.
1193 */
1195
1196 /**
1197 * Invoked by this delegate when the {@link FlutterSurfaceView} that renders the Flutter UI is
1198 * initially instantiated.
1199 *
1200 * <p>This method is only invoked if the {@link
1201 * io.flutter.embedding.android.FlutterView.RenderMode} is set to {@link
1202 * io.flutter.embedding.android.FlutterView.RenderMode#surface}. Otherwise, {@link
1203 * #onFlutterTextureViewCreated(FlutterTextureView)} is invoked.
1204 *
1205 * <p>This method is invoked before the given {@link FlutterSurfaceView} is attached to the
1206 * {@code View} hierarchy. Implementers should not attempt to climb the {@code View} hierarchy
1207 * or make assumptions about relationships with other {@code View}s.
1208 */
1209 void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView);
1210
1211 /**
1212 * Invoked by this delegate when the {@link FlutterTextureView} that renders the Flutter UI is
1213 * initially instantiated.
1214 *
1215 * <p>This method is only invoked if the {@link
1216 * io.flutter.embedding.android.FlutterView.RenderMode} is set to {@link
1217 * io.flutter.embedding.android.FlutterView.RenderMode#texture}. Otherwise, {@link
1218 * #onFlutterSurfaceViewCreated(FlutterSurfaceView)} is invoked.
1219 *
1220 * <p>This method is invoked before the given {@link FlutterTextureView} is attached to the
1221 * {@code View} hierarchy. Implementers should not attempt to climb the {@code View} hierarchy
1222 * or make assumptions about relationships with other {@code View}s.
1223 */
1224 void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView);
1225
1226 /** Invoked by this delegate when its {@link FlutterView} starts painting pixels. */
1228
1229 /** Invoked by this delegate when its {@link FlutterView} stops painting pixels. */
1231
1232 /**
1233 * Whether state restoration is enabled.
1234 *
1235 * <p>When this returns true, the instance state provided to {@code
1236 * onRestoreInstanceState(Bundle)} will be forwarded to the framework via the {@code
1237 * RestorationChannel} and during {@code onSaveInstanceState(Bundle)} the current framework
1238 * instance state obtained from {@code RestorationChannel} will be stored in the provided
1239 * bundle.
1240 *
1241 * <p>This defaults to true, unless a cached engine is used.
1242 */
1244
1245 /**
1246 * Refreshes Android's window system UI (AKA system chrome) to match Flutter's desired system
1247 * chrome style.
1248 *
1249 * <p>This is useful when using the splash screen API available in Android 12. {@code
1250 * SplashScreenView#remove} resets the system UI colors to the values set prior to the execution
1251 * of the Dart entrypoint. As a result, the values set from Dart are reverted by this API. To
1252 * workaround this issue, call this method after removing the splash screen with {@code
1253 * SplashScreenView#remove}.
1254 */
1256
1257 /**
1258 * Give the host application a chance to take control of the app lifecycle events to avoid
1259 * lifecycle crosstalk.
1260 *
1261 * <p>In the add-to-app scenario where multiple {@link FlutterActivity} shares the same {@link
1262 * FlutterEngine}, the application lifecycle state will have crosstalk causing the page to
1263 * freeze. For example, we open a new page called FlutterActivity#2 from the previous page
1264 * called FlutterActivity#1. The flow of app lifecycle states received by dart is as follows:
1265 *
1266 * <p>inactive (from FlutterActivity#1) -> resumed (from FlutterActivity#2) -> paused (from
1267 * FlutterActivity#1)
1268 *
1269 * <p>On the one hand, the {@code paused} state from FlutterActivity#1 will cause the
1270 * FlutterActivity#2 page to be stuck; On the other hand, these states are not expected from the
1271 * perspective of the entire application lifecycle. If the host application gets the control of
1272 * sending {@link AppLifecycleState}, It will be possible to correctly match the {@link
1273 * AppLifecycleState} with the application-level lifecycle.
1274 *
1275 * <p>Return {@code false} means the host application dispatches these app lifecycle events,
1276 * while return {@code true} means the engine dispatches these events.
1277 */
1279
1280 /**
1281 * Whether to automatically attach the {@link FlutterView} to the engine.
1282 *
1283 * <p>In the add-to-app scenario where multiple {@link FlutterView} share the same {@link
1284 * FlutterEngine}, the host application desires to determine the timing of attaching the {@link
1285 * FlutterView} to the engine, for example, during the {@code onResume} instead of the {@code
1286 * onCreateView}.
1287 *
1288 * <p>Defaults to {@code true}.
1289 */
1291 }
1292}
const char * options
static SkCanvas * trim(SkCanvas *canvas, SkScalar width, SkScalar height, const SkRect *content)
static void v(@NonNull String tag, @NonNull String message)
Definition Log.java:40
static void w(@NonNull String tag, @NonNull String message)
Definition Log.java:76
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState, int flutterViewId, boolean shouldDelayFirstAndroidViewDraw)
FlutterActivityAndFragmentDelegate(@NonNull Host host, @Nullable FlutterEngineGroup engineGroup)
void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
void removeOnFirstFrameRenderedListener(@NonNull FlutterUiDisplayListener listener)
void addOnFirstFrameRenderedListener(@NonNull FlutterUiDisplayListener listener)
void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine)
FlutterActivityAndFragmentDelegate createDelegate(FlutterActivityAndFragmentDelegate.Host host)
PlatformPlugin providePlatformPlugin( @Nullable Activity activity, @NonNull FlutterEngine flutterEngine)
void configureFlutterEngine(@NonNull FlutterEngine flutterEngine)
void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView)
void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView)
FlutterEngine provideFlutterEngine(@NonNull Context context)
void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service host
Definition switches.h:74
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
#define TAG()