diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
index d77a06f1fcc88..49b9cc976bfb3 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
@@ -10,6 +10,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
@@ -75,10 +76,12 @@ public class FlutterActivity extends FragmentActivity implements OnFirstFrameRen
// Intent extra arguments.
protected static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint";
protected static final String EXTRA_INITIAL_ROUTE = "initial_route";
+ protected static final String EXTRA_BACKGROUND_MODE = "background_mode";
// Default configuration.
protected static final String DEFAULT_DART_ENTRYPOINT = "main";
protected static final String DEFAULT_INITIAL_ROUTE = "/";
+ protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode.opaque.name();
// FlutterFragment management.
private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment";
@@ -114,6 +117,7 @@ public static class IntentBuilder {
private final Class extends FlutterActivity> activityClass;
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT;
private String initialRoute = DEFAULT_INITIAL_ROUTE;
+ private String backgroundMode = DEFAULT_BACKGROUND_MODE;
protected IntentBuilder(@NonNull Class extends FlutterActivity> activityClass) {
this.activityClass = activityClass;
@@ -138,6 +142,28 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
return this;
}
+ /**
+ * The mode of {@code FlutterActivity}'s background, either {@link BackgroundMode#opaque} or
+ * {@link BackgroundMode#transparent}.
+ *
+ * The default background mode is {@link BackgroundMode#opaque}.
+ *
+ * Choosing a background mode of {@link BackgroundMode#transparent} will configure the inner
+ * {@link FlutterView} of this {@code FlutterActivity} to be configured with a
+ * {@link FlutterTextureView} to support transparency. This choice has a non-trivial performance
+ * impact. A transparent background should only be used if it is necessary for the app design
+ * being implemented.
+ *
+ * A {@code FlutterActivity} that is configured with a background mode of
+ * {@link BackgroundMode#transparent} must have a theme applied to it that includes the
+ * following property: {@code - true
}.
+ */
+ @NonNull
+ public IntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
+ this.backgroundMode = backgroundMode.name();
+ return this;
+ }
+
/**
* Creates and returns an {@link Intent} that will launch a {@code FlutterActivity} with
* the desired configuration.
@@ -146,7 +172,8 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
public Intent build(@NonNull Context context) {
return new Intent(context, activityClass)
.putExtra(EXTRA_DART_ENTRYPOINT, dartEntrypoint)
- .putExtra(EXTRA_INITIAL_ROUTE, initialRoute);
+ .putExtra(EXTRA_INITIAL_ROUTE, initialRoute)
+ .putExtra(EXTRA_BACKGROUND_MODE, backgroundMode);
}
}
@@ -154,12 +181,31 @@ public Intent build(@NonNull Context context) {
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
+ configureWindowForTransparency();
setContentView(createFragmentContainer());
showCoverView();
configureStatusBarForFullscreenFlutterExperience();
ensureFlutterFragmentCreated();
}
+ /**
+ * Sets this {@code Activity}'s {@code Window} background to be transparent, and hides the status
+ * bar, if this {@code Activity}'s desired {@link BackgroundMode} is {@link BackgroundMode#transparent}.
+ *
+ * For {@code Activity} transparency to work as expected, the theme applied to this {@code Activity}
+ * must include {@code - true
}.
+ */
+ private void configureWindowForTransparency() {
+ BackgroundMode backgroundMode = getBackgroundMode();
+ if (backgroundMode == BackgroundMode.transparent) {
+ getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ getWindow().setFlags(
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ );
+ }
+ }
+
/**
* Cover all visible {@code Activity} area with a {@code View} that paints everything the same
* color as the {@code Window}.
@@ -170,6 +216,11 @@ public void onCreate(Bundle savedInstanceState) {
* itself transparent.
*/
private void showCoverView() {
+ if (getBackgroundMode() == BackgroundMode.transparent) {
+ // Don't display an opaque cover view if the Activity is intended to be transparent.
+ return;
+ }
+
// Create the coverView.
if (coverView == null) {
coverView = new View(this);
@@ -210,7 +261,9 @@ private Drawable createCoverViewBackground() {
* for details.
*/
private void hideCoverView() {
- coverView.setVisibility(View.GONE);
+ if (coverView != null) {
+ coverView.setVisibility(View.GONE);
+ }
}
private void configureStatusBarForFullscreenFlutterExperience() {
@@ -267,13 +320,19 @@ private void ensureFlutterFragmentCreated() {
*/
@NonNull
protected FlutterFragment createFlutterFragment() {
+ BackgroundMode backgroundMode = getBackgroundMode();
+
return new FlutterFragment.Builder()
.dartEntrypoint(getDartEntrypoint())
.initialRoute(getInitialRoute())
.appBundlePath(getAppBundlePath())
.flutterShellArgs(FlutterShellArgs.fromIntent(getIntent()))
- .renderMode(FlutterView.RenderMode.surface)
- .transparencyMode(FlutterView.TransparencyMode.opaque)
+ .renderMode(backgroundMode == BackgroundMode.opaque
+ ? FlutterView.RenderMode.surface
+ : FlutterView.RenderMode.texture)
+ .transparencyMode(backgroundMode == BackgroundMode.opaque
+ ? FlutterView.TransparencyMode.opaque
+ : FlutterView.TransparencyMode.transparent)
.shouldAttachEngineToActivity(shouldAttachEngineToActivity())
.build();
}
@@ -432,6 +491,19 @@ protected String getInitialRoute() {
}
}
+ /**
+ * The desired window background mode of this {@code Activity}, which defaults to
+ * {@link BackgroundMode#opaque}.
+ */
+ @NonNull
+ protected BackgroundMode getBackgroundMode() {
+ if (getIntent().hasExtra(EXTRA_BACKGROUND_MODE)) {
+ return BackgroundMode.valueOf(getIntent().getStringExtra(EXTRA_BACKGROUND_MODE));
+ } else {
+ return BackgroundMode.opaque;
+ }
+ }
+
/**
* Returns true if Flutter is running in "debug mode", and false otherwise.
*
@@ -445,4 +517,14 @@ private boolean isDebuggable() {
public void onFirstFrameRendered() {
hideCoverView();
}
+
+ /**
+ * The mode of the background of a {@code FlutterActivity}, either opaque or transparent.
+ */
+ public enum BackgroundMode {
+ /** Indicates a FlutterActivity with an opaque background. This is the default. */
+ opaque,
+ /** Indicates a FlutterActivity with a transparent background. */
+ transparent
+ }
}