Skip to content

Commit

Permalink
Android Embedding PR27: Fix SurfaceView flicker in Fragment transacti…
Browse files Browse the repository at this point in the history
…ons (flutter#8504)
  • Loading branch information
matthew-carroll authored Apr 9, 2019
1 parent 86ab45c commit 5279132
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ protected FlutterFragment createFlutterFragment() {
.appBundlePath(getAppBundlePath())
.flutterShellArgs(FlutterShellArgs.fromIntent(getIntent()))
.renderMode(FlutterView.RenderMode.surface)
.transparencyMode(FlutterView.TransparencyMode.opaque)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class FlutterFragment extends Fragment {
private static final String ARG_APP_BUNDLE_PATH = "app_bundle_path";
private static final String ARG_FLUTTER_INITIALIZATION_ARGS = "initialization_args";
private static final String ARG_FLUTTERVIEW_RENDER_MODE = "flutterview_render_mode";
private static final String ARG_FLUTTERVIEW_TRANSPARENCY_MODE = "flutterview_transparency_mode";

/**
* Builder that creates a new {@code FlutterFragment} with {@code arguments} that correspond
Expand All @@ -82,6 +83,7 @@ public static class Builder {
private String appBundlePath = null;
private FlutterShellArgs shellArgs = null;
private FlutterView.RenderMode renderMode = FlutterView.RenderMode.surface;
private FlutterView.TransparencyMode transparencyMode = FlutterView.TransparencyMode.transparent;

/**
* The name of the initial Dart method to invoke, defaults to "main".
Expand Down Expand Up @@ -135,6 +137,18 @@ public Builder renderMode(@NonNull FlutterView.RenderMode renderMode) {
return this;
}

/**
* Support a {@link FlutterView.TransparencyMode#transparent} background within {@link FlutterView},
* or force an {@link FlutterView.TransparencyMode#opaque} background.
* <p>
* See {@link FlutterView.TransparencyMode} for implications of this selection.
*/
@NonNull
public Builder transparencyMode(@NonNull FlutterView.TransparencyMode transparencyMode) {
this.transparencyMode = transparencyMode;
return this;
}

@NonNull
public FlutterFragment build() {
FlutterFragment frag = new FlutterFragment();
Expand All @@ -144,7 +158,8 @@ public FlutterFragment build() {
initialRoute,
appBundlePath,
shellArgs,
renderMode
renderMode,
transparencyMode
);
frag.setArguments(args);

Expand Down Expand Up @@ -194,7 +209,8 @@ protected static Bundle createArgsBundle(@Nullable String dartEntrypoint,
@Nullable String initialRoute,
@Nullable String appBundlePath,
@Nullable FlutterShellArgs flutterShellArgs,
@Nullable FlutterView.RenderMode renderMode) {
@Nullable FlutterView.RenderMode renderMode,
@Nullable FlutterView.TransparencyMode transparencyMode) {
Bundle args = new Bundle();
args.putString(ARG_INITIAL_ROUTE, initialRoute);
args.putString(ARG_APP_BUNDLE_PATH, appBundlePath);
Expand All @@ -204,6 +220,7 @@ protected static Bundle createArgsBundle(@Nullable String dartEntrypoint,
args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, flutterShellArgs.toArray());
}
args.putString(ARG_FLUTTERVIEW_RENDER_MODE, renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name());
args.putString(ARG_FLUTTERVIEW_TRANSPARENCY_MODE, transparencyMode != null ? transparencyMode.name() : FlutterView.TransparencyMode.transparent.name());
return args;
}

Expand Down Expand Up @@ -315,7 +332,7 @@ protected void setupFlutterEngine() {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
flutterView = new FlutterView(getContext(), getRenderMode());
flutterView = new FlutterView(getContext(), getRenderMode(), getTransparencyMode());
flutterView.addOnFirstFrameRenderedListener(onFirstFrameRenderedListener);

// We post() the code that attaches the FlutterEngine to our FlutterView because there is
Expand Down Expand Up @@ -413,6 +430,18 @@ protected FlutterView.RenderMode getRenderMode() {
return FlutterView.RenderMode.valueOf(renderModeName);
}

/**
* Returns the desired {@link FlutterView.TransparencyMode} for the {@link FlutterView} displayed in
* this {@code FlutterFragment}.
* <p>
* Defaults to {@link FlutterView.TransparencyMode#transparent}.
*/
@NonNull
protected FlutterView.TransparencyMode getTransparencyMode() {
String transparencyModeName = getArguments().getString(ARG_FLUTTERVIEW_TRANSPARENCY_MODE, FlutterView.TransparencyMode.transparent.name());
return FlutterView.TransparencyMode.valueOf(transparencyModeName);
}

// TODO(mattcarroll): determine why this can't be in onResume(). Comment reason, or move if possible.
public void onPostResume() {
Log.d(TAG, "onPostResume()");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package io.flutter.embedding.android;

import android.content.Context;
import android.graphics.PixelFormat;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
Expand Down Expand Up @@ -36,6 +37,7 @@
public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.RenderSurface {
private static final String TAG = "FlutterSurfaceView";

private final boolean renderTransparently;
private boolean isSurfaceAvailableForRendering = false;
private boolean isAttachedToFlutterRenderer = false;
@Nullable
Expand Down Expand Up @@ -80,19 +82,39 @@ public void surfaceDestroyed(SurfaceHolder holder) {
/**
* Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes.
*/
public FlutterSurfaceView(Context context) {
this(context, null);
public FlutterSurfaceView(@NonNull Context context) {
this(context, null, false);
}

/**
* Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes, and
* with control over whether or not this {@code FlutterSurfaceView} renders with transparency.
*/
public FlutterSurfaceView(@NonNull Context context, boolean renderTransparently) {
this(context, null, renderTransparently);
}

/**
* Constructs a {@code FlutterSurfaceView} in an XML-inflation-compliant manner.
*/
public FlutterSurfaceView(Context context, AttributeSet attrs) {
public FlutterSurfaceView(@NonNull Context context, @NonNull AttributeSet attrs) {
this(context, attrs, false);
}

private FlutterSurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, boolean renderTransparently) {
super(context, attrs);
this.renderTransparently = renderTransparently;
init();
}

private void init() {
// If transparency is desired then we'll enable a transparent pixel format and place
// our Window above everything else to get transparent background rendering.
if (renderTransparently) {
getHolder().setFormat(PixelFormat.TRANSPARENT);
setZOrderOnTop(true);
}

// Grab a reference to our underlying Surface and register callbacks with that Surface so we
// can monitor changes and forward those changes on to native Flutter code.
getHolder().addCallback(surfaceCallback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public class FlutterView extends FrameLayout {
// Behavior configuration of this FlutterView.
@NonNull
private RenderMode renderMode;
@Nullable
private TransparencyMode transparencyMode;

// Internal view hierarchy references.
@Nullable
Expand Down Expand Up @@ -96,20 +98,41 @@ public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTou
};

/**
* Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes.
*
* {@link #renderMode} defaults to {@link RenderMode#surface}.
* Constructs a {@code FlutterView} programmatically, without any XML attributes.
* <p>
* <ul>
* <li>{@link #renderMode} defaults to {@link RenderMode#surface}.</li>
* <li>{@link #transparencyMode} defaults to {@link TransparencyMode#opaque}.</li>
* </ul>
*/
public FlutterView(@NonNull Context context) {
this(context, null, null);
this(context, null, null, null);
}

/**
* Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes,
* Constructs a {@code FlutterView} programmatically, without any XML attributes,
* and allows selection of a {@link #renderMode}.
* <p>
* {@link #transparencyMode} defaults to {@link TransparencyMode#opaque}.
*/
public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) {
this(context, null, renderMode);
this(context, null, renderMode, null);
}

/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes,
* assumes the use of {@link RenderMode#surface}, and allows selection of a {@link #transparencyMode}.
*/
public FlutterView(@NonNull Context context, @NonNull TransparencyMode transparencyMode) {
this(context, null, RenderMode.surface, transparencyMode);
}

/**
* Constructs a {@code FlutterView} programmatically, without any XML attributes, and allows
* a selection of {@link #renderMode} and {@link #transparencyMode}.
*/
public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode, @NonNull TransparencyMode transparencyMode) {
this(context, null, renderMode, transparencyMode);
}

/**
Expand All @@ -118,13 +141,14 @@ public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) {
* // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr
*/
public FlutterView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, null);
this(context, attrs, null, null);
}

private FlutterView(@NonNull Context context, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode) {
private FlutterView(@NonNull Context context, @Nullable AttributeSet attrs, @Nullable RenderMode renderMode, @Nullable TransparencyMode transparencyMode) {
super(context, attrs);

this.renderMode = renderMode == null ? RenderMode.surface : renderMode;
this.transparencyMode = transparencyMode != null ? transparencyMode : TransparencyMode.opaque;

init();
}
Expand All @@ -135,7 +159,7 @@ private void init() {
switch (renderMode) {
case surface:
Log.d(TAG, "Internally creating a FlutterSurfaceView.");
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext());
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext(), transparencyMode == TransparencyMode.transparent);
renderSurface = flutterSurfaceView;
addView(flutterSurfaceView);
break;
Expand Down Expand Up @@ -591,4 +615,39 @@ public enum RenderMode {
*/
texture
}

/**
* Transparency mode for a {@code FlutterView}.
* <p>
* {@code TransparencyMode} impacts the visual behavior and performance of a {@link FlutterSurfaceView},
* which is displayed when a {@code FlutterView} uses {@link RenderMode#surface}.
* <p>
* {@code TransparencyMode} does not impact {@link FlutterTextureView}, which is displayed when
* a {@code FlutterView} uses {@link RenderMode#texture}, because a {@link FlutterTextureView}
* automatically comes with transparency.
*/
public enum TransparencyMode {
/**
* Renders a {@code FlutterView} without any transparency. This affects {@code FlutterView}s in
* {@link RenderMode#surface} by introducing a base color of black, and places the
* {@link FlutterSurfaceView}'s {@code Window} behind all other content.
* <p>
* In {@link RenderMode#surface}, this mode is the most performant and is a good choice for
* fullscreen Flutter UIs that will not undergo {@code Fragment} transactions. If this mode is
* used within a {@code Fragment}, and that {@code Fragment} is replaced by another one, a
* brief black flicker may be visible during the switch.
*/
opaque,
/**
* Renders a {@code FlutterView} with transparency. This affects {@code FlutterView}s in
* {@link RenderMode#surface} by allowing background transparency, and places the
* {@link FlutterSurfaceView}'s {@code Window} on top of all other content.
* <p>
* In {@link RenderMode#surface}, this mode is less performant than {@link #opaque}, but this
* mode avoids the black flicker problem that {@link #opaque} has when going through
* {@code Fragment} transactions. Consider using this {@code TransparencyMode} if you intend to
* switch {@code Fragment}s at runtime that contain a Flutter UI.
*/
transparent
}
}

0 comments on commit 5279132

Please sign in to comment.