flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
164.54k stars 27.13k forks source link

Ensure Android embedding works with Navigation component #45793

Open matthew-carroll opened 4 years ago

matthew-carroll commented 4 years ago

Jetpack has introduced a Navigation component: https://developer.android.com/guide/navigation/navigation-getting-started

This ticket is to ensure that FlutterFragment can work with the Navigation component, and to produce any relevant docs/guides for add-to-app developers to do so.

matthew-carroll commented 4 years ago

CC @xster

maxcorrads commented 4 years ago

Is it currently possible to embed flutter in a native app that is using jetpack navigation?

hm-tamim commented 1 year ago

Any update on this?

hm-tamim commented 1 year ago

I got a workaround for this. This is how you can use FlutterFragment(Add-to-app) with NavGraph/Jetpack Navigation Component:


class FlutterRootFragment : FlutterFragment() {

    @Inject
    lateinit var flutterManager: FlutterManager

    // this does the trick
    override fun getCachedEngineId(): String {
        return FlutterManager.ENGINE_NAME;
    }

    override fun onDestroy() {
        super.onDestroy()
        // clear route when user goes back
        flutterManager.pushRoute("/");
    }
}

Then add FlutterFragment to navigation graph XML.

<fragment
        android:id="@+id/flutterRootFragment"
        android:name=".view.screens.flutter.FlutterRootFragment"
        android:label="FlutterRootFragment" />

To navigate to a Flutter page use a method channel to update the route in your Flutter module, and use NavController

flutterManager.pushRoute("/profile-page")
navController.navigate(R.id.flutterRootFragment)

In Flutter main.dart:

channel.setMethodCallHandler((MethodCall call) async {
     if (call.method == 'pushRoute') {
      appRouter.replaceNamed(call.arguments);
      if (call.arguments == "/") {
        appRouter.popUntilRouteWithPath("/");
      }
    }
  }); 

Hope it helps.

shoaibkhalid-int commented 10 months ago

@hm-tamim above method you provided doesn't work. here's why.

By traditional navigation method, when we create a flutterFragment instance it calls createArgs() below which does some ads-on work like creating input args for newly created flutterFragment but the problem seems like Navigation component has got some different implementation for fragment creation which flutter doesn't support yet.

    protected Bundle createArgs() {
      Bundle args = new Bundle();
      args.putString(ARG_INITIAL_ROUTE, initialRoute);
      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);
      args.putStringArrayList(
          ARG_DART_ENTRYPOINT_ARGS,
          dartEntrypointArgs != null ? new ArrayList(dartEntrypointArgs) : null);
      // TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of
      // conflating.
      if (null != shellArgs) {
        args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, shellArgs.toArray());
      }
      args.putString(
          ARG_FLUTTERVIEW_RENDER_MODE,
          renderMode != null ? renderMode.name() : RenderMode.surface.name());
      args.putString(
          ARG_FLUTTERVIEW_TRANSPARENCY_MODE,
          transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name());
      args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity);
      args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
      args.putBoolean(
          ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, shouldAutomaticallyHandleOnBackPressed);
      args.putBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW, shouldDelayFirstAndroidViewDraw);
      return args;
    }

this is what i found out but not sure if something else is going on under the hood.

vvvictor07 commented 8 months ago

@hm-tamim above method you provided doesn't work. here's why.

By traditional navigation method, when we create a flutterFragment instance it calls createArgs() below which does some ads-on work like creating input args for newly created flutterFragment but the problem seems like Navigation component has got some different implementation for fragment creation which flutter doesn't support yet.

    protected Bundle createArgs() {
      Bundle args = new Bundle();
      args.putString(ARG_INITIAL_ROUTE, initialRoute);
      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);
      args.putStringArrayList(
          ARG_DART_ENTRYPOINT_ARGS,
          dartEntrypointArgs != null ? new ArrayList(dartEntrypointArgs) : null);
      // TODO(mattcarroll): determine if we should have an explicit FlutterTestFragment instead of
      // conflating.
      if (null != shellArgs) {
        args.putStringArray(ARG_FLUTTER_INITIALIZATION_ARGS, shellArgs.toArray());
      }
      args.putString(
          ARG_FLUTTERVIEW_RENDER_MODE,
          renderMode != null ? renderMode.name() : RenderMode.surface.name());
      args.putString(
          ARG_FLUTTERVIEW_TRANSPARENCY_MODE,
          transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name());
      args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity);
      args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true);
      args.putBoolean(
          ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, shouldAutomaticallyHandleOnBackPressed);
      args.putBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW, shouldDelayFirstAndroidViewDraw);
      return args;
    }

this is what i found out but not sure if something else is going on under the hood.

I have faced the same issue, any updates on it?