@@ -68,6 +68,7 @@ public class FlutterView extends FrameLayout {
6868 // Internal view hierarchy references.
6969 @ Nullable
7070 private FlutterRenderer .RenderSurface renderSurface ;
71+ private boolean didRenderFirstFrame ;
7172
7273 // Connections to a Flutter execution context.
7374 @ Nullable
@@ -97,6 +98,13 @@ public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTou
9798 }
9899 };
99100
101+ private final OnFirstFrameRenderedListener onFirstFrameRenderedListener = new OnFirstFrameRenderedListener () {
102+ @ Override
103+ public void onFirstFrameRendered () {
104+ didRenderFirstFrame = true ;
105+ }
106+ };
107+
100108 /**
101109 * Constructs a {@code FlutterView} programmatically, without any XML attributes.
102110 * <p>
@@ -171,11 +179,34 @@ private void init() {
171179 break ;
172180 }
173181
182+ // Register a listener for the first frame render event to set didRenderFirstFrame.
183+ renderSurface .addOnFirstFrameRenderedListener (onFirstFrameRenderedListener );
184+
174185 // FlutterView needs to be focusable so that the InputMethodManager can interact with it.
175186 setFocusable (true );
176187 setFocusableInTouchMode (true );
177188 }
178189
190+ /**
191+ * Returns true if an attached {@link FlutterEngine} has rendered at least 1 frame to this
192+ * {@code FlutterView}.
193+ * <p>
194+ * Returns false if no {@link FlutterEngine} is attached.
195+ * <p>
196+ * This flag is specific to a given {@link FlutterEngine}. The following hypothetical timeline
197+ * demonstrates how this flag changes over time.
198+ * <ol>
199+ * <li>{@code flutterEngineA} is attached to this {@code FlutterView}: returns false</li>
200+ * <li>{@code flutterEngineA} renders its first frame to this {@code FlutterView}: returns true</li>
201+ * <li>{@code flutterEngineA} is detached from this {@code FlutterView}: returns false</li>
202+ * <li>{@code flutterEngineB} is attached to this {@code FlutterView}: returns false</li>
203+ * <li>{@code flutterEngineB} renders its first frame to this {@code FlutterView}: returns true</li>
204+ * </ol>
205+ */
206+ public boolean hasRenderedFirstFrame () {
207+ return didRenderFirstFrame ;
208+ }
209+
179210 /**
180211 * Adds the given {@code listener} to this {@code FlutterView}, to be notified upon Flutter's
181212 * first rendered frame.
@@ -471,6 +502,7 @@ public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
471502 this .flutterEngine = flutterEngine ;
472503
473504 // Instruct our FlutterRenderer that we are now its designated RenderSurface.
505+ didRenderFirstFrame = false ;
474506 this .flutterEngine .getRenderer ().attachToRenderSurface (renderSurface );
475507
476508 // Initialize various components that know how to process Android View I/O
@@ -536,6 +568,7 @@ public void detachFromFlutterEngine() {
536568 textInputPlugin .getInputMethodManager ().restartInput (this );
537569
538570 // Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
571+ didRenderFirstFrame = false ;
539572 flutterEngine .getRenderer ().detachFromRenderSurface ();
540573 flutterEngine = null ;
541574
0 commit comments