Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
329 public NewEngineFragmentBuilder flutterShellArgs(@NonNull FlutterShellArgs shellArgs) {
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. */
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
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 }
1060 context.registerComponentCallbacks(this);
1061 }
1062
1063 @Override
1064 public void onCreate(@Nullable Bundle savedInstanceState) {
1065 super.onCreate(savedInstanceState);
1066 delegate.onRestoreInstanceState(savedInstanceState);
1067 }
1068
1069 @Nullable
1070 @Override
1071 public View onCreateView(
1072 LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
1073 return delegate.onCreateView(
1074 inflater,
1075 container,
1076 savedInstanceState,
1077 /*flutterViewId=*/ FLUTTER_VIEW_ID,
1079 }
1080
1081 @Override
1082 public void onStart() {
1083 super.onStart();
1084 if (stillAttachedForEvent("onStart")) {
1085 delegate.onStart();
1086 }
1087 }
1088
1089 @Override
1090 public void onResume() {
1091 super.onResume();
1092 if (stillAttachedForEvent("onResume")) {
1094 }
1095 }
1096
1097 // TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if
1098 // possible.
1099 @ActivityCallThrough
1100 public void onPostResume() {
1101 if (stillAttachedForEvent("onPostResume")) {
1103 }
1104 }
1105
1106 @Override
1107 public void onPause() {
1108 super.onPause();
1109 if (stillAttachedForEvent("onPause")) {
1110 delegate.onPause();
1111 }
1112 }
1113
1114 @Override
1115 public void onStop() {
1116 super.onStop();
1117 if (stillAttachedForEvent("onStop")) {
1118 delegate.onStop();
1119 }
1120 }
1121
1122 @Override
1123 public void onViewCreated(View view, Bundle savedInstanceState) {
1124 super.onViewCreated(view, savedInstanceState);
1125 view.getViewTreeObserver().addOnWindowFocusChangeListener(onWindowFocusChangeListener);
1126 }
1127
1128 @Override
1129 public void onDestroyView() {
1130 super.onDestroyView();
1131 requireView()
1132 .getViewTreeObserver()
1133 .removeOnWindowFocusChangeListener(onWindowFocusChangeListener);
1134 if (stillAttachedForEvent("onDestroyView")) {
1136 }
1137 }
1138
1139 @Override
1140 public void onSaveInstanceState(Bundle outState) {
1141 super.onSaveInstanceState(outState);
1142 if (stillAttachedForEvent("onSaveInstanceState")) {
1143 delegate.onSaveInstanceState(outState);
1144 }
1145 }
1146
1147 @Override
1149 Log.w(
1150 TAG,
1151 "FlutterFragment "
1152 + this
1153 + " connection to the engine "
1155 + " evicted by another attaching activity");
1156 if (delegate != null) {
1157 // Redundant calls are ok.
1160 }
1161 }
1162
1163 @Override
1164 public void onDetach() {
1165 getContext().unregisterComponentCallbacks(this);
1166 super.onDetach();
1167 if (delegate != null) {
1169 delegate.release();
1170 delegate = null;
1171 } else {
1172 Log.v(TAG, "FlutterFragment " + this + " onDetach called after release.");
1173 }
1174 }
1175
1176 /**
1177 * The result of a permission request has been received.
1178 *
1179 * <p>See {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])}
1180 *
1181 * <p>
1182 *
1183 * @param requestCode identifier passed with the initial permission request
1184 * @param permissions permissions that were requested
1185 * @param grantResults permission grants or denials
1186 */
1187 @ActivityCallThrough
1189 int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
1190 if (stillAttachedForEvent("onRequestPermissionsResult")) {
1191 delegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
1192 }
1193 }
1194
1195 /**
1196 * A new Intent was received by the {@link android.app.Activity} that currently owns this {@link
1197 * Fragment}.
1198 *
1199 * <p>See {@link android.app.Activity#onNewIntent(Intent)}
1200 *
1201 * <p>
1202 *
1203 * @param intent new Intent
1204 */
1205 @ActivityCallThrough
1206 public void onNewIntent(@NonNull Intent intent) {
1207 if (stillAttachedForEvent("onNewIntent")) {
1208 delegate.onNewIntent(intent);
1209 }
1210 }
1211
1212 /**
1213 * The hardware back button was pressed.
1214 *
1215 * <p>If the fragment uses {@code shouldAutomaticallyHandleOnBackPressed(true)}, this method
1216 * should not be called through. It will be called automatically instead.
1217 *
1218 * <p>See {@link android.app.Activity#onBackPressed()}
1219 */
1220 @ActivityCallThrough
1221 public void onBackPressed() {
1222 if (stillAttachedForEvent("onBackPressed")) {
1224 }
1225 }
1226
1227 /**
1228 * A result has been returned after an invocation of {@link
1229 * Fragment#startActivityForResult(Intent, int)}.
1230 *
1231 * <p>
1232 *
1233 * @param requestCode request code sent with {@link Fragment#startActivityForResult(Intent, int)}
1234 * @param resultCode code representing the result of the {@code Activity} that was launched
1235 * @param data any corresponding return data, held within an {@code Intent}
1236 */
1237 @Override
1238 public void onActivityResult(int requestCode, int resultCode, Intent data) {
1239 if (stillAttachedForEvent("onActivityResult")) {
1240 delegate.onActivityResult(requestCode, resultCode, data);
1241 }
1242 }
1243
1244 /**
1245 * The {@link android.app.Activity} that owns this {@link Fragment} is about to go to the
1246 * background as the result of a user's choice/action, i.e., not as the result of an OS decision.
1247 *
1248 * <p>See {@link android.app.Activity#onUserLeaveHint()}
1249 */
1250 @ActivityCallThrough
1251 public void onUserLeaveHint() {
1252 if (stillAttachedForEvent("onUserLeaveHint")) {
1254 }
1255 }
1256
1257 /**
1258 * Callback invoked when memory is low.
1259 *
1260 * <p>This implementation forwards a memory pressure warning to the running Flutter app.
1261 *
1262 * <p>
1263 *
1264 * @param level level
1265 */
1266 @Override
1267 public void onTrimMemory(int level) {
1268 if (stillAttachedForEvent("onTrimMemory")) {
1269 delegate.onTrimMemory(level);
1270 }
1271 }
1272
1273 /**
1274 * {@link FlutterActivityAndFragmentDelegate.Host} method that is used by {@link
1275 * FlutterActivityAndFragmentDelegate} to obtain Flutter shell arguments when initializing
1276 * Flutter.
1277 */
1278 @Override
1279 @NonNull
1280 public FlutterShellArgs getFlutterShellArgs() {
1281 String[] flutterShellArgsArray = getArguments().getStringArray(ARG_FLUTTER_INITIALIZATION_ARGS);
1282 return new FlutterShellArgs(
1283 flutterShellArgsArray != null ? flutterShellArgsArray : new String[] {});
1284 }
1285
1286 /**
1287 * Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngine} to use
1288 * within this {@code FlutterFragment}, or {@code null} if this {@code FlutterFragment} does not
1289 * want to use a cached {@link io.flutter.embedding.engine.FlutterEngine}.
1290 */
1291 @Nullable
1292 @Override
1293 public String getCachedEngineId() {
1294 return getArguments().getString(ARG_CACHED_ENGINE_ID, null);
1295 }
1296
1297 /**
1298 * Returns the ID of a statically cached {@link io.flutter.embedding.engine.FlutterEngineGroup} to
1299 * use within this {@code FlutterFragment}, or {@code null} if this {@code FlutterFragment} does
1300 * not want to use a cached {@link io.flutter.embedding.engine.FlutterEngineGroup}.
1301 */
1302 @Override
1303 @Nullable
1304 public String getCachedEngineGroupId() {
1305 return getArguments().getString(ARG_CACHED_ENGINE_GROUP_ID, null);
1306 }
1307
1308 /**
1309 * Returns true a {@code FlutterEngine} was explicitly created and injected into the {@code
1310 * FlutterFragment} rather than one that was created implicitly in the {@code FlutterFragment}.
1311 */
1312 /* package */ boolean isFlutterEngineInjected() {
1313 return delegate.isFlutterEngineFromHost();
1314 }
1315
1316 /**
1317 * Returns false if the {@link io.flutter.embedding.engine.FlutterEngine} within this {@code
1318 * FlutterFragment} should outlive the {@code FlutterFragment}, itself.
1319 *
1320 * <p>Defaults to true if no custom {@link io.flutter.embedding.engine.FlutterEngine is provided},
1321 * false if a custom {@link FlutterEngine} is provided.
1322 */
1323 @Override
1325 boolean explicitDestructionRequested =
1326 getArguments().getBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, false);
1327 if (getCachedEngineId() != null || delegate.isFlutterEngineFromHost()) {
1328 // Only destroy a cached engine if explicitly requested by app developer.
1329 return explicitDestructionRequested;
1330 } else {
1331 // If this Fragment created the FlutterEngine, destroy it by default unless
1332 // explicitly requested not to.
1333 return getArguments().getBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
1334 }
1335 }
1336
1337 /**
1338 * Returns the name of the Dart method that this {@code FlutterFragment} should execute to start a
1339 * Flutter app.
1340 *
1341 * <p>Defaults to "main".
1342 *
1343 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1344 */
1345 @Override
1346 @NonNull
1348 return getArguments().getString(ARG_DART_ENTRYPOINT, "main");
1349 }
1350
1351 /**
1352 * The Dart entrypoint arguments will be passed as a list of string to Dart's entrypoint function.
1353 *
1354 * <p>A value of null means do not pass any arguments to Dart's entrypoint function.
1355 *
1356 * <p>Subclasses may override this method to directly control the Dart entrypoint arguments.
1357 */
1358 @Override
1359 @Nullable
1361 return getArguments().getStringArrayList(ARG_DART_ENTRYPOINT_ARGS);
1362 }
1363
1364 /**
1365 * Returns the library URI of the Dart method that this {@code FlutterFragment} should execute to
1366 * start a Flutter app.
1367 *
1368 * <p>Defaults to null (example value: "package:foo/bar.dart").
1369 *
1370 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1371 */
1372 @Override
1373 @Nullable
1375 return getArguments().getString(ARG_DART_ENTRYPOINT_URI);
1376 }
1377
1378 /**
1379 * A custom path to the bundle that contains this Flutter app's resources, e.g., Dart code
1380 * snapshots.
1381 *
1382 * <p>When unspecified, the value is null, which defaults to the app bundle path defined in {@link
1383 * io.flutter.embedding.engine.loader.FlutterLoader#findAppBundlePath()}.
1384 *
1385 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1386 */
1387 @Override
1388 @NonNull
1389 public String getAppBundlePath() {
1390 return getArguments().getString(ARG_APP_BUNDLE_PATH);
1391 }
1392
1393 /**
1394 * Returns the initial route that should be rendered within Flutter, once the Flutter app starts.
1395 *
1396 * <p>Defaults to {@code null}, which signifies a route of "/" in Flutter.
1397 *
1398 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1399 */
1400 @Override
1401 @Nullable
1402 public String getInitialRoute() {
1403 return getArguments().getString(ARG_INITIAL_ROUTE);
1404 }
1405
1406 /**
1407 * Returns the desired {@link RenderMode} for the {@link io.flutter.embedding.android.FlutterView}
1408 * displayed in this {@code FlutterFragment}.
1409 *
1410 * <p>Defaults to {@link RenderMode#surface}.
1411 *
1412 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1413 */
1414 @Override
1415 @NonNull
1417 String renderModeName =
1418 getArguments().getString(ARG_FLUTTERVIEW_RENDER_MODE, RenderMode.surface.name());
1419 return RenderMode.valueOf(renderModeName);
1420 }
1421
1422 /**
1423 * Returns the desired {@link TransparencyMode} for the {@link
1424 * io.flutter.embedding.android.FlutterView} displayed in this {@code FlutterFragment}.
1425 *
1426 * <p>Defaults to {@link TransparencyMode#transparent}.
1427 *
1428 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1429 */
1430 @Override
1431 @NonNull
1433 String transparencyModeName =
1434 getArguments()
1436 return TransparencyMode.valueOf(transparencyModeName);
1437 }
1438
1439 /**
1440 * Hook for subclasses to return a {@link io.flutter.embedding.engine.FlutterEngine} with whatever
1441 * configuration is desired.
1442 *
1443 * <p>By default this method defers to this {@code FlutterFragment}'s surrounding {@code
1444 * Activity}, if that {@code Activity} implements {@link
1445 * io.flutter.embedding.android.FlutterEngineProvider}. If this method is overridden, the
1446 * surrounding {@code Activity} will no longer be given an opportunity to provide a {@link
1447 * io.flutter.embedding.engine.FlutterEngine}, unless the subclass explicitly implements that
1448 * behavior.
1449 *
1450 * <p>Consider returning a cached {@link io.flutter.embedding.engine.FlutterEngine} instance from
1451 * this method to avoid the typical warm-up time that a new {@link
1452 * io.flutter.embedding.engine.FlutterEngine} instance requires.
1453 *
1454 * <p>If null is returned then a new default {@link io.flutter.embedding.engine.FlutterEngine}
1455 * will be created to back this {@code FlutterFragment}.
1456 *
1457 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1458 */
1459 @Override
1460 @Nullable
1461 public FlutterEngine provideFlutterEngine(@NonNull Context context) {
1462 // Defer to the FragmentActivity that owns us to see if it wants to provide a
1463 // FlutterEngine.
1464 FlutterEngine flutterEngine = null;
1465 FragmentActivity attachedActivity = getActivity();
1466 if (attachedActivity instanceof FlutterEngineProvider) {
1467 // Defer to the Activity that owns us to provide a FlutterEngine.
1468 Log.v(TAG, "Deferring to attached Activity to provide a FlutterEngine.");
1469 FlutterEngineProvider flutterEngineProvider = (FlutterEngineProvider) attachedActivity;
1470 flutterEngine = flutterEngineProvider.provideFlutterEngine(getContext());
1471 }
1472
1473 return flutterEngine;
1474 }
1475
1476 /**
1477 * Hook for subclasses to obtain a reference to the {@link
1478 * io.flutter.embedding.engine.FlutterEngine} that is owned by this {@code FlutterActivity}.
1479 */
1480 @Nullable
1484
1485 @Nullable
1486 @Override
1487 public PlatformPlugin providePlatformPlugin(
1488 @Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
1489 if (activity != null) {
1490 return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel(), this);
1491 } else {
1492 return null;
1493 }
1494 }
1495
1496 /**
1497 * Configures a {@link io.flutter.embedding.engine.FlutterEngine} after its creation.
1498 *
1499 * <p>This method is called after {@link #provideFlutterEngine(Context)}, and after the given
1500 * {@link io.flutter.embedding.engine.FlutterEngine} has been attached to the owning {@code
1501 * FragmentActivity}. See {@link
1502 * io.flutter.embedding.engine.plugins.activity.ActivityControlSurface#attachToActivity(
1503 * ExclusiveAppComponent, Lifecycle)}.
1504 *
1505 * <p>It is possible that the owning {@code FragmentActivity} opted not to connect itself as an
1506 * {@link io.flutter.embedding.engine.plugins.activity.ActivityControlSurface}. In that case, any
1507 * configuration, e.g., plugins, must not expect or depend upon an available {@code Activity} at
1508 * the time that this method is invoked.
1509 *
1510 * <p>The default behavior of this method is to defer to the owning {@code FragmentActivity} as a
1511 * {@link io.flutter.embedding.android.FlutterEngineConfigurator}. Subclasses can override this
1512 * method if the subclass needs to override the {@code FragmentActivity}'s behavior, or add to it.
1513 *
1514 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1515 */
1516 @Override
1517 public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
1518 FragmentActivity attachedActivity = getActivity();
1519 if (attachedActivity instanceof FlutterEngineConfigurator) {
1520 ((FlutterEngineConfigurator) attachedActivity).configureFlutterEngine(flutterEngine);
1521 }
1522 }
1523
1524 /**
1525 * Hook for the host to cleanup references that were established in {@link
1526 * #configureFlutterEngine(FlutterEngine)} before the host is destroyed or detached.
1527 *
1528 * <p>This method is called in {@link #onDetach()}.
1529 */
1530 @Override
1531 public void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine) {
1532 FragmentActivity attachedActivity = getActivity();
1533 if (attachedActivity instanceof FlutterEngineConfigurator) {
1534 ((FlutterEngineConfigurator) attachedActivity).cleanUpFlutterEngine(flutterEngine);
1535 }
1536 }
1537
1538 /**
1539 * See {@link NewEngineFragmentBuilder#shouldAttachEngineToActivity()} and {@link
1540 * CachedEngineFragmentBuilder#shouldAttachEngineToActivity()}.
1541 *
1542 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate}
1543 */
1544 @Override
1546 return getArguments().getBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY);
1547 }
1548
1549 /**
1550 * Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
1551 * getInitialRoute} returns null.
1552 */
1553 @Override
1554 public boolean shouldHandleDeeplinking() {
1555 return getArguments().getBoolean(ARG_HANDLE_DEEPLINKING);
1556 }
1557
1558 @Override
1559 public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) {
1560 // Hook for subclasses.
1561 }
1562
1563 @Override
1564 public void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView) {
1565 // Hook for subclasses.
1566 }
1567
1568 /**
1569 * Invoked after the {@link io.flutter.embedding.android.FlutterView} within this {@code
1570 * FlutterFragment} starts rendering pixels to the screen.
1571 *
1572 * <p>This method forwards {@code onFlutterUiDisplayed()} to its attached {@code Activity}, if the
1573 * attached {@code Activity} implements {@link FlutterUiDisplayListener}.
1574 *
1575 * <p>Subclasses that override this method must call through to the {@code super} method.
1576 *
1577 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1578 */
1579 @Override
1580 public void onFlutterUiDisplayed() {
1581 FragmentActivity attachedActivity = getActivity();
1582 if (attachedActivity instanceof FlutterUiDisplayListener) {
1583 ((FlutterUiDisplayListener) attachedActivity).onFlutterUiDisplayed();
1584 }
1585 }
1586
1587 /**
1588 * Invoked after the {@link io.flutter.embedding.android.FlutterView} within this {@code
1589 * FlutterFragment} stops rendering pixels to the screen.
1590 *
1591 * <p>This method forwards {@code onFlutterUiNoLongerDisplayed()} to its attached {@code
1592 * Activity}, if the attached {@code Activity} implements {@link FlutterUiDisplayListener}.
1593 *
1594 * <p>Subclasses that override this method must call through to the {@code super} method.
1595 *
1596 * <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
1597 */
1598 @Override
1600 FragmentActivity attachedActivity = getActivity();
1601 if (attachedActivity instanceof FlutterUiDisplayListener) {
1602 ((FlutterUiDisplayListener) attachedActivity).onFlutterUiNoLongerDisplayed();
1603 }
1604 }
1605
1606 @Override
1607 public boolean shouldRestoreAndSaveState() {
1608 if (getArguments().containsKey(ARG_ENABLE_STATE_RESTORATION)) {
1609 return getArguments().getBoolean(ARG_ENABLE_STATE_RESTORATION);
1610 }
1611 if (getCachedEngineId() != null) {
1612 return false;
1613 }
1614 return true;
1615 }
1616
1617 @Override
1619 if (delegate != null) {
1621 }
1622 }
1623
1624 /**
1625 * Give the host application a chance to take control of the app lifecycle events.
1626 *
1627 * <p>Return {@code false} means the host application dispatches these app lifecycle events, while
1628 * return {@code true} means the engine dispatches these events.
1629 *
1630 * <p>Defaults to {@code true}.
1631 */
1632 @Override
1634 return true;
1635 }
1636
1637 /**
1638 * Whether to automatically attach the {@link FlutterView} to the engine.
1639 *
1640 * <p>Returning {@code false} means that the task of attaching the {@link FlutterView} to the
1641 * engine will be taken over by the host application.
1642 *
1643 * <p>Defaults to {@code true}.
1644 */
1645 @Override
1647 return true;
1648 }
1649
1650 /**
1651 * {@inheritDoc}
1652 *
1653 * <p>Avoid overriding this method when using {@code
1654 * shouldAutomaticallyHandleOnBackPressed(true)}. If you do, you must always {@code return
1655 * super.popSystemNavigator()} rather than {@code return false}. Otherwise the navigation behavior
1656 * will recurse infinitely between this method and {@link #onBackPressed()}, breaking navigation.
1657 */
1658 @Override
1659 public boolean popSystemNavigator() {
1660 if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) {
1661 FragmentActivity activity = getActivity();
1662 if (activity != null) {
1663 // Unless we disable the callback, the dispatcher call will trigger it. This will then
1664 // trigger the fragment's onBackPressed() implementation, which will call through to the
1665 // dart side and likely call back through to this method, creating an infinite call loop.
1666 onBackPressedCallback.setEnabled(false);
1667 activity.getOnBackPressedDispatcher().onBackPressed();
1668 onBackPressedCallback.setEnabled(true);
1669 return true;
1670 }
1671 }
1672 // Hook for subclass. No-op if returns false.
1673 return false;
1674 }
1675
1676 @VisibleForTesting
1677 @NonNull
1679 return getArguments().getBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW);
1680 }
1681
1682 private boolean stillAttachedForEvent(String event) {
1683 if (delegate == null) {
1684 Log.w(TAG, "FlutterFragment " + hashCode() + " " + event + " called after release.");
1685 return false;
1686 }
1687 if (!delegate.isAttached()) {
1688 Log.w(TAG, "FlutterFragment " + hashCode() + " " + event + " called after detach.");
1689 return false;
1690 }
1691 return true;
1692 }
1693
1694 /**
1695 * Annotates methods in {@code FlutterFragment} that must be called by the containing {@code
1696 * Activity}.
1697 */
1699}
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)
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)
#define T
#define TAG()