1010import android .content .pm .ApplicationInfo ;
1111import android .content .pm .PackageManager ;
1212import android .graphics .Color ;
13+ import android .graphics .drawable .ColorDrawable ;
1314import android .graphics .drawable .Drawable ;
1415import android .os .Build ;
1516import android .os .Bundle ;
@@ -75,10 +76,12 @@ public class FlutterActivity extends FragmentActivity implements OnFirstFrameRen
7576 // Intent extra arguments.
7677 protected static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint" ;
7778 protected static final String EXTRA_INITIAL_ROUTE = "initial_route" ;
79+ protected static final String EXTRA_BACKGROUND_MODE = "background_mode" ;
7880
7981 // Default configuration.
8082 protected static final String DEFAULT_DART_ENTRYPOINT = "main" ;
8183 protected static final String DEFAULT_INITIAL_ROUTE = "/" ;
84+ protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode .opaque .name ();
8285
8386 // FlutterFragment management.
8487 private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment" ;
@@ -114,6 +117,7 @@ public static class IntentBuilder {
114117 private final Class <? extends FlutterActivity > activityClass ;
115118 private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT ;
116119 private String initialRoute = DEFAULT_INITIAL_ROUTE ;
120+ private String backgroundMode = DEFAULT_BACKGROUND_MODE ;
117121
118122 protected IntentBuilder (@ NonNull Class <? extends FlutterActivity > activityClass ) {
119123 this .activityClass = activityClass ;
@@ -138,6 +142,28 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
138142 return this ;
139143 }
140144
145+ /**
146+ * The mode of {@code FlutterActivity}'s background, either {@link BackgroundMode#opaque} or
147+ * {@link BackgroundMode#transparent}.
148+ * <p>
149+ * The default background mode is {@link BackgroundMode#opaque}.
150+ * <p>
151+ * Choosing a background mode of {@link BackgroundMode#transparent} will configure the inner
152+ * {@link FlutterView} of this {@code FlutterActivity} to be configured with a
153+ * {@link FlutterTextureView} to support transparency. This choice has a non-trivial performance
154+ * impact. A transparent background should only be used if it is necessary for the app design
155+ * being implemented.
156+ * <p>
157+ * A {@code FlutterActivity} that is configured with a background mode of
158+ * {@link BackgroundMode#transparent} must have a theme applied to it that includes the
159+ * following property: {@code <item name="android:windowIsTranslucent">true</item>}.
160+ */
161+ @ NonNull
162+ public IntentBuilder backgroundMode (@ NonNull BackgroundMode backgroundMode ) {
163+ this .backgroundMode = backgroundMode .name ();
164+ return this ;
165+ }
166+
141167 /**
142168 * Creates and returns an {@link Intent} that will launch a {@code FlutterActivity} with
143169 * the desired configuration.
@@ -146,20 +172,40 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
146172 public Intent build (@ NonNull Context context ) {
147173 return new Intent (context , activityClass )
148174 .putExtra (EXTRA_DART_ENTRYPOINT , dartEntrypoint )
149- .putExtra (EXTRA_INITIAL_ROUTE , initialRoute );
175+ .putExtra (EXTRA_INITIAL_ROUTE , initialRoute )
176+ .putExtra (EXTRA_BACKGROUND_MODE , backgroundMode );
150177 }
151178 }
152179
153180 @ Override
154181 public void onCreate (Bundle savedInstanceState ) {
155182 Log .d (TAG , "onCreate()" );
156183 super .onCreate (savedInstanceState );
184+ configureWindowForTransparency ();
157185 setContentView (createFragmentContainer ());
158186 showCoverView ();
159187 configureStatusBarForFullscreenFlutterExperience ();
160188 ensureFlutterFragmentCreated ();
161189 }
162190
191+ /**
192+ * Sets this {@code Activity}'s {@code Window} background to be transparent, and hides the status
193+ * bar, if this {@code Activity}'s desired {@link BackgroundMode} is {@link BackgroundMode#transparent}.
194+ * <p>
195+ * For {@code Activity} transparency to work as expected, the theme applied to this {@code Activity}
196+ * must include {@code <item name="android:windowIsTranslucent">true</item>}.
197+ */
198+ private void configureWindowForTransparency () {
199+ BackgroundMode backgroundMode = getBackgroundMode ();
200+ if (backgroundMode == BackgroundMode .transparent ) {
201+ getWindow ().setBackgroundDrawable (new ColorDrawable (Color .TRANSPARENT ));
202+ getWindow ().setFlags (
203+ WindowManager .LayoutParams .FLAG_LAYOUT_NO_LIMITS ,
204+ WindowManager .LayoutParams .FLAG_LAYOUT_NO_LIMITS
205+ );
206+ }
207+ }
208+
163209 /**
164210 * Cover all visible {@code Activity} area with a {@code View} that paints everything the same
165211 * color as the {@code Window}.
@@ -170,6 +216,11 @@ public void onCreate(Bundle savedInstanceState) {
170216 * itself transparent.
171217 */
172218 private void showCoverView () {
219+ if (getBackgroundMode () == BackgroundMode .transparent ) {
220+ // Don't display an opaque cover view if the Activity is intended to be transparent.
221+ return ;
222+ }
223+
173224 // Create the coverView.
174225 if (coverView == null ) {
175226 coverView = new View (this );
@@ -210,7 +261,9 @@ private Drawable createCoverViewBackground() {
210261 * for details.
211262 */
212263 private void hideCoverView () {
213- coverView .setVisibility (View .GONE );
264+ if (coverView != null ) {
265+ coverView .setVisibility (View .GONE );
266+ }
214267 }
215268
216269 private void configureStatusBarForFullscreenFlutterExperience () {
@@ -267,13 +320,19 @@ private void ensureFlutterFragmentCreated() {
267320 */
268321 @ NonNull
269322 protected FlutterFragment createFlutterFragment () {
323+ BackgroundMode backgroundMode = getBackgroundMode ();
324+
270325 return new FlutterFragment .Builder ()
271326 .dartEntrypoint (getDartEntrypoint ())
272327 .initialRoute (getInitialRoute ())
273328 .appBundlePath (getAppBundlePath ())
274329 .flutterShellArgs (FlutterShellArgs .fromIntent (getIntent ()))
275- .renderMode (FlutterView .RenderMode .surface )
276- .transparencyMode (FlutterView .TransparencyMode .opaque )
330+ .renderMode (backgroundMode == BackgroundMode .opaque
331+ ? FlutterView .RenderMode .surface
332+ : FlutterView .RenderMode .texture )
333+ .transparencyMode (backgroundMode == BackgroundMode .opaque
334+ ? FlutterView .TransparencyMode .opaque
335+ : FlutterView .TransparencyMode .transparent )
277336 .shouldAttachEngineToActivity (shouldAttachEngineToActivity ())
278337 .build ();
279338 }
@@ -432,6 +491,19 @@ protected String getInitialRoute() {
432491 }
433492 }
434493
494+ /**
495+ * The desired window background mode of this {@code Activity}, which defaults to
496+ * {@link BackgroundMode#opaque}.
497+ */
498+ @ NonNull
499+ protected BackgroundMode getBackgroundMode () {
500+ if (getIntent ().hasExtra (EXTRA_BACKGROUND_MODE )) {
501+ return BackgroundMode .valueOf (getIntent ().getStringExtra (EXTRA_BACKGROUND_MODE ));
502+ } else {
503+ return BackgroundMode .opaque ;
504+ }
505+ }
506+
435507 /**
436508 * Returns true if Flutter is running in "debug mode", and false otherwise.
437509 * <p>
@@ -445,4 +517,14 @@ private boolean isDebuggable() {
445517 public void onFirstFrameRendered () {
446518 hideCoverView ();
447519 }
520+
521+ /**
522+ * The mode of the background of a {@code FlutterActivity}, either opaque or transparent.
523+ */
524+ public enum BackgroundMode {
525+ /** Indicates a FlutterActivity with an opaque background. This is the default. */
526+ opaque ,
527+ /** Indicates a FlutterActivity with a transparent background. */
528+ transparent
529+ }
448530}
0 commit comments