diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 5bcc91e55ed45..cee5b20b46e8e 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -5,6 +5,7 @@ package io.flutter.embedding.android; import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DART_ENTRYPOINT_META_DATA_KEY; +import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DART_ENTRYPOINT_URI_META_DATA_KEY; import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_BACKGROUND_MODE; import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_DART_ENTRYPOINT; import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_INITIAL_ROUTE; @@ -824,6 +825,32 @@ public String getDartEntrypointFunctionName() { } } + /** + * The Dart library URI for the entrypoint that will be executed as soon as the Dart snapshot is + * loaded. + * + *
Example value: "package:foo/bar.dart" + * + *
This preference can be controlled by setting a {@code A value of null means use the default root library.
+ *
+ * Subclasses may override this method to directly control the Dart entrypoint uri.
+ */
+ @Nullable
+ public String getDartEntrypointLibraryUri() {
+ try {
+ Bundle metaData = getMetaData();
+ String desiredDartLibraryUri =
+ metaData != null ? metaData.getString(DART_ENTRYPOINT_URI_META_DATA_KEY) : null;
+ return desiredDartLibraryUri;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
/**
* The initial route that a Flutter app will render upon loading and executing its Dart code.
*
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
index d8b3fc809bea5..8665af6185743 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
@@ -417,12 +417,16 @@ private void doInitialFlutterViewRun() {
initialRoute = DEFAULT_INITIAL_ROUTE;
}
}
+ @Nullable String libraryUri = host.getDartEntrypointLibraryUri();
Log.v(
TAG,
"Executing Dart entrypoint: "
- + host.getDartEntrypointFunctionName()
- + ", and sending initial route: "
- + initialRoute);
+ + host.getDartEntrypointFunctionName()
+ + ", library uri: "
+ + libraryUri
+ == null
+ ? "\"\""
+ : libraryUri + ", and sending initial route: " + initialRoute);
// The engine needs to receive the Flutter app's initial route before executing any
// Dart code to ensure that the initial route arrives in time to be applied.
@@ -435,8 +439,11 @@ private void doInitialFlutterViewRun() {
// Configure the Dart entrypoint and execute it.
DartExecutor.DartEntrypoint entrypoint =
- new DartExecutor.DartEntrypoint(
- appBundlePathOverride, host.getDartEntrypointFunctionName());
+ libraryUri == null
+ ? new DartExecutor.DartEntrypoint(
+ appBundlePathOverride, host.getDartEntrypointFunctionName())
+ : new DartExecutor.DartEntrypoint(
+ appBundlePathOverride, libraryUri, host.getDartEntrypointFunctionName());
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
}
@@ -916,6 +923,14 @@ private void ensureAlive() {
@NonNull
String getDartEntrypointFunctionName();
+ /**
+ * Returns the URI of the Dart library which contains the entrypoint method (example
+ * "package:foo_package/main.dart"). If null, this will default to the same library as the
+ * `main()` function in the Dart program.
+ */
+ @Nullable
+ String getDartEntrypointLibraryUri();
+
/** Returns the path to the app bundle where the Dart code exists. */
@NonNull
String getAppBundlePath();
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java
index a92e342333765..c6aa0aa242f69 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java
@@ -11,6 +11,7 @@
public class FlutterActivityLaunchConfigs {
// Meta-data arguments, processed from manifest XML.
/* package */ static final String DART_ENTRYPOINT_META_DATA_KEY = "io.flutter.Entrypoint";
+ /* package */ static final String DART_ENTRYPOINT_URI_META_DATA_KEY = "io.flutter.EntrypointUri";
/* package */ static final String INITIAL_ROUTE_META_DATA_KEY = "io.flutter.InitialRoute";
/* package */ static final String SPLASH_SCREEN_META_DATA_KEY =
"io.flutter.embedding.android.SplashScreenDrawable";
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
index 9d34a29c67e6b..910826c960348 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
@@ -105,6 +105,8 @@ public class FlutterFragment extends Fragment
/** The Dart entrypoint method name that is executed upon initialization. */
protected static final String ARG_DART_ENTRYPOINT = "dart_entrypoint";
+ /** The Dart entrypoint method's URI that is executed upon initialization. */
+ protected static final String ARG_DART_ENTRYPOINT_URI = "dart_entrypoint_uri";
/** Initial Flutter route that is rendered in a Navigator widget. */
protected static final String ARG_INITIAL_ROUTE = "initial_route";
/** Whether the activity delegate should handle the deeplinking request. */
@@ -223,6 +225,7 @@ public static NewEngineFragmentBuilder withNewEngine() {
public static class NewEngineFragmentBuilder {
private final Class extends FlutterFragment> fragmentClass;
private String dartEntrypoint = "main";
+ private String dartLibraryUri = null;
private String initialRoute = "/";
private boolean handleDeeplinking = false;
private String appBundlePath = null;
@@ -256,6 +259,12 @@ public NewEngineFragmentBuilder dartEntrypoint(@NonNull String dartEntrypoint) {
return this;
}
+ @NonNull
+ public NewEngineFragmentBuilder dartLibraryUri(@NonNull String dartLibraryUri) {
+ this.dartLibraryUri = dartLibraryUri;
+ return this;
+ }
+
/**
* The initial route that a Flutter app will render in this {@link FlutterFragment}, defaults to
* "/".
@@ -410,6 +419,7 @@ protected Bundle createArgs() {
args.putBoolean(ARG_HANDLE_DEEPLINKING, handleDeeplinking);
args.putString(ARG_APP_BUNDLE_PATH, appBundlePath);
args.putString(ARG_DART_ENTRYPOINT, dartEntrypoint);
+ args.putString(ARG_DART_ENTRYPOINT_URI, dartLibraryUri);
// TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of
// conflating.
if (null != shellArgs) {
@@ -1026,6 +1036,20 @@ public String getDartEntrypointFunctionName() {
return getArguments().getString(ARG_DART_ENTRYPOINT, "main");
}
+ /**
+ * Returns the library URI of the Dart method that this {@code FlutterFragment} should execute to
+ * start a Flutter app.
+ *
+ * Defaults to null (example value: "package:foo/bar.dart").
+ *
+ * Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
+ */
+ @Override
+ @Nullable
+ public String getDartEntrypointLibraryUri() {
+ return getArguments().getString(ARG_DART_ENTRYPOINT_URI);
+ }
+
/**
* A custom path to the bundle that contains this Flutter app's resources, e.g., Dart code
* snapshots.
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 d7b90c0a8c867..43414b4ff65d7 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java
@@ -320,6 +320,25 @@ public void itExecutesDartEntrypointProvidedByHost() {
verify(mockFlutterEngine.getDartExecutor(), times(1)).executeDartEntrypoint(eq(dartEntrypoint));
}
+ @Test
+ public void itExecutesDartLibraryUriProvidedByHost() {
+ when(mockHost.getAppBundlePath()).thenReturn("/my/bundle/path");
+ when(mockHost.getDartEntrypointFunctionName()).thenReturn("myEntrypoint");
+ when(mockHost.getDartEntrypointLibraryUri()).thenReturn("package:foo/bar.dart");
+
+ DartExecutor.DartEntrypoint expectedEntrypoint =
+ new DartExecutor.DartEntrypoint("/my/bundle/path", "package:foo/bar.dart", "myEntrypoint");
+
+ FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
+
+ delegate.onAttach(RuntimeEnvironment.application);
+ delegate.onCreateView(null, null, null, 0, true);
+ delegate.onStart();
+
+ verify(mockFlutterEngine.getDartExecutor(), times(1))
+ .executeDartEntrypoint(eq(expectedEntrypoint));
+ }
+
@Test
public void itUsesDefaultFlutterLoaderAppBundlePathWhenUnspecified() {
// ---- Test setup ----
diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java
index f78c5e93ab89f..265cdc17a28bb 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java
@@ -95,6 +95,7 @@ public void itCreatesDefaultIntentWithExpectedDefaults() {
flutterActivity.setDelegate(new FlutterActivityAndFragmentDelegate(flutterActivity));
assertEquals("main", flutterActivity.getDartEntrypointFunctionName());
+ assertNull(flutterActivity.getDartEntrypointLibraryUri());
assertEquals("/", flutterActivity.getInitialRoute());
assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray());
assertTrue(flutterActivity.shouldAttachEngineToActivity());
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 331906e473f43..3586339d8b99e 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java
@@ -305,6 +305,12 @@ public String getDartEntrypointFunctionName() {
return "main";
}
+ @Nullable
+ @Override
+ public String getDartEntrypointLibraryUri() {
+ return null;
+ }
+
@NonNull
@Override
public String getAppBundlePath() {
diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java
index 2f3454ff76fe4..ea8b9d0acb29f 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java
@@ -38,6 +38,7 @@ public void itCreatesDefaultFragmentWithExpectedDefaults() {
fragment.setDelegate(new FlutterActivityAndFragmentDelegate(fragment));
assertEquals("main", fragment.getDartEntrypointFunctionName());
+ assertNull(fragment.getDartEntrypointLibraryUri());
assertEquals("/", fragment.getInitialRoute());
assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
assertTrue(fragment.shouldAttachEngineToActivity());
@@ -54,6 +55,7 @@ public void itCreatesNewEngineFragmentWithRequestedSettings() {
FlutterFragment fragment =
FlutterFragment.withNewEngine()
.dartEntrypoint("custom_entrypoint")
+ .dartLibraryUri("package:foo/bar.dart")
.initialRoute("/custom/route")
.shouldAttachEngineToActivity(false)
.handleDeeplinking(true)
@@ -63,6 +65,7 @@ public void itCreatesNewEngineFragmentWithRequestedSettings() {
fragment.setDelegate(new FlutterActivityAndFragmentDelegate(fragment));
assertEquals("custom_entrypoint", fragment.getDartEntrypointFunctionName());
+ assertEquals("package:foo/bar.dart", fragment.getDartEntrypointLibraryUri());
assertEquals("/custom/route", fragment.getInitialRoute());
assertArrayEquals(new String[] {}, fragment.getFlutterShellArgs().toArray());
assertFalse(fragment.shouldAttachEngineToActivity());
diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h
index ea10495c614c2..5867ea36aa6c6 100644
--- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h
+++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h
@@ -219,8 +219,9 @@ FLUTTER_DARWIN_EXPORT
* FlutterDefaultDartEntrypoint (or nil); this will default to `main()`. If it is not the app's
* main() function, that function must be decorated with `@pragma(vm:entry-point)` to ensure the
* method is not tree-shaken by the Dart compiler.
- * @param uri The URI of the Dart library which contains the entrypoint method. IF nil,
- * this will default to the same library as the `main()` function in the Dart program.
+ * @param uri The URI of the Dart library which contains the entrypoint method
+ * (example "package:foo_package/main.dart"). If nil, this will default to
+ * the same library as the `main()` function in the Dart program.
* @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise.
*/
- (BOOL)runWithEntrypoint:(nullable NSString*)entrypoint libraryURI:(nullable NSString*)uri;
@@ -236,8 +237,9 @@ FLUTTER_DARWIN_EXPORT
* FlutterDefaultDartEntrypoint (or nil); this will default to `main()`. If it is not the app's
* main() function, that function must be decorated with `@pragma(vm:entry-point)` to ensure the
* method is not tree-shaken by the Dart compiler.
- * @param libraryURI The URI of the Dart library which contains the entrypoint method. IF nil,
- * this will default to the same library as the `main()` function in the Dart program.
+ * @param libraryURI The URI of the Dart library which contains the entrypoint
+ * method (example "package:foo_package/main.dart"). If nil, this will
+ * default to the same library as the `main()` function in the Dart program.
* @param initialRoute The name of the initial Flutter `Navigator` `Route` to load. If this is
* FlutterDefaultInitialRoute (or nil), it will default to the "/" route.
* @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise.
@@ -257,8 +259,9 @@ FLUTTER_DARWIN_EXPORT
* FlutterDefaultDartEntrypoint (or nil); this will default to `main()`. If it is not the app's
* main() function, that function must be decorated with `@pragma(vm:entry-point)` to ensure the
* method is not tree-shaken by the Dart compiler.
- * @param libraryURI The URI of the Dart library which contains the entrypoint method. IF nil,
- * this will default to the same library as the `main()` function in the Dart program.
+ * @param libraryURI The URI of the Dart library which contains the entrypoint
+ * method (example "package:foo_package/main.dart"). If nil, this will
+ * default to the same library as the `main()` function in the Dart program.
* @param initialRoute The name of the initial Flutter `Navigator` `Route` to load. If this is
* FlutterDefaultInitialRoute (or nil), it will default to the "/" route.
* @param entrypointArgs Arguments passed as a list of string to Dart's entrypoint function.