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