diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 5ec47c5831a09..acdfc1ca3aca5 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -1407,6 +1407,19 @@ public boolean shouldDispatchAppLifecycleState() { return true; } + /** + * Whether to automatically attach the {@link FlutterView} to the engine. + * + *
Returning {@code false} means that the task of attaching the {@link FlutterView} to the + * engine will be taken over by the host application. + * + *
Defaults to {@code true}. + */ + @Override + public boolean attachToEngineAutomatically() { + return true; + } + @Override public boolean popSystemNavigator() { // Hook for subclass. No-op if returns false. diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 1e59b74e03bbb..9596dc6cc4a26 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -390,8 +390,10 @@ View onCreateView( // Add listener to be notified when Flutter renders its first frame. flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener); - Log.v(TAG, "Attaching FlutterEngine to FlutterView."); - flutterView.attachToFlutterEngine(flutterEngine); + if (host.attachToEngineAutomatically()) { + Log.v(TAG, "Attaching FlutterEngine to FlutterView."); + flutterView.attachToFlutterEngine(flutterEngine); + } flutterView.setId(flutterViewId); if (shouldDelayFirstAndroidViewDraw) { @@ -1171,5 +1173,17 @@ PlatformPlugin providePlatformPlugin( * while return {@code true} means the engine dispatches these events. */ boolean shouldDispatchAppLifecycleState(); + + /** + * Whether to automatically attach the {@link FlutterView} to the engine. + * + *
In the add-to-app scenario where multiple {@link FlutterView} share the same {@link + * FlutterEngine}, the host application desires to determine the timing of attaching the {@link + * FlutterView} to the engine, for example, during the {@code onResume} instead of the {@code + * onCreateView}. + * + *
Defaults to {@code true}. + */ + boolean attachToEngineAutomatically(); } } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index b521c73882f0d..586d60c9c6980 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -1645,6 +1645,19 @@ public boolean shouldDispatchAppLifecycleState() { return true; } + /** + * Whether to automatically attach the {@link FlutterView} to the engine. + * + *
Returning {@code false} means that the task of attaching the {@link FlutterView} to the + * engine will be taken over by the host application. + * + *
Defaults to {@code true}. + */ + @Override + public boolean attachToEngineAutomatically() { + return true; + } + /** * {@inheritDoc} * diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 3cb5d2dd6665f..86b5acfac06b9 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -2,9 +2,11 @@ import static android.content.ComponentCallbacks2.*; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; @@ -88,6 +90,7 @@ public void setup() { when(mockHost.shouldHandleDeeplinking()).thenReturn(false); when(mockHost.shouldDestroyEngineWithHost()).thenReturn(true); when(mockHost.shouldDispatchAppLifecycleState()).thenReturn(true); + when(mockHost.attachToEngineAutomatically()).thenReturn(true); } @Test @@ -1241,6 +1244,31 @@ public void usesFlutterEngineGroup() { assertEquals(engineUnderTest, mockFlutterEngine); } + @Test + public void itDoesAttachFlutterViewToEngine() { + // ---- Test setup ---- + // Create the real object that we're testing. + FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost); + delegate.onAttach(ctx); + delegate.onCreateView(null, null, null, 0, true); + + // --- Execute the behavior under test --- + assertTrue(delegate.flutterView.isAttachedToFlutterEngine()); + } + + @Test + public void itDoesNotAttachFlutterViewToEngine() { + // ---- Test setup ---- + // Create the real object that we're testing. + when(mockHost.attachToEngineAutomatically()).thenReturn(false); + FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost); + delegate.onAttach(ctx); + delegate.onCreateView(null, null, null, 0, true); + + // --- Execute the behavior under test --- + assertFalse(delegate.flutterView.isAttachedToFlutterEngine()); + } + /** * Creates a mock {@link io.flutter.embedding.engine.FlutterEngine}. * diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java index d569c64c36203..64cf15d0404d2 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java @@ -389,6 +389,11 @@ public boolean shouldDispatchAppLifecycleState() { return true; } + @Override + public boolean attachToEngineAutomatically() { + return true; + } + @Override public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) {}