Let’s see what it looks like:

access

As for the access method, I refer to the official introduction document. What I try here is the Android access method, which is relatively smooth.

1. Execute the flutter create -t module my_flutter command in your Android project directory. When the flutter is complete, it should look something like this.

2, Open your Android project setting.gradle file, line it up, and add:

setBinding(new Binding([gradle: this]))                                 // new
evaluate(new File(                                                      // new
        settingsDir.parentFile,                                               // new
        'tip_flutter/.android/include_flutter.groovy'                          // new
))
Copy the code

These lines of code mean to introduce the module you just created into the Android project as an Android module. 3. Finally, open build.gradle in your app directory and add it to your dependencies

 //flutter
    implementation project(':flutter')
Copy the code

Ok, by synchronizing, you can introduce Flutter into your existing Android project. The steps for ios will not be described. Refer to the documentation.

Have a problem

Of course, the process I described above was pretty smooth, but my access process wasn’t so smooth, and I tried everything. 1, For example, what if the flutter create -t module my_flutter is not in the same directory as the Android project

setBinding(new Binding([gradle: this]))                                 // new
evaluate(new File(                                                      // new
        settingsDir.parentFile,                                               // new
        'Your project directory name /tip_flutter/.android/include_flutter. Groovy'                          // new
))
Copy the code

That’s ok, but it doesn’t look good. Ios introduces the Flutter module to your Android project, so it’s more elegant to be independent of the Android project and keep things decoupled. Some of you may have to configure buildTypes that will fail to synchronize. The solution is to modify your build.gradle folder in the.android folder. Keep it consistent with your app’s buildTypes.

3. Debug runs well, release is down, this must be a confusion problem, you can refer to it

-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.**  { *; }
-keep class io.flutter.util.**  { *; }
-keep class io.flutter.view.**  { *; }
-keep class io.flutter.**  { *; }
-keep class io.flutter.plugins.**  { *; }
Copy the code

Libflutter. So is a library that has not been loaded into the corresponding CPU architecture

ndk {
                abiFilters "armeabi"."armeabi-v7a". Add everything. Here, your bag is too big for you. }Copy the code

In summary, in fact, the access process is not so smooth, ha ha, or quite pits, but generally can find a solution.

Use the pose

Create a flutterView directly createView and add it to your layout, route1 in this case.

View flutterView = Flutter.createView(
      MainActivity.this,
      getLifecycle(),
      "route1"
    );
    FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600.800);
    layout.leftMargin = 100;
    layout.topMargin = 200;
    addContentView(flutterView, layout);
Copy the code

This is actually a way for flutter to manage pages. Routing is used to configure a route for each page. Route1 is the name of the page, so you can jump directly to each page named Route1. Dart void main() => runApp(new MyApp()); dart void main() => runApp(new MyApp()); It is understandable that he is the root. 2. Use fragments

FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
   tx.replace(R.id.someContainer, Flutter.createFragment("route1"));
   tx.commit();
Copy the code

There are two ways to apply the Flutter module. In fact, if you are smart enough, there is only one of them. Both add a flutterView to the native, and if you look at the source code, you will find that the flutterView is actually a SurfaceView. It just implements a special interface, BinaryMessenger.

The flutter calls the native module

Opening a page for a FLUTTER implementation is very simple, but where does the data presented inside come from? Nativie sends data to the flutter end in one of two ways. B. The flutter end requests data from the native end. Here, we first look at the second method, the flutter side asks the native side for data, because the second method is mentioned more officially. Usually, the native method of flutter invocation is done through the MethodChannel. Let’s learn how to do this first.

MethodChannel(getFlutterView(), "app.channel.shared.data")
      .setMethodCallHandler(MethodChannel.MethodCallHandler() {
        @Override
        public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
          if (methodCall.method.contentEquals("getSharedText")) {
            result.success("some thing want to send to flutter");
            sharedText = null; }}});Copy the code

Let’s take a look at the source code definition. Here’s the excerpt

public final class MethodChannel {
    private static final String TAG = "MethodChannel#";
    private final BinaryMessenger messenger;
    private final String name;
    private final MethodCodec codec;

    public MethodChannel(BinaryMessenger messenger, String name) {
        this(messenger, name, StandardMethodCodec.INSTANCE);
    }
Copy the code

As mentioned earlier, the first argument is BinaryMessenger, and since FlutterView implements this interface, the official demo passes getFlutterView().

So obviously, there are two ways that we talked about using postures. The first way is that the FlutterView is obviously out there, you can easily grab it and start a MethodChannel. What about the second way? Wait, let’s see what a FlutterFragment is.

public class FlutterFragment extends Fragment {
  public static final String ARG_ROUTE = "route";
  private String mRoute = "/";

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(getArguments() ! = null) { mRoute = getArguments().getString(ARG_ROUTE); } } @Override public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) { super.onInflate(context, attrs, savedInstanceState); } @Override public FlutterView onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {returnFlutter.createView(getActivity(), getLifecycle(), mRoute); }}Copy the code

OnCreateView returns a FlutterView, as mentioned earlier. In fact, the native reference to the Flutter module simply adds the FlutterView to the existing layout.

Ok, so now that I’ve done both, I’m going to show you how to do it in the second way.

public class FlutterBaseFragment extends FlutterFragment {

    private static final String METHOD_CHANNEL = "tip.flutter.io/method";

    public static FlutterBaseFragment newInstance(String route) {

        Bundle args = new Bundle();
        args.putString(ARG_ROUTE, route);
        FlutterBaseFragment fragment = new FlutterBaseFragment();
        fragment.setArguments(args);
        return fragment;
    }
    @SuppressWarnings("unchecked")
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        new MethodChannel((FlutterView) getView(), METHOD_CHANNEL).setMethodCallHandler(
                new MethodChannel.MethodCallHandler() {
                    @Override
                    public void onMethodCall(MethodCall call, final MethodChannel.Result result) {
                        if (call.method.equals("getBatteryLevel")) {
                            int batteryLevel = getBatteryLevel();

                            if(batteryLevel ! = -1) { result.success(batteryLevel); }else {
                                result.error("UNAVAILABLE"."Battery level not available.", null); }}else{ result.notImplemented(); }}}); } private intgetBatteryLevel() {...}
}
Copy the code

That’s the native implementation part. Then, how does the flutter side call?

class _MyHomePageState extends State<MyHomePage> {
  static const platform = const MethodChannel('tip.flutter.io/method');
  String _batteryLevel = 'Unknown battery level.';

  Future<Null> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }
Copy the code

Familiar? This is the official demo.

Isn’t there a requirement for data sent to flutter via MethodChannel? Can any type of data be sent? Sorry, no, for example, your custom class is obviously not allowed. He supports only the following types:

So, what if we want to send custom type data?

Obviously, we need to convert to the type supported by dart. You might think of Object->Json, and then, on the flutter side, change to a Json Object. There are other ways, for example, if you happen to be using protobuf, it would be nice to just pass byte[], or you can implement custom protocols if you have enough time. In general, the data passed should be of a type that is recognizable across platforms.

Sends data to the flutter native

It feels strange to send data to a flutter natively. So, what is the scenario, for example, a change in the charging status of a cell phone? How does this change inform the flutter?

In this case, you need to use EventChannel, which is actually not that different from how MethodChannel sends data over, except that we understand that the flutter side requests the data and gets it, EventChannel might be understood as being actively pushed from native.

new EventChannel((FlutterView) getView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object arguments, final EventChannel.EventSink events) {
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run(a) {
                        events.success("Current time in milliseconds" + System.currentTimeMillis() / 1000); }},1000.1000);
            }

            @Override
            public void onCancel(Object arguments) {... }});Copy the code

How does the code on the flutter side receive the data pushed by this EventChannel?

Future<Null> _lisEvent() async {
    String eventStr;
    try {
      _streamSubscription =
          eventChannel.receiveBroadcastStream().listen((data) {
        eventStr = 'event get data is $data ';
        setState(() {
          _eventStr = eventStr;
        });
      });
    } on PlatformException catch (e) {
      eventStr = "event get data err: '${e.message}'."; setState(() { _eventStr = eventStr; }); }}Copy the code

conclusion

The pages made by the native pull up of the flutter, the native module that the flutter calls, and the native module that pushes data to the flutter are all verified to be OK. Therefore, the way that the flutter connects to the existing APP is feasible. After the flutter is connected, The package size will increase by 5.5m +, mainly because this SO library is needed. It would be nice if it could be obtained from the network, but unfortunately it can only be packaged into APK at present. Together with the files generated by the business, the overall package size increases by about 8M for a simple business.