Flutter Engine
The Flutter Engine
FlutterFragment.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 android.app.Activity;
8import android.content.ComponentCallbacks2;
9import android.content.Context;
10import android.content.Intent;
11import android.os.Bundle;
12import android.view.LayoutInflater;
13import android.view.View;
14import android.view.ViewGroup;
15import android.view.ViewTreeObserver.OnWindowFocusChangeListener;
16import androidx.activity.OnBackPressedCallback;
17import androidx.annotation.NonNull;
18import androidx.annotation.Nullable;
19import androidx.annotation.VisibleForTesting;
20import androidx.fragment.app.Fragment;
21import androidx.fragment.app.FragmentActivity;
22import androidx.lifecycle.Lifecycle;
23import io.flutter.Log;
24import io.flutter.embedding.engine.FlutterEngine;
25import io.flutter.embedding.engine.FlutterShellArgs;
26import io.flutter.embedding.engine.renderer.FlutterUiDisplayListener;
27import io.flutter.plugin.platform.PlatformPlugin;
28import java.util.ArrayList;
29import java.util.List;
30
31/**
32 * {@code Fragment} which displays a Flutter UI that takes up all available {@code Fragment} space.
33 *
34 * <p>Using a {@code FlutterFragment} requires forwarding a number of calls from an {@code Activity}
35 * to ensure that the internal Flutter app behaves as expected:
36 *
37 * <ol>
38 * <li>{@link #onPostResume()}
39 * <li>{@link #onBackPressed()}
40 * <li>{@link #onRequestPermissionsResult(int, String[], int[])}
41 * <li>{@link #onNewIntent(Intent)}
42 * <li>{@link #onUserLeaveHint()}
43 * </ol>
44 *
45 * {@link #onBackPressed()} does not need to be called through if the fragment is constructed by one
46 * of the builders with {@code shouldAutomaticallyHandleOnBackPressed(true)}.
47 *
48 * <p>Additionally, when starting an {@code Activity} for a result from this {@code Fragment}, be
49 * sure to invoke {@link Fragment#startActivityForResult(Intent, int)} rather than {@link
50 * android.app.Activity#startActivityForResult(Intent, int)}. If the {@code Activity} version of the
51 * method is invoked then this {@code Fragment} will never receive its {@link
52 * Fragment#onActivityResult(int, int, Intent)} callback.
53 *
54 * <p>If convenient, consider using a {@link FlutterActivity} instead of a {@code FlutterFragment}
55 * to avoid the work of forwarding calls.
56 *
57 * <p>{@code FlutterFragment} supports the use of an existing, cached {@link
58 * io.flutter.embedding.engine.FlutterEngine}. To use a cached {@link
59 * io.flutter.embedding.engine.FlutterEngine}, ensure that the {@link
60 * io.flutter.embedding.engine.FlutterEngine} is stored in {@link
61 * io.flutter.embedding.engine.FlutterEngineCache} and then use {@link #withCachedEngine(String)} to
62 * build a {@code FlutterFragment} with the cached {@link
63 * io.flutter.embedding.engine.FlutterEngine}'s ID.
64 *
65 * <p>It is generally recommended to use a cached {@link io.flutter.embedding.engine.FlutterEngine}
66 * to avoid a momentary delay when initializing a new {@link
67 * io.flutter.embedding.engine.FlutterEngine}. The two exceptions to using a cached {@link
68 * FlutterEngine} are:
69 *
70 * <ul>
71 * <li>When {@code FlutterFragment} is in the first {@code Activity} displayed by the app, because
72 * pre-warming a {@link io.flutter.embedding.engine.FlutterEngine} would have no impact in
73 * this situation.
74 * <li>When you are unsure when/if you will need to display a Flutter experience.
75 * </ul>
76 *
77 * <p>The following illustrates how to pre-warm and cache a {@link
78 * io.flutter.embedding.engine.FlutterEngine}:
79 *
80 * <pre>{@code
81 * // Create and pre-warm a FlutterEngine.
82 * FlutterEngineGroup group = new FlutterEngineGroup(context);
83 * FlutterEngine flutterEngine = group.createAndRunDefaultEngine(context);
84 * flutterEngine
85 * .getDartExecutor()
86 * .executeDartEntrypoint(DartEntrypoint.createDefault());
87 *
88 * // Cache the pre-warmed FlutterEngine in the FlutterEngineCache.
89 * FlutterEngineCache.getInstance().put("my_engine", flutterEngine);
90 * }</pre>
91 *
92 * <p>If Flutter is needed in a location that can only use a {@code View}, consider using a {@link
93 * io.flutter.embedding.android.FlutterView}. Using a {@link
94 * io.flutter.embedding.android.FlutterView} requires forwarding some calls from an {@code
95 * Activity}, as well as forwarding lifecycle calls from an {@code Activity} or a {@code Fragment}.
96 */
97public class FlutterFragment extends Fragment
99 ComponentCallbacks2,
100 FlutterActivityAndFragmentDelegate.DelegateFactory {
101 /**
102 * The ID of the {@code FlutterView} created by this activity.
103 *
104 * <p>This ID can be used to lookup {@code FlutterView} in the Android view hierarchy. For more,
105 * see {@link android.view.View#findViewById}.
106 */
107 public static final int FLUTTER_VIEW_ID = View.generateViewId();
108
109 private static final String TAG = "FlutterFragment";
110
111 /** The Dart entrypoint method name that is executed upon initialization. */
112 protected static final String ARG_DART_ENTRYPOINT = "dart_entrypoint";
113 /** The Dart entrypoint method's URI that is executed upon initialization. */
114 protected static final String ARG_DART_ENTRYPOINT_URI = "dart_entrypoint_uri";
115 /** The Dart entrypoint arguments that is executed upon initialization. */
116 protected static final String ARG_DART_ENTRYPOINT_ARGS = "dart_entrypoint_args";
117 /** Initial Flutter route that is rendered in a Navigator widget. */
118 protected static final String ARG_INITIAL_ROUTE = "initial_route";
119 /** Whether the activity delegate should handle the deeplinking request. */
120 protected static final String ARG_HANDLE_DEEPLINKING = "handle_deeplinking";
121 /** Path to Flutter's Dart code. */
122 protected static final String ARG_APP_BUNDLE_PATH = "app_bundle_path";
123 /** Whether to delay the Android drawing pass till after the Flutter UI has been displayed. */
124 protected static final String ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW =
125 "should_delay_first_android_view_draw";
126
127 /** Flutter shell arguments. */
128 protected static final String ARG_FLUTTER_INITIALIZATION_ARGS = "initialization_args";
129 /**
130 * {@link RenderMode} to be used for the {@link io.flutter.embedding.android.FlutterView} in this
131 * {@code FlutterFragment}
132 */
133 protected static final String ARG_FLUTTERVIEW_RENDER_MODE = "flutterview_render_mode";
134 /**
135 * {@link TransparencyMode} to be used for the {@link io.flutter.embedding.android.FlutterView} in
136 * this {@code FlutterFragment}
137 */
138 protected static final String ARG_FLUTTERVIEW_TRANSPARENCY_MODE = "flutterview_transparency_mode";
139 /** See {@link #shouldAttachEngineToActivity()}. */
140 protected static final String ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY =
141 "should_attach_engine_to_activity";
142 /**
143 * The ID of a {@link io.flutter.embedding.engine.FlutterEngine} cached in {@link
144 * io.flutter.embedding.engine.FlutterEngineCache} that will be used within the created {@code
145 * FlutterFragment}.
146 */
147 protected static final String ARG_CACHED_ENGINE_ID = "cached_engine_id";
148
149 protected static final String ARG_CACHED_ENGINE_GROUP_ID = "cached_engine_group_id";
150
151 /**
152 * True if the {@link io.flutter.embedding.engine.FlutterEngine} in the created {@code
153 * FlutterFragment} should be destroyed when the {@code FlutterFragment} is destroyed, false if
154 * the {@link io.flutter.embedding.engine.FlutterEngine} should outlive the {@code
155 * FlutterFragment}.
156 */
157 protected static final String ARG_DESTROY_ENGINE_WITH_FRAGMENT = "destroy_engine_with_fragment";
158 /**
159 * True if the framework state in the engine attached to this engine should be stored and restored
160 * when this fragment is created and destroyed.
161 */
162 protected static final String ARG_ENABLE_STATE_RESTORATION = "enable_state_restoration";
163 /**
164 * True if the fragment should receive {@link #onBackPressed()} events automatically, without
165 * requiring an explicit activity call through.
166 */
168 "should_automatically_handle_on_back_pressed";
169
170 private final OnWindowFocusChangeListener onWindowFocusChangeListener =
171 new OnWindowFocusChangeListener() {
172 @Override
173 public void onWindowFocusChanged(boolean hasFocus) {
174 if (stillAttachedForEvent("onWindowFocusChanged")) {
176 }
177 }
178 };
179
180 /**
181 * Creates a {@code FlutterFragment} with a default configuration.
182 *
183 * <p>{@code FlutterFragment}'s default configuration creates a new {@link
184 * io.flutter.embedding.engine.FlutterEngine} within the {@code FlutterFragment} and uses the
185 * following settings:
186 *
187 * <ul>
188 * <li>Dart entrypoint: "main"
189 * <li>Initial route: "/"
190 * <li>Render mode: surface
191 * <li>Transparency mode: transparent
192 * </ul>
193 *
194 * <p>To use a new {@link io.flutter.embedding.engine.FlutterEngine} with different settings, use
195 * {@link #withNewEngine()}.
196 *
197 * <p>To use a cached {@link io.flutter.embedding.engine.FlutterEngine} instead of creating a new
198 * one, use {@link #withCachedEngine(String)}.
199 */
200 @NonNull
202 return new NewEngineFragmentBuilder().build();
203 }
204
205 /**
206 * Returns a {@link NewEngineFragmentBuilder} to create a {@code FlutterFragment} with a new
207 * {@link io.flutter.embedding.engine.FlutterEngine} and a desired engine configuration.
208 */
209 @NonNull
211 return new NewEngineFragmentBuilder();
212 }
213
214 /**
215 * Builder that creates a new {@code FlutterFragment} with {@code arguments} that correspond to
216 * the values set on this {@code NewEngineFragmentBuilder}.
217 *
218 * <p>To create a {@code FlutterFragment} with default {@code arguments}, invoke {@link
219 * #createDefault()}.
220 *
221 * <p>Subclasses of {@code FlutterFragment} that do not introduce any new arguments can use this
222 * {@code NewEngineFragmentBuilder} to construct instances of the subclass without subclassing
223 * this {@code NewEngineFragmentBuilder}. {@code MyFlutterFragment f = new
224 * FlutterFragment.NewEngineFragmentBuilder(MyFlutterFragment.class) .someProperty(...)
225 * .someOtherProperty(...) .build<MyFlutterFragment>(); }
226 *
227 * <p>Subclasses of {@code FlutterFragment} that introduce new arguments should subclass this
228 * {@code NewEngineFragmentBuilder} to add the new properties:
229 *
230 * <ol>
231 * <li>Ensure the {@code FlutterFragment} subclass has a no-arg constructor.
232 * <li>Subclass this {@code NewEngineFragmentBuilder}.
233 * <li>Override the new {@code NewEngineFragmentBuilder}'s no-arg constructor and invoke the
234 * super constructor to set the {@code FlutterFragment} subclass: {@code public MyBuilder()
235 * { super(MyFlutterFragment.class); } }
236 * <li>Add appropriate property methods for the new properties.
237 * <li>Override {@link NewEngineFragmentBuilder#createArgs()}, call through to the super method,
238 * then add the new properties as arguments in the {@link Bundle}.
239 * </ol>
240 *
241 * Once a {@code NewEngineFragmentBuilder} subclass is defined, the {@code FlutterFragment}
242 * subclass can be instantiated as follows. {@code MyFlutterFragment f = new MyBuilder()
243 * .someExistingProperty(...) .someNewProperty(...) .build<MyFlutterFragment>(); }
244 */
245 public static class NewEngineFragmentBuilder {
246 private final Class<? extends FlutterFragment> fragmentClass;
247 private String dartEntrypoint = "main";
248 private String dartLibraryUri = null;
249 private List<String> dartEntrypointArgs;
250 private String initialRoute = "/";
251 private boolean handleDeeplinking = false;
252 private String appBundlePath = null;
253 private FlutterShellArgs shellArgs = null;
254 private RenderMode renderMode = RenderMode.surface;
255 private TransparencyMode transparencyMode = TransparencyMode.transparent;
256 private boolean shouldAttachEngineToActivity = true;
257 private boolean shouldAutomaticallyHandleOnBackPressed = false;
258 private boolean shouldDelayFirstAndroidViewDraw = false;
259
260 /**
261 * Constructs a {@code NewEngineFragmentBuilder} that is configured to construct an instance of
262 * {@code FlutterFragment}.
263 */
265 fragmentClass = FlutterFragment.class;
266 }
267
268 /**
269 * Constructs a {@code NewEngineFragmentBuilder} that is configured to construct an instance of
270 * {@code subclass}, which extends {@code FlutterFragment}.
271 */
272 public NewEngineFragmentBuilder(@NonNull Class<? extends FlutterFragment> subclass) {
273 fragmentClass = subclass;
274 }
275
276 /** The name of the initial Dart method to invoke, defaults to "main". */
277 @NonNull
278 public NewEngineFragmentBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
279 this.dartEntrypoint = dartEntrypoint;
280 return this;
281 }
282
283 @NonNull
284 public NewEngineFragmentBuilder dartLibraryUri(@NonNull String dartLibraryUri) {
285 this.dartLibraryUri = dartLibraryUri;
286 return this;
287 }
288
289 /** Arguments passed as a list of string to Dart's entrypoint function. */
290 @NonNull
291 public NewEngineFragmentBuilder dartEntrypointArgs(@NonNull List<String> dartEntrypointArgs) {
292 this.dartEntrypointArgs = dartEntrypointArgs;
293 return this;
294 }
295
296 /**
297 * The initial route that a Flutter app will render in this {@link FlutterFragment}, defaults to
298 * "/".
299 */
300 @NonNull
301 public NewEngineFragmentBuilder initialRoute(@NonNull String initialRoute) {
302 this.initialRoute = initialRoute;
303 return this;
304 }
305
306 /**
307 * Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
308 * getInitialRoute} returns null.
309 */
310 @NonNull
311 public NewEngineFragmentBuilder handleDeeplinking(@NonNull Boolean handleDeeplinking) {
312 this.handleDeeplinking = handleDeeplinking;
313 return this;
314 }
315
316 /**
317 * The path to the app bundle which contains the Dart app to execute. Null when unspecified,
318 * which defaults to {@link
319 * io.flutter.embedding.engine.loader.FlutterLoader#findAppBundlePath()}
320 */
321 @NonNull
322 public NewEngineFragmentBuilder appBundlePath(@NonNull String appBundlePath) {
323 this.appBundlePath = appBundlePath;
324 return this;
325 }
326
327 /** Any special configuration arguments for the Flutter engine */
328 @NonNull
330 this.shellArgs = shellArgs;
331 return this;
332 }
333
334 /**
335 * Render Flutter either as a {@link RenderMode#surface} or a {@link RenderMode#texture}. You
336 * should use {@code surface} unless you have a specific reason to use {@code texture}. {@code
337 * texture} comes with a significant performance impact, but {@code texture} can be displayed
338 * beneath other Android {@code View}s and animated, whereas {@code surface} cannot.
339 */
340 @NonNull
341 public NewEngineFragmentBuilder renderMode(@NonNull RenderMode renderMode) {
342 this.renderMode = renderMode;
343 return this;
344 }
345
346 /**
347 * Support a {@link TransparencyMode#transparent} background within {@link
348 * io.flutter.embedding.android.FlutterView}, or force an {@link TransparencyMode#opaque}
349 * background.
350 *
351 * <p>See {@link TransparencyMode} for implications of this selection.
352 */
353 @NonNull
355 this.transparencyMode = transparencyMode;
356 return this;
357 }
358
359 /**
360 * Whether or not this {@code FlutterFragment} should automatically attach its {@code Activity}
361 * as a control surface for its {@link io.flutter.embedding.engine.FlutterEngine}.
362 *
363 * <p>Control surfaces are used to provide Android resources and lifecycle events to plugins
364 * that are attached to the {@link io.flutter.embedding.engine.FlutterEngine}. If {@code
365 * shouldAttachEngineToActivity} is true then this {@code FlutterFragment} will connect its
366 * {@link io.flutter.embedding.engine.FlutterEngine} to the surrounding {@code Activity}, along
367 * with any plugins that are registered with that {@link FlutterEngine}. This allows plugins to
368 * access the {@code Activity}, as well as receive {@code Activity}-specific calls, e.g., {@link
369 * android.app.Activity#onNewIntent(Intent)}. If {@code shouldAttachEngineToActivity} is false,
370 * then this {@code FlutterFragment} will not automatically manage the connection between its
371 * {@link io.flutter.embedding.engine.FlutterEngine} and the surrounding {@code Activity}. The
372 * {@code Activity} will need to be manually connected to this {@code FlutterFragment}'s {@link
373 * io.flutter.embedding.engine.FlutterEngine} by the app developer. See {@link
374 * FlutterEngine#getActivityControlSurface()}.
375 *
376 * <p>One reason that a developer might choose to manually manage the relationship between the
377 * {@code Activity} and {@link io.flutter.embedding.engine.FlutterEngine} is if the developer
378 * wants to move the {@link FlutterEngine} somewhere else. For example, a developer might want
379 * the {@link io.flutter.embedding.engine.FlutterEngine} to outlive the surrounding {@code
380 * Activity} so that it can be used later in a different {@code Activity}. To accomplish this,
381 * the {@link io.flutter.embedding.engine.FlutterEngine} will need to be disconnected from the
382 * surrounding {@code Activity} at an unusual time, preventing this {@code FlutterFragment} from
383 * correctly managing the relationship between the {@link
384 * io.flutter.embedding.engine.FlutterEngine} and the surrounding {@code Activity}.
385 *
386 * <p>Another reason that a developer might choose to manually manage the relationship between
387 * the {@code Activity} and {@link io.flutter.embedding.engine.FlutterEngine} is if the
388 * developer wants to prevent, or explicitly control when the {@link
389 * io.flutter.embedding.engine.FlutterEngine}'s plugins have access to the surrounding {@code
390 * Activity}. For example, imagine that this {@code FlutterFragment} only takes up part of the
391 * screen and the app developer wants to ensure that none of the Flutter plugins are able to
392 * manipulate the surrounding {@code Activity}. In this case, the developer would not want the
393 * {@link io.flutter.embedding.engine.FlutterEngine} to have access to the {@code Activity},
394 * which can be accomplished by setting {@code shouldAttachEngineToActivity} to {@code false}.
395 */
396 @NonNull
398 boolean shouldAttachEngineToActivity) {
399 this.shouldAttachEngineToActivity = shouldAttachEngineToActivity;
400 return this;
401 }
402
403 /**
404 * Whether or not this {@code FlutterFragment} should automatically receive {@link
405 * #onBackPressed()} events, rather than requiring an explicit activity call through. Disabled
406 * by default.
407 *
408 * <p>When enabled, the activity will automatically dispatch back-press events to the fragment's
409 * {@link OnBackPressedCallback}, instead of requiring the activity to manually call {@link
410 * #onBackPressed()} in client code. If enabled, do <b>not</b> invoke {@link #onBackPressed()}
411 * manually.
412 *
413 * <p>This behavior relies on the implementation of {@link #popSystemNavigator()}. It's not
414 * recommended to override that method when enabling this attribute, but if you do, you should
415 * always fall back to calling {@code super.popSystemNavigator()} when not relying on custom
416 * behavior.
417 */
418 @NonNull
420 boolean shouldAutomaticallyHandleOnBackPressed) {
421 this.shouldAutomaticallyHandleOnBackPressed = shouldAutomaticallyHandleOnBackPressed;
422 return this;
423 }
424
425 /**
426 * Whether to delay the Android drawing pass till after the Flutter UI has been displayed.
427 *
428 * <p>See {#link FlutterActivityAndFragmentDelegate#onCreateView} for more details.
429 */
430 @NonNull
432 boolean shouldDelayFirstAndroidViewDraw) {
433 this.shouldDelayFirstAndroidViewDraw = shouldDelayFirstAndroidViewDraw;
434 return this;
435 }
436
437 /**
438 * Creates a {@link Bundle} of arguments that are assigned to the new {@code FlutterFragment}.
439 *
440 * <p>Subclasses should override this method to add new properties to the {@link Bundle}.
441 * Subclasses must call through to the super method to collect all existing property values.
442 */
443 @NonNull
444 protected Bundle createArgs() {
445 Bundle args = new Bundle();
446 args.putString(ARG_INITIAL_ROUTE, initialRoute);
447 args.putBoolean(ARG_HANDLE_DEEPLINKING, handleDeeplinking);
448 args.putString(ARG_APP_BUNDLE_PATH, appBundlePath);
449 args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);
450 args.putString(ARG_DART_ENTRYPOINT_URI, dartLibraryUri);
451 args.putStringArrayList(
453 dartEntrypointArgs != null ? new ArrayList(dartEntrypointArgs) : null);
454 // TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of
455 // conflating.
456 if (null != shellArgs) {
457 args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, shellArgs.toArray());
458 }
459 args.putString(
461 renderMode != null ? renderMode.name() : RenderMode.surface.name());
462 args.putString(
464 transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name());
465 args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity);
466 args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
467 args.putBoolean(
468 ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, shouldAutomaticallyHandleOnBackPressed);
469 args.putBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW, shouldDelayFirstAndroidViewDraw);
470 return args;
471 }
472
473 /**
474 * Constructs a new {@code FlutterFragment} (or a subclass) that is configured based on
475 * properties set on this {@code Builder}.
476 */
477 @NonNull
478 public <T extends FlutterFragment> T build() {
479 try {
480 @SuppressWarnings("unchecked")
481 T frag = (T) fragmentClass.getDeclaredConstructor().newInstance();
482 if (frag == null) {
483 throw new RuntimeException(
484 "The FlutterFragment subclass sent in the constructor ("
485 + fragmentClass.getCanonicalName()
486 + ") does not match the expected return type.");
487 }
488
489 Bundle args = createArgs();
490 frag.setArguments(args);
491
492 return frag;
493 } catch (Exception e) {
494 throw new RuntimeException(
495 "Could not instantiate FlutterFragment subclass (" + fragmentClass.getName() + ")", e);
496 }
497 }
498 }
499
500 /**
501 * Returns a {@link CachedEngineFragmentBuilder} to create a {@code FlutterFragment} with a cached
502 * {@link io.flutter.embedding.engine.FlutterEngine} in {@link
503 * io.flutter.embedding.engine.FlutterEngineCache}.
504 *
505 * <p>An {@code IllegalStateException} will be thrown during the lifecycle of the {@code
506 * FlutterFragment} if a cached {@link io.flutter.embedding.engine.FlutterEngine} is requested but
507 * does not exist in the cache.
508 *
509 * <p>To create a {@code FlutterFragment} that uses a new {@link
510 * io.flutter.embedding.engine.FlutterEngine}, use {@link #createDefault()} or {@link
511 * #withNewEngine()}.
512 */
513 @NonNull
514 public static CachedEngineFragmentBuilder withCachedEngine(@NonNull String engineId) {
515 return new CachedEngineFragmentBuilder(engineId);
516 }
517
518 /**
519 * Builder that creates a new {@code FlutterFragment} that uses a cached {@link
520 * io.flutter.embedding.engine.FlutterEngine} with {@code arguments} that correspond to the values
521 * set on this {@code Builder}.
522 *
523 * <p>Subclasses of {@code FlutterFragment} that do not introduce any new arguments can use this
524 * {@code Builder} to construct instances of the subclass without subclassing this {@code
525 * Builder}. {@code MyFlutterFragment f = new
526 * FlutterFragment.CachedEngineFragmentBuilder(MyFlutterFragment.class) .someProperty(...)
527 * .someOtherProperty(...) .build<MyFlutterFragment>(); }
528 *
529 * <p>Subclasses of {@code FlutterFragment} that introduce new arguments should subclass this
530 * {@code CachedEngineFragmentBuilder} to add the new properties:
531 *
532 * <ol>
533 * <li>Ensure the {@code FlutterFragment} subclass has a no-arg constructor.
534 * <li>Subclass this {@code CachedEngineFragmentBuilder}.
535 * <li>Override the new {@code CachedEngineFragmentBuilder}'s no-arg constructor and invoke the
536 * super constructor to set the {@code FlutterFragment} subclass: {@code public MyBuilder()
537 * { super(MyFlutterFragment.class); } }
538 * <li>Add appropriate property methods for the new properties.
539 * <li>Override {@link CachedEngineFragmentBuilder#createArgs()}, call through to the super
540 * method, then add the new properties as arguments in the {@link Bundle}.
541 * </ol>
542 *
543 * Once a {@code CachedEngineFragmentBuilder} subclass is defined, the {@code FlutterFragment}
544 * subclass can be instantiated as follows. {@code MyFlutterFragment f = new MyBuilder()
545 * .someExistingProperty(...) .someNewProperty(...) .build<MyFlutterFragment>(); }
546 */
547 public static class CachedEngineFragmentBuilder {
548 private final Class<? extends FlutterFragment> fragmentClass;
549 private final String engineId;
550 private boolean destroyEngineWithFragment = false;
551 private boolean handleDeeplinking = false;
552 private RenderMode renderMode = RenderMode.surface;
553 private TransparencyMode transparencyMode = TransparencyMode.transparent;
554 private boolean shouldAttachEngineToActivity = true;
555 private boolean shouldAutomaticallyHandleOnBackPressed = false;
556 private boolean shouldDelayFirstAndroidViewDraw = false;
557
558 private CachedEngineFragmentBuilder(@NonNull String engineId) {
559 this(FlutterFragment.class, engineId);
560 }
561
563 @NonNull Class<? extends FlutterFragment> subclass, @NonNull String engineId) {
564 this.fragmentClass = subclass;
565 this.engineId = engineId;
566 }
567
568 /**
569 * Pass {@code true} to destroy the cached {@link io.flutter.embedding.engine.FlutterEngine}
570 * when this {@code FlutterFragment} is destroyed, or {@code false} for the cached {@link
571 * io.flutter.embedding.engine.FlutterEngine} to outlive this {@code FlutterFragment}.
572 */
573 @NonNull
575 boolean destroyEngineWithFragment) {
576 this.destroyEngineWithFragment = destroyEngineWithFragment;
577 return this;
578 }
579
580 /**
581 * Render Flutter either as a {@link RenderMode#surface} or a {@link RenderMode#texture}. You
582 * should use {@code surface} unless you have a specific reason to use {@code texture}. {@code
583 * texture} comes with a significant performance impact, but {@code texture} can be displayed
584 * beneath other Android {@code View}s and animated, whereas {@code surface} cannot.
585 */
586 @NonNull
588 this.renderMode = renderMode;
589 return this;
590 }
591
592 /**
593 * Support a {@link TransparencyMode#transparent} background within {@link
594 * io.flutter.embedding.android.FlutterView}, or force an {@link TransparencyMode#opaque}
595 * background.
596 *
597 * <p>See {@link TransparencyMode} for implications of this selection.
598 */
599 @NonNull
601 @NonNull TransparencyMode transparencyMode) {
602 this.transparencyMode = transparencyMode;
603 return this;
604 }
605
606 /**
607 * Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
608 * getInitialRoute} returns null.
609 */
610 @NonNull
611 public CachedEngineFragmentBuilder handleDeeplinking(@NonNull Boolean handleDeeplinking) {
612 this.handleDeeplinking = handleDeeplinking;
613 return this;
614 }
615
616 /**
617 * Whether or not this {@code FlutterFragment} should automatically attach its {@code Activity}
618 * as a control surface for its {@link io.flutter.embedding.engine.FlutterEngine}.
619 *
620 * <p>Control surfaces are used to provide Android resources and lifecycle events to plugins
621 * that are attached to the {@link io.flutter.embedding.engine.FlutterEngine}. If {@code
622 * shouldAttachEngineToActivity} is true then this {@code FlutterFragment} will connect its
623 * {@link io.flutter.embedding.engine.FlutterEngine} to the surrounding {@code Activity}, along
624 * with any plugins that are registered with that {@link FlutterEngine}. This allows plugins to
625 * access the {@code Activity}, as well as receive {@code Activity}-specific calls, e.g., {@link
626 * android.app.Activity#onNewIntent(Intent)}. If {@code shouldAttachEngineToActivity} is false,
627 * then this {@code FlutterFragment} will not automatically manage the connection between its
628 * {@link io.flutter.embedding.engine.FlutterEngine} and the surrounding {@code Activity}. The
629 * {@code Activity} will need to be manually connected to this {@code FlutterFragment}'s {@link
630 * io.flutter.embedding.engine.FlutterEngine} by the app developer. See {@link
631 * FlutterEngine#getActivityControlSurface()}.
632 *
633 * <p>One reason that a developer might choose to manually manage the relationship between the
634 * {@code Activity} and {@link io.flutter.embedding.engine.FlutterEngine} is if the developer
635 * wants to move the {@link FlutterEngine} somewhere else. For example, a developer might want
636 * the {@link io.flutter.embedding.engine.FlutterEngine} to outlive the surrounding {@code
637 * Activity} so that it can be used later in a different {@code Activity}. To accomplish this,
638 * the {@link io.flutter.embedding.engine.FlutterEngine} will need to be disconnected from the
639 * surrounding {@code Activity} at an unusual time, preventing this {@code FlutterFragment} from
640 * correctly managing the relationship between the {@link
641 * io.flutter.embedding.engine.FlutterEngine} and the surrounding {@code Activity}.
642 *
643 * <p>Another reason that a developer might choose to manually manage the relationship between
644 * the {@code Activity} and {@link io.flutter.embedding.engine.FlutterEngine} is if the
645 * developer wants to prevent, or explicitly control when the {@link
646 * io.flutter.embedding.engine.FlutterEngine}'s plugins have access to the surrounding {@code
647 * Activity}. For example, imagine that this {@code FlutterFragment} only takes up part of the
648 * screen and the app developer wants to ensure that none of the Flutter plugins are able to
649 * manipulate the surrounding {@code Activity}. In this case, the developer would not want the
650 * {@link io.flutter.embedding.engine.FlutterEngine} to have access to the {@code Activity},
651 * which can be accomplished by setting {@code shouldAttachEngineToActivity} to {@code false}.
652 */
653 @NonNull
655 boolean shouldAttachEngineToActivity) {
656 this.shouldAttachEngineToActivity = shouldAttachEngineToActivity;
657 return this;
658 }
659
660 /**
661 * Whether or not this {@code FlutterFragment} should automatically receive {@link
662 * #onBackPressed()} events, rather than requiring an explicit activity call through. Disabled
663 * by default.
664 *
665 * <p>When enabled, the activity will automatically dispatch back-press events to the fragment's
666 * {@link OnBackPressedCallback}, instead of requiring the activity to manually call {@link
667 * #onBackPressed()} in client code. If enabled, do <b>not</b> invoke {@link #onBackPressed()}
668 * manually.
669 *
670 * <p>Enabling this behavior relies on explicit behavior in {@link #popSystemNavigator()}. It's
671 * not recommended to override that method when enabling this attribute, but if you do, you
672 * should always fall back to calling {@code super.popSystemNavigator()} when not relying on
673 * custom behavior.
674 */
675 @NonNull
677 boolean shouldAutomaticallyHandleOnBackPressed) {
678 this.shouldAutomaticallyHandleOnBackPressed = shouldAutomaticallyHandleOnBackPressed;
679 return this;
680 }
681
682 /**
683 * Whether to delay the Android drawing pass till after the Flutter UI has been displayed.
684 *
685 * <p>See {#link FlutterActivityAndFragmentDelegate#onCreateView} for more details.
686 */
687 @NonNull
689 @NonNull boolean shouldDelayFirstAndroidViewDraw) {
690 this.shouldDelayFirstAndroidViewDraw = shouldDelayFirstAndroidViewDraw;
691 return this;
692 }
693
694 /**
695 * Creates a {@link Bundle} of arguments that are assigned to the new {@code FlutterFragment}.
696 *
697 * <p>Subclasses should override this method to add new properties to the {@link Bundle}.
698 * Subclasses must call through to the super method to collect all existing property values.
699 */
700 @NonNull
701 protected Bundle createArgs() {
702 Bundle args = new Bundle();
703 args.putString(ARG_CACHED_ENGINE_ID, engineId);
704 args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, destroyEngineWithFragment);
705 args.putBoolean(ARG_HANDLE_DEEPLINKING, handleDeeplinking);
706 args.putString(
708 renderMode != null ? renderMode.name() : RenderMode.surface.name());
709 args.putString(
711 transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name());
712 args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity);
713 args.putBoolean(
714 ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, shouldAutomaticallyHandleOnBackPressed);
715 args.putBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW, shouldDelayFirstAndroidViewDraw);
716 return args;
717 }
718
719 /**
720 * Constructs a new {@code FlutterFragment} (or a subclass) that is configured based on
721 * properties set on this {@code CachedEngineFragmentBuilder}.
722 */
723 @NonNull
724 public <T extends FlutterFragment> T build() {
725 try {
726 @SuppressWarnings("unchecked")
727 T frag = (T) fragmentClass.getDeclaredConstructor().newInstance();
728 if (frag == null) {
729 throw new RuntimeException(
730 "The FlutterFragment subclass sent in the constructor ("
731 + fragmentClass.getCanonicalName()
732 + ") does not match the expected return type.");
733 }
734
735 Bundle args = createArgs();
736 frag.setArguments(args);
737
738 return frag;
739 } catch (Exception e) {
740 throw new RuntimeException(
741 "Could not instantiate FlutterFragment subclass (" + fragmentClass.getName() + ")", e);
742 }
743 }
744 }
745
746 /**
747 * Returns a {@link NewEngineInGroupFragmentBuilder} to create a {@code FlutterFragment} with a
748 * cached {@link io.flutter.embedding.engine.FlutterEngineGroup} in {@link
749 * io.flutter.embedding.engine.FlutterEngineGroupCache}.
750 *
751 * <p>An {@code IllegalStateException} will be thrown during the lifecycle of the {@code
752 * FlutterFragment} if a cached {@link io.flutter.embedding.engine.FlutterEngineGroup} is
753 * requested but does not exist in the {@link
754 * io.flutter.embedding.engine.FlutterEngineGroupCache}.
755 */
756 @NonNull
758 @NonNull String engineGroupId) {
759 return new NewEngineInGroupFragmentBuilder(engineGroupId);
760 }
761
762 /**
763 * Builder that creates a new {@code FlutterFragment} that uses a cached {@link
764 * io.flutter.embedding.engine.FlutterEngineGroup} to create a new {@link
765 * io.flutter.embedding.engine.FlutterEngine} with {@code arguments} that correspond to the values
766 * set on this {@code Builder}.
767 *
768 * <p>Subclasses of {@code FlutterFragment} that do not introduce any new arguments can use this
769 * {@code Builder} to construct instances of the subclass without subclassing this {@code
770 * Builder}. {@code MyFlutterFragment f = new
771 * FlutterFragment.NewEngineInGroupFragmentBuilder(MyFlutterFragment.class, engineGroupId)
772 * .someProperty(...) .someOtherProperty(...) .build<MyFlutterFragment>(); }
773 *
774 * <p>Subclasses of {@code FlutterFragment} that introduce new arguments should subclass this
775 * {@code NewEngineInGroupFragmentBuilder} to add the new properties:
776 *
777 * <ol>
778 * <li>Ensure the {@code FlutterFragment} subclass has a no-arg constructor.
779 * <li>Subclass this {@code NewEngineInGroupFragmentBuilder}.
780 * <li>Override the new {@code NewEngineInGroupFragmentBuilder}'s no-arg constructor and invoke
781 * the super constructor to set the {@code FlutterFragment} subclass: {@code public
782 * MyBuilder() { super(MyFlutterFragment.class); } }
783 * <li>Add appropriate property methods for the new properties.
784 * <li>Override {@link NewEngineInGroupFragmentBuilder#createArgs()}, call through to the super
785 * method, then add the new properties as arguments in the {@link Bundle}.
786 * </ol>
787 *
788 * Once a {@code NewEngineInGroupFragmentBuilder} subclass is defined, the {@code FlutterFragment}
789 * subclass can be instantiated as follows. {@code MyFlutterFragment f = new MyBuilder()
790 * .someExistingProperty(...) .someNewProperty(...) .build<MyFlutterFragment>(); }
791 */
793 private final Class<? extends FlutterFragment> fragmentClass;
794 private final String cachedEngineGroupId;
795 private @NonNull String dartEntrypoint = "main";
796 private @NonNull String initialRoute = "/";
797 private @NonNull boolean handleDeeplinking = false;
798 private @NonNull RenderMode renderMode = RenderMode.surface;
799 private @NonNull TransparencyMode transparencyMode = TransparencyMode.transparent;
800 private boolean shouldAttachEngineToActivity = true;
801 private boolean shouldAutomaticallyHandleOnBackPressed = false;
802 private boolean shouldDelayFirstAndroidViewDraw = false;
803
804 public NewEngineInGroupFragmentBuilder(@NonNull String engineGroupId) {
805 this(FlutterFragment.class, engineGroupId);
806 }
807
809 @NonNull Class<? extends FlutterFragment> fragmentClass, @NonNull String engineGroupId) {
810 this.fragmentClass = fragmentClass;
811 this.cachedEngineGroupId = engineGroupId;
812 }
813
814 /** The name of the initial Dart method to invoke, defaults to "main". */
815 @NonNull
816 public NewEngineInGroupFragmentBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
817 this.dartEntrypoint = dartEntrypoint;
818 return this;
819 }
820
821 /**
822 * The initial route that a Flutter app will render in this {@link FlutterFragment}, defaults to
823 * "/".
824 */
825 @NonNull
826 public NewEngineInGroupFragmentBuilder initialRoute(@NonNull String initialRoute) {
827 this.initialRoute = initialRoute;
828 return this;
829 }
830
831 /**
832 * Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
833 * getInitialRoute} returns null.
834 */
835 @NonNull
836 public NewEngineInGroupFragmentBuilder handleDeeplinking(@NonNull boolean handleDeeplinking) {
837 this.handleDeeplinking = handleDeeplinking;
838 return this;
839 }
840
841 /**
842 * Render Flutter either as a {@link RenderMode#surface} or a {@link RenderMode#texture}. You
843 * should use {@code surface} unless you have a specific reason to use {@code texture}. {@code
844 * texture} comes with a significant performance impact, but {@code texture} can be displayed
845 * beneath other Android {@code View}s and animated, whereas {@code surface} cannot.
846 */
847 @NonNull
849 this.renderMode = renderMode;
850 return this;
851 }
852
853 /**
854 * Support a {@link TransparencyMode#transparent} background within {@link
855 * io.flutter.embedding.android.FlutterView}, or force an {@link TransparencyMode#opaque}
856 * background.
857 *
858 * <p>See {@link TransparencyMode} for implications of this selection.
859 */
860 @NonNull
862 @NonNull TransparencyMode transparencyMode) {
863 this.transparencyMode = transparencyMode;
864 return this;
865 }
866
867 /**
868 * Whether or not this {@code FlutterFragment} should automatically attach its {@code Activity}
869 * as a control surface for its {@link io.flutter.embedding.engine.FlutterEngine}.
870 *
871 * <p>Control surfaces are used to provide Android resources and lifecycle events to plugins
872 * that are attached to the {@link io.flutter.embedding.engine.FlutterEngine}. If {@code
873 * shouldAttachEngineToActivity} is true then this {@code FlutterFragment} will connect its
874 * {@link io.flutter.embedding.engine.FlutterEngine} to the surrounding {@code Activity}, along
875 * with any plugins that are registered with that {@link FlutterEngine}. This allows plugins to
876 * access the {@code Activity}, as well as receive {@code Activity}-specific calls, e.g., {@link
877 * android.app.Activity#onNewIntent(Intent)}. If {@code shouldAttachEngineToActivity} is false,
878 * then this {@code FlutterFragment} will not automatically manage the connection between its
879 * {@link io.flutter.embedding.engine.FlutterEngine} and the surrounding {@code Activity}. The
880 * {@code Activity} will need to be manually connected to this {@code FlutterFragment}'s {@link
881 * io.flutter.embedding.engine.FlutterEngine} by the app developer. See {@link
882 * FlutterEngine#getActivityControlSurface()}.
883 *
884 * <p>One reason that a developer might choose to manually manage the relationship between the
885 * {@code Activity} and {@link io.flutter.embedding.engine.FlutterEngine} is if the developer
886 * wants to move the {@link FlutterEngine} somewhere else. For example, a developer might want
887 * the {@link io.flutter.embedding.engine.FlutterEngine} to outlive the surrounding {@code
888 * Activity} so that it can be used later in a different {@code Activity}. To accomplish this,
889 * the {@link io.flutter.embedding.engine.FlutterEngine} will need to be disconnected from the
890 * surrounding {@code Activity} at an unusual time, preventing this {@code FlutterFragment} from
891 * correctly managing the relationship between the {@link
892 * io.flutter.embedding.engine.FlutterEngine} and the surrounding {@code Activity}.
893 *
894 * <p>Another reason that a developer might choose to manually manage the relationship between
895 * the {@code Activity} and {@link io.flutter.embedding.engine.FlutterEngine} is if the
896 * developer wants to prevent, or explicitly control when the {@link
897 * io.flutter.embedding.engine.FlutterEngine}'s plugins have access to the surrounding {@code
898 * Activity}. For example, imagine that this {@code FlutterFragment} only takes up part of the
899 * screen and the app developer wants to ensure that none of the Flutter plugins are able to
900 * manipulate the surrounding {@code Activity}. In this case, the developer would not want the
901 * {@link io.flutter.embedding.engine.FlutterEngine} to have access to the {@code Activity},
902 * which can be accomplished by setting {@code shouldAttachEngineToActivity} to {@code false}.
903 */
904 @NonNull
906 boolean shouldAttachEngineToActivity) {
907 this.shouldAttachEngineToActivity = shouldAttachEngineToActivity;
908 return this;
909 }
910
911 /**
912 * Whether or not this {@code FlutterFragment} should automatically receive {@link
913 * #onBackPressed()} events, rather than requiring an explicit activity call through. Disabled
914 * by default.
915 *
916 * <p>When enabled, the activity will automatically dispatch back-press events to the fragment's
917 * {@link OnBackPressedCallback}, instead of requiring the activity to manually call {@link
918 * #onBackPressed()} in client code. If enabled, do <b>not</b> invoke {@link #onBackPressed()}
919 * manually.
920 *
921 * <p>This behavior relies on the implementation of {@link #popSystemNavigator()}. It's not
922 * recommended to override that method when enabling this attribute, but if you do, you should
923 * always fall back to calling {@code super.popSystemNavigator()} when not relying on custom
924 * behavior.
925 */
926 @NonNull
928 boolean shouldAutomaticallyHandleOnBackPressed) {
929 this.shouldAutomaticallyHandleOnBackPressed = shouldAutomaticallyHandleOnBackPressed;
930 return this;
931 }
932
933 /**
934 * Whether to delay the Android drawing pass till after the Flutter UI has been displayed.
935 *
936 * <p>See {#link FlutterActivityAndFragmentDelegate#onCreateView} for more details.
937 */
938 @NonNull
940 @NonNull boolean shouldDelayFirstAndroidViewDraw) {
941 this.shouldDelayFirstAndroidViewDraw = shouldDelayFirstAndroidViewDraw;
942 return this;
943 }
944
945 /**
946 * Creates a {@link Bundle} of arguments that are assigned to the new {@code FlutterFragment}.
947 *
948 * <p>Subclasses should override this method to add new properties to the {@link Bundle}.
949 * Subclasses must call through to the super method to collect all existing property values.
950 */
951 @NonNull
952 protected Bundle createArgs() {
953 Bundle args = new Bundle();
954 args.putString(ARG_CACHED_ENGINE_GROUP_ID, cachedEngineGroupId);
955 args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);
956 args.putString(ARG_INITIAL_ROUTE, initialRoute);
957 args.putBoolean(ARG_HANDLE_DEEPLINKING, handleDeeplinking);
958 args.putString(
960 renderMode != null ? renderMode.name() : RenderMode.surface.name());
961 args.putString(
963 transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name());
964 args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity);
965 args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
966 args.putBoolean(
967 ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, shouldAutomaticallyHandleOnBackPressed);
968 args.putBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW, shouldDelayFirstAndroidViewDraw);
969 return args;
970 }
971
972 /**
973 * Constructs a new {@code FlutterFragment} (or a subclass) that is configured based on
974 * properties set on this {@code Builder}.
975 */
976 @NonNull
977 public <T extends FlutterFragment> T build() {
978 try {
979 @SuppressWarnings("unchecked")
980 T frag = (T) fragmentClass.getDeclaredConstructor().newInstance();
981 if (frag == null) {
982 throw new RuntimeException(
983 "The FlutterFragment subclass sent in the constructor ("
984 + fragmentClass.getCanonicalName()
985 + ") does not match the expected return type.");
986 }
987
988 Bundle args = createArgs();
989 frag.setArguments(args);
990
991 return frag;
992 } catch (Exception e) {
993 throw new RuntimeException(
994 "Could not instantiate FlutterFragment subclass (" + fragmentClass.getName() + ")", e);
995 }
996 }
997 }
998
999 // Delegate that runs all lifecycle and OS hook logic that is common between
1000 // FlutterActivity and FlutterFragment. See the FlutterActivityAndFragmentDelegate
1001 // implementation for details about why it exists.
1002 @VisibleForTesting @Nullable /* package */ FlutterActivityAndFragmentDelegate delegate;
1003
1004 @NonNull private FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory = this;
1005
1006 /** Default delegate factory that creates a simple FlutterActivityAndFragmentDelegate instance. */
1010 }
1011
1012 private final OnBackPressedCallback onBackPressedCallback =
1013 new OnBackPressedCallback(true) {
1014 @Override
1015 public void handleOnBackPressed() {
1016 onBackPressed();
1017 }
1018 };
1019
1021 // Ensure that we at least have an empty Bundle of arguments so that we don't
1022 // need to continually check for null arguments before grabbing one.
1023 setArguments(new Bundle());
1024 }
1025
1026 /**
1027 * This method exists so that JVM tests can ensure that a delegate exists without putting this
1028 * Fragment through any lifecycle events, because JVM tests cannot handle executing any lifecycle
1029 * methods, at the time of writing this.
1030 *
1031 * <p>The testing infrastructure should be upgraded to make FlutterFragment tests easy to write
1032 * while exercising real lifecycle methods. At such a time, this method should be removed.
1033 */
1034 // TODO(mattcarroll): remove this when tests allow for it
1035 // (https://github.com/flutter/flutter/issues/43798)
1036 @VisibleForTesting
1037 /* package */ void setDelegateFactory(
1038 @NonNull FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory) {
1039 this.delegateFactory = delegateFactory;
1040 delegate = delegateFactory.createDelegate(this);
1041 }
1042
1043 /**
1044 * Returns the Android App Component exclusively attached to {@link
1045 * io.flutter.embedding.engine.FlutterEngine}.
1046 */
1047 @Override
1049 return delegate;
1050 }
1051
1052 @Override
1053 public void onAttach(@NonNull Context context) {
1054 super.onAttach(context);
1055 delegate = delegateFactory.createDelegate(this);
1056 delegate.onAttach(context);
1057 if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {
1058 requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
1059 // When Android handles a back gesture, it pops an Activity or goes back
1060 // to the home screen. When Flutter handles a back gesture, it pops a
1061 // route inside of the Flutter part of the app. By default, Android
1062 // handles back gestures, so this callback is disabled. If, for example,
1063 // the Flutter app has routes for which it wants to handle the back
1064 // gesture, then it will enable this callback using
1065 // setFrameworkHandlesBack.
1066 onBackPressedCallback.setEnabled(false);
1067 }
1068 context.registerComponentCallbacks(this);
1069 }
1070
1071 @Override
1072 public void onCreate(@Nullable Bundle savedInstanceState) {
1073 super.onCreate(savedInstanceState);
1074 delegate.onRestoreInstanceState(savedInstanceState);
1075 }
1076
1077 @Nullable
1078 @Override
1079 public View onCreateView(
1080 LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
1081 return delegate.onCreateView(
1082 inflater,
1083 container,
1084 savedInstanceState,
1085 /*flutterViewId=*/ FLUTTER_VIEW_ID,
1087 }
1088
1089 @Override
1090 public void onStart() {
1091 super.onStart();
1092 if (stillAttachedForEvent("onStart")) {
1093 delegate.onStart();
1094 }
1095 }
1096
1097 @Override
1098 public void onResume() {
1099 super.onResume();
1100 if (stillAttachedForEvent("onResume")) {
1102 }
1103 }
1104
1105 // TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if
1106 // possible.
1107 @ActivityCallThrough
1108 public void onPostResume() {
1109 if (stillAttachedForEvent("onPostResume")) {
1111 }
1112 }
1113
1114 @Override
1115 public void onPause() {
1116 super.onPause();
1117 if (stillAttachedForEvent("onPause")) {
1118 delegate.onPause();
1119 }
1120 }
1121
1122 @Override
1123 public void onStop() {
1124 super.onStop();
1125 if (stillAttachedForEvent("onStop")) {
1126 delegate.onStop();
1127 }
1128 }
1129
1130 @Override
1131 public void onViewCreated(View view, Bundle savedInstanceState) {
1132 super.onViewCreated(view, savedInstanceState);
1133 view.getViewTreeObserver().addOnWindowFocusChangeListener(onWindowFocusChangeListener);
1134 }
1135
1136 @Override
1137 public void onDestroyView() {
1138 super.onDestroyView();
1139 requireView()
1140 .getViewTreeObserver()
1141 .removeOnWindowFocusChangeListener(onWindowFocusChangeListener);
1142 if (stillAttachedForEvent("onDestroyView")) {
1144 }
1145 }
1146
1147 @Override
1148 public void onSaveInstanceState(Bundle outState) {
1149 super.onSaveInstanceState(outState);
1150 if (stillAttachedForEvent("onSaveInstanceState")) {
1151 delegate.onSaveInstanceState(outState);
1152 }
1153 }
1154
1155 @Override
1157 Log.w(
1158 TAG,
1159 "FlutterFragment "
1160 + this
1161 + " connection to the engine "
1163 + " evicted by another attaching activity");
1164 if (delegate != null) {
1165 // Redundant calls are ok.
1168 }
1169 }
1170
1171 @Override
1172 public void onDetach() {
1173 getContext().unregisterComponentCallbacks(this);
1174 super.onDetach();
1175 if (delegate != null) {
1177 delegate.release();
1178 delegate = null;
1179 } else {
1180 Log.v(TAG, "FlutterFragment " + this + " onDetach called after release.");
1181 }
1182 }
1183
1184 /**
1185 * The result of a permission request has been received.
1186 *
1187 * <p>See {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])}
1188 *
1189 * <p>
1190 *
1191 * @param requestCode identifier passed with the initial permission request
1192 * @param permissions permissions that were requested
1193 * @param grantResults permission grants or denials
1194 */
1195 @ActivityCallThrough
1197 int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
1198 if (stillAttachedForEvent("onRequestPermissionsResult")) {
1199 delegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
1200 }
1201 }
1202
1203 /**
1204 * A new Intent was received by the {@link android.app.Activity} that currently owns this {@link
1205 * Fragment}.
1206 *
1207 * <p>See {@link android.app.Activity#onNewIntent(Intent)}
1208 *
1209 * <p>
1210 *
1211 * @param intent new Intent
1212 */
1213 @ActivityCallThrough
1214 public void onNewIntent(@NonNull Intent intent) {
1215 if (stillAttachedForEvent("onNewIntent")) {
1216 delegate.onNewIntent(intent);
1217 }
1218 }
1219
1220 /**
1221 * The hardware back button was pressed.
1222 *
1223 * <p>If the fragment uses {@code shouldAutomaticallyHandleOnBackPressed(true)}, this method
1224 * should not be called through. It will be called automatically instead.
1225 *
1226 * <p>See {@link android.app.Activity#onBackPressed()}
1227 */
1228 @ActivityCallThrough
1229 public void onBackPressed() {
1230 if (stillAttachedForEvent("onBackPressed")) {
1232 }
1233 }
1234
1235 /**
1236 * A result has been returned after an invocation of {@link
1237 * Fragment#startActivityForResult(Intent, int)}.
1238 *
1239 * <p>
1240 *
1241 * @param requestCode request code sent with {@link Fragment#startActivityForResult(Intent, int)}
1242 * @param resultCode code representing the result of the {@code Activity} that was launched
1243 * @param data any corresponding return data, held within an {@code Intent}
1244 */
1245 @Override
1246 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1247 if (stillAttachedForEvent("onActivityResult")) {
1248 delegate.onActivityResult(requestCode, resultCode, data);
1249 }
1250 }
1251
1252 /**
1253 * The {@link android.app.Activity} that owns this {@link Fragment} is about to go to the
1254 * background as the result of a user's choice/action, i.e., not as the result of an OS decision.
1255 *
1256 * <p>See {@link android.app.Activity#onUserLeaveHint()}
1257 */
1258 @ActivityCallThrough
1259 public void onUserLeaveHint() {
1260 if (stillAttachedForEvent("onUserLeaveHint")) {
1262 }
1263 }
1264
1265 /**
1266 * Callback invoked when memory is low.
1267 *
1268 * <p>This implementation forwards a memory pressure warning to the running Flutter app.
1269 *
1270 * <p>
1271 *
1272 * @param level level
1273 */
1274 @Override
1275 public void onTrimMemory(int level) {
1276 if (stillAttachedForEvent("onTrimMemory")) {
1278 }
1279 }
1280
1281 /**
1282 * {@link FlutterActivityAndFragmentDelegate.Host} method that is used by {@link
1283 * FlutterActivityAndFragmentDelegate} to obtain Flutter shell arguments when initializing
1284 * Flutter.
1285 */
1286 @Override
1287 @NonNull
1289 String[] flutterShellArgsArray = getArguments().getStringArray(ARG_FLUTTER_INITIALIZATION_ARGS);
1290 return new FlutterShellArgs(
1291 flutterShellArgsArray != null ? flutterShellArgsArray : new String[] {});
1292 }
1293
1294 /**
1295 * Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngine} to use
1296 * within this {@code FlutterFragment}, or {@code null} if this {@code FlutterFragment} does not
1297 * want to use a cached {@link io.flutter.embedding.engine.FlutterEngine}.
1298 */
1299 @Nullable
1300 @Override
1301 public String getCachedEngineId() {
1302 return getArguments().getString(ARG_CACHED_ENGINE_ID, null);
1303 }
1304
1305 /**
1306 * Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngineGroup} to
1307 * use within this {@code FlutterFragment}, or {@code null} if this {@code FlutterFragment} does
1308 * not want to use a cached {@link io.flutter.embedding.engine.FlutterEngineGroup}.
1309 */
1310 @Override
1311 @Nullable
1312 public String getCachedEngineGroupId() {
1313 return getArguments().getString(ARG_CACHED_ENGINE_GROUP_ID, null);
1314 }
1315
1316 /**
1317 * Returns true a {@code FlutterEngine} was explicitly created and injected into the {@code
1318 * FlutterFragment} rather than one that was created implicitly in the {@code FlutterFragment}.
1319 */
1320 /* package */ boolean isFlutterEngineInjected() {
1321 return delegate.isFlutterEngineFromHost();
1322 }
1323
1324 /**
1325 * Returns false if the {@link io.flutter.embedding.engine.FlutterEngine} within this {@code
1326 * FlutterFragment} should outlive the {@code FlutterFragment}, itself.
1327 *
1328 * <p>Defaults to true if no custom {@link io.flutter.embedding.engine.FlutterEngine is provided},
1329 * false if a custom {@link FlutterEngine} is provided.
1330 */
1331 @Override
1333 boolean explicitDestructionRequested =
1334 getArguments().getBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, false);
1335 if (getCachedEngineId() != null || delegate.isFlutterEngineFromHost()) {
1336 // Only destroy a cached engine if explicitly requested by app developer.
1337 return explicitDestructionRequested;
1338 } else {
1339 // If this Fragment created the FlutterEngine, destroy it by default unless
1340 // explicitly requested not to.
1341 return getArguments().getBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
1342 }
1343 }
1344
1345 /**
1346 * Returns the name of the Dart method that this {@code FlutterFragment} should execute to start a
1347 * Flutter app.
1348 *
1349 * <p>Defaults to "main".
1350 *
1351 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1352 */
1353 @Override
1354 @NonNull
1356 return getArguments().getString(ARG_DART_ENTRYPOINT, "main");
1357 }
1358
1359 /**
1360 * The Dart entrypoint arguments will be passed as a list of string to Dart's entrypoint function.
1361 *
1362 * <p>A value of null means do not pass any arguments to Dart's entrypoint function.
1363 *
1364 * <p>Subclasses may override this method to directly control the Dart entrypoint arguments.
1365 */
1366 @Override
1367 @Nullable
1369 return getArguments().getStringArrayList(ARG_DART_ENTRYPOINT_ARGS);
1370 }
1371
1372 /**
1373 * Returns the library URI of the Dart method that this {@code FlutterFragment} should execute to
1374 * start a Flutter app.
1375 *
1376 * <p>Defaults to null (example value: "package:foo/bar.dart").
1377 *
1378 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1379 */
1380 @Override
1381 @Nullable
1383 return getArguments().getString(ARG_DART_ENTRYPOINT_URI);
1384 }
1385
1386 /**
1387 * A custom path to the bundle that contains this Flutter app's resources, e.g., Dart code
1388 * snapshots.
1389 *
1390 * <p>When unspecified, the value is null, which defaults to the app bundle path defined in {@link
1391 * io.flutter.embedding.engine.loader.FlutterLoader#findAppBundlePath()}.
1392 *
1393 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1394 */
1395 @Override
1396 @NonNull
1397 public String getAppBundlePath() {
1398 return getArguments().getString(ARG_APP_BUNDLE_PATH);
1399 }
1400
1401 /**
1402 * Returns the initial route that should be rendered within Flutter, once the Flutter app starts.
1403 *
1404 * <p>Defaults to {@code null}, which signifies a route of "/" in Flutter.
1405 *
1406 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1407 */
1408 @Override
1409 @Nullable
1410 public String getInitialRoute() {
1411 return getArguments().getString(ARG_INITIAL_ROUTE);
1412 }
1413
1414 /**
1415 * Returns the desired {@link RenderMode} for the {@link io.flutter.embedding.android.FlutterView}
1416 * displayed in this {@code FlutterFragment}.
1417 *
1418 * <p>Defaults to {@link RenderMode#surface}.
1419 *
1420 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1421 */
1422 @Override
1423 @NonNull
1425 String renderModeName =
1426 getArguments().getString(ARG_FLUTTERVIEW_RENDER_MODE, RenderMode.surface.name());
1427 return RenderMode.valueOf(renderModeName);
1428 }
1429
1430 /**
1431 * Returns the desired {@link TransparencyMode} for the {@link
1432 * io.flutter.embedding.android.FlutterView} displayed in this {@code FlutterFragment}.
1433 *
1434 * <p>Defaults to {@link TransparencyMode#transparent}.
1435 *
1436 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1437 */
1438 @Override
1439 @NonNull
1441 String transparencyModeName =
1442 getArguments()
1444 return TransparencyMode.valueOf(transparencyModeName);
1445 }
1446
1447 /**
1448 * Hook for subclasses to return a {@link io.flutter.embedding.engine.FlutterEngine} with whatever
1449 * configuration is desired.
1450 *
1451 * <p>By default this method defers to this {@code FlutterFragment}'s surrounding {@code
1452 * Activity}, if that {@code Activity} implements {@link
1453 * io.flutter.embedding.android.FlutterEngineProvider}. If this method is overridden, the
1454 * surrounding {@code Activity} will no longer be given an opportunity to provide a {@link
1455 * io.flutter.embedding.engine.FlutterEngine}, unless the subclass explicitly implements that
1456 * behavior.
1457 *
1458 * <p>Consider returning a cached {@link io.flutter.embedding.engine.FlutterEngine} instance from
1459 * this method to avoid the typical warm-up time that a new {@link
1460 * io.flutter.embedding.engine.FlutterEngine} instance requires.
1461 *
1462 * <p>If null is returned then a new default {@link io.flutter.embedding.engine.FlutterEngine}
1463 * will be created to back this {@code FlutterFragment}.
1464 *
1465 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1466 */
1467 @Override
1468 @Nullable
1469 public FlutterEngine provideFlutterEngine(@NonNull Context context) {
1470 // Defer to the FragmentActivity that owns us to see if it wants to provide a
1471 // FlutterEngine.
1472 FlutterEngine flutterEngine = null;
1473 FragmentActivity attachedActivity = getActivity();
1474 if (attachedActivity instanceof FlutterEngineProvider) {
1475 // Defer to the Activity that owns us to provide a FlutterEngine.
1476 Log.v(TAG, "Deferring to attached Activity to provide a FlutterEngine.");
1477 FlutterEngineProvider flutterEngineProvider = (FlutterEngineProvider) attachedActivity;
1478 flutterEngine = flutterEngineProvider.provideFlutterEngine(getContext());
1479 }
1480
1481 return flutterEngine;
1482 }
1483
1484 /**
1485 * Hook for subclasses to obtain a reference to the {@link
1486 * io.flutter.embedding.engine.FlutterEngine} that is owned by this {@code FlutterActivity}.
1487 */
1488 @Nullable
1490 return delegate.getFlutterEngine();
1491 }
1492
1493 @Nullable
1494 @Override
1496 @Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
1497 if (activity != null) {
1498 return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel(), this);
1499 } else {
1500 return null;
1501 }
1502 }
1503
1504 /**
1505 * Configures a {@link io.flutter.embedding.engine.FlutterEngine} after its creation.
1506 *
1507 * <p>This method is called after {@link #provideFlutterEngine(Context)}, and after the given
1508 * {@link io.flutter.embedding.engine.FlutterEngine} has been attached to the owning {@code
1509 * FragmentActivity}. See {@link
1510 * io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(
1511 * ExclusiveAppComponent, Lifecycle)}.
1512 *
1513 * <p>It is possible that the owning {@code FragmentActivity} opted not to connect itself as an
1514 * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that case, any
1515 * configuration, e.g., plugins, must not expect or depend upon an available {@code Activity} at
1516 * the time that this method is invoked.
1517 *
1518 * <p>The default behavior of this method is to defer to the owning {@code FragmentActivity} as a
1519 * {@link io.flutter.embedding.android.FlutterEngineConfigurator}. Subclasses can override this
1520 * method if the subclass needs to override the {@code FragmentActivity}'s behavior, or add to it.
1521 *
1522 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1523 */
1524 @Override
1525 public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
1526 FragmentActivity attachedActivity = getActivity();
1527 if (attachedActivity instanceof FlutterEngineConfigurator) {
1528 ((FlutterEngineConfigurator) attachedActivity).configureFlutterEngine(flutterEngine);
1529 }
1530 }
1531
1532 /**
1533 * Hook for the host to cleanup references that were established in {@link
1534 * #configureFlutterEngine(FlutterEngine)} before the host is destroyed or detached.
1535 *
1536 * <p>This method is called in {@link #onDetach()}.
1537 */
1538 @Override
1539 public void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine) {
1540 FragmentActivity attachedActivity = getActivity();
1541 if (attachedActivity instanceof FlutterEngineConfigurator) {
1542 ((FlutterEngineConfigurator) attachedActivity).cleanUpFlutterEngine(flutterEngine);
1543 }
1544 }
1545
1546 /**
1547 * See {@link NewEngineFragmentBuilder#shouldAttachEngineToActivity()} and {@link
1548 * CachedEngineFragmentBuilder#shouldAttachEngineToActivity()}.
1549 *
1550 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate}
1551 */
1552 @Override
1554 return getArguments().getBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY);
1555 }
1556
1557 /**
1558 * Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
1559 * getInitialRoute} returns null.
1560 */
1561 @Override
1562 public boolean shouldHandleDeeplinking() {
1563 return getArguments().getBoolean(ARG_HANDLE_DEEPLINKING);
1564 }
1565
1566 @Override
1567 public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) {
1568 // Hook for subclasses.
1569 }
1570
1571 @Override
1572 public void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView) {
1573 // Hook for subclasses.
1574 }
1575
1576 /**
1577 * Invoked after the {@link io.flutter.embedding.android.FlutterView} within this {@code
1578 * FlutterFragment} starts rendering pixels to the screen.
1579 *
1580 * <p>This method forwards {@code onFlutterUiDisplayed()} to its attached {@code Activity}, if the
1581 * attached {@code Activity} implements {@link FlutterUiDisplayListener}.
1582 *
1583 * <p>Subclasses that override this method must call through to the {@code super} method.
1584 *
1585 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1586 */
1587 @Override
1588 public void onFlutterUiDisplayed() {
1589 FragmentActivity attachedActivity = getActivity();
1590 if (attachedActivity instanceof FlutterUiDisplayListener) {
1591 ((FlutterUiDisplayListener) attachedActivity).onFlutterUiDisplayed();
1592 }
1593 }
1594
1595 /**
1596 * Invoked after the {@link io.flutter.embedding.android.FlutterView} within this {@code
1597 * FlutterFragment} stops rendering pixels to the screen.
1598 *
1599 * <p>This method forwards {@code onFlutterUiNoLongerDisplayed()} to its attached {@code
1600 * Activity}, if the attached {@code Activity} implements {@link FlutterUiDisplayListener}.
1601 *
1602 * <p>Subclasses that override this method must call through to the {@code super} method.
1603 *
1604 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1605 */
1606 @Override
1608 FragmentActivity attachedActivity = getActivity();
1609 if (attachedActivity instanceof FlutterUiDisplayListener) {
1610 ((FlutterUiDisplayListener) attachedActivity).onFlutterUiNoLongerDisplayed();
1611 }
1612 }
1613
1614 @Override
1615 public boolean shouldRestoreAndSaveState() {
1616 if (getArguments().containsKey(ARG_ENABLE_STATE_RESTORATION)) {
1617 return getArguments().getBoolean(ARG_ENABLE_STATE_RESTORATION);
1618 }
1619 if (getCachedEngineId() != null) {
1620 return false;
1621 }
1622 return true;
1623 }
1624
1625 @Override
1627 if (delegate != null) {
1629 }
1630 }
1631
1632 /**
1633 * Give the host application a chance to take control of the app lifecycle events.
1634 *
1635 * <p>Return {@code false} means the host application dispatches these app lifecycle events, while
1636 * return {@code true} means the engine dispatches these events.
1637 *
1638 * <p>Defaults to {@code true}.
1639 */
1640 @Override
1642 return true;
1643 }
1644
1645 /**
1646 * Whether to automatically attach the {@link FlutterView} to the engine.
1647 *
1648 * <p>Returning {@code false} means that the task of attaching the {@link FlutterView} to the
1649 * engine will be taken over by the host application.
1650 *
1651 * <p>Defaults to {@code true}.
1652 */
1653 @Override
1655 return true;
1656 }
1657
1658 /**
1659 * {@inheritDoc}
1660 *
1661 * <p>Avoid overriding this method when using {@code
1662 * shouldAutomaticallyHandleOnBackPressed(true)}. If you do, you must always {@code return
1663 * super.popSystemNavigator()} rather than {@code return false}. Otherwise the navigation behavior
1664 * will recurse infinitely between this method and {@link #onBackPressed()}, breaking navigation.
1665 */
1666 @Override
1667 public boolean popSystemNavigator() {
1668 if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {
1669 FragmentActivity activity = getActivity();
1670 if (activity != null) {
1671 // Unless we disable the callback, the dispatcher call will trigger it. This will then
1672 // trigger the fragment's onBackPressed() implementation, which will call through to the
1673 // dart side and likely call back through to this method, creating an infinite call loop.
1674 boolean enabledAtStart = onBackPressedCallback.isEnabled();
1675 if (enabledAtStart) {
1676 onBackPressedCallback.setEnabled(false);
1677 }
1678 activity.getOnBackPressedDispatcher().onBackPressed();
1679 if (enabledAtStart) {
1680 onBackPressedCallback.setEnabled(true);
1681 }
1682 return true;
1683 }
1684 }
1685 // Hook for subclass. No-op if returns false.
1686 return false;
1687 }
1688
1689 @Override
1690 public void setFrameworkHandlesBack(boolean frameworkHandlesBack) {
1691 if (!getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {
1692 return;
1693 }
1694 onBackPressedCallback.setEnabled(frameworkHandlesBack);
1695 }
1696
1697 @VisibleForTesting
1698 @NonNull
1700 return getArguments().getBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW);
1701 }
1702
1703 private boolean stillAttachedForEvent(String event) {
1704 if (delegate == null) {
1705 Log.w(TAG, "FlutterFragment " + hashCode() + " " + event + " called after release.");
1706 return false;
1707 }
1708 if (!delegate.isAttached()) {
1709 Log.w(TAG, "FlutterFragment " + hashCode() + " " + event + " called after detach.");
1710 return false;
1711 }
1712 return true;
1713 }
1714
1715 /**
1716 * Annotates methods in {@code FlutterFragment} that must be called by the containing {@code
1717 * Activity}.
1718 */
1720}
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)
void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
CachedEngineFragmentBuilder shouldAutomaticallyHandleOnBackPressed(boolean shouldAutomaticallyHandleOnBackPressed)
CachedEngineFragmentBuilder destroyEngineWithFragment(boolean destroyEngineWithFragment)
CachedEngineFragmentBuilder renderMode(@NonNull RenderMode renderMode)
CachedEngineFragmentBuilder shouldDelayFirstAndroidViewDraw( @NonNull boolean shouldDelayFirstAndroidViewDraw)
CachedEngineFragmentBuilder( @NonNull Class<? extends FlutterFragment > subclass, @NonNull String engineId)
CachedEngineFragmentBuilder transparencyMode( @NonNull TransparencyMode transparencyMode)
CachedEngineFragmentBuilder shouldAttachEngineToActivity(boolean shouldAttachEngineToActivity)
CachedEngineFragmentBuilder handleDeeplinking(@NonNull Boolean handleDeeplinking)
NewEngineFragmentBuilder renderMode(@NonNull RenderMode renderMode)
NewEngineFragmentBuilder initialRoute(@NonNull String initialRoute)
NewEngineFragmentBuilder shouldDelayFirstAndroidViewDraw(boolean shouldDelayFirstAndroidViewDraw)
NewEngineFragmentBuilder transparencyMode(@NonNull TransparencyMode transparencyMode)
NewEngineFragmentBuilder(@NonNull Class<? extends FlutterFragment > subclass)
NewEngineFragmentBuilder handleDeeplinking(@NonNull Boolean handleDeeplinking)
NewEngineFragmentBuilder dartEntrypointArgs(@NonNull List< String > dartEntrypointArgs)
NewEngineFragmentBuilder dartEntrypoint(@NonNull String dartEntrypoint)
NewEngineFragmentBuilder flutterShellArgs(@NonNull FlutterShellArgs shellArgs)
NewEngineFragmentBuilder shouldAutomaticallyHandleOnBackPressed(boolean shouldAutomaticallyHandleOnBackPressed)
NewEngineFragmentBuilder shouldAttachEngineToActivity(boolean shouldAttachEngineToActivity)
NewEngineFragmentBuilder dartLibraryUri(@NonNull String dartLibraryUri)
NewEngineFragmentBuilder appBundlePath(@NonNull String appBundlePath)
NewEngineInGroupFragmentBuilder handleDeeplinking(@NonNull boolean handleDeeplinking)
NewEngineInGroupFragmentBuilder initialRoute(@NonNull String initialRoute)
NewEngineInGroupFragmentBuilder( @NonNull Class<? extends FlutterFragment > fragmentClass, @NonNull String engineGroupId)
NewEngineInGroupFragmentBuilder shouldDelayFirstAndroidViewDraw( @NonNull boolean shouldDelayFirstAndroidViewDraw)
NewEngineInGroupFragmentBuilder renderMode(@NonNull RenderMode renderMode)
NewEngineInGroupFragmentBuilder shouldAutomaticallyHandleOnBackPressed(boolean shouldAutomaticallyHandleOnBackPressed)
NewEngineInGroupFragmentBuilder transparencyMode( @NonNull TransparencyMode transparencyMode)
NewEngineInGroupFragmentBuilder dartEntrypoint(@NonNull String dartEntrypoint)
NewEngineInGroupFragmentBuilder shouldAttachEngineToActivity(boolean shouldAttachEngineToActivity)
void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
void setFrameworkHandlesBack(boolean frameworkHandlesBack)
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView)
void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView)
static NewEngineFragmentBuilder withNewEngine()
void onViewCreated(View view, Bundle savedInstanceState)
void onCreate(@Nullable Bundle savedInstanceState)
static NewEngineInGroupFragmentBuilder withNewEngineInGroup( @NonNull String engineGroupId)
static final String ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED
FlutterActivityAndFragmentDelegate createDelegate(FlutterActivityAndFragmentDelegate.Host host)
void configureFlutterEngine(@NonNull FlutterEngine flutterEngine)
void onActivityResult(int requestCode, int resultCode, Intent data)
static CachedEngineFragmentBuilder withCachedEngine(@NonNull String engineId)
PlatformPlugin providePlatformPlugin( @Nullable Activity activity, @NonNull FlutterEngine flutterEngine)
ExclusiveAppComponent< Activity > getExclusiveAppComponent()
FlutterEngine provideFlutterEngine(@NonNull Context context)
void setDelegateFactory( @NonNull FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory)
void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine)
FlutterActivityAndFragmentDelegate delegate
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent * event
FlutterEngine provideFlutterEngine(@NonNull Context context)
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 T
Definition: precompiler.cc:65
#define TAG()
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63