At the beginning

There are two inevitable problems with FLUTTER development:

  • When native projects migrate to FLUTTER, they need to access flutter in the native project
  • For some mature applications to be used in the Flutter project, there is no way to avoid using native mature libraries such as audio and video

This article will introduce the above two cases respectively

Access the Flutter interface on Android

Flutter needs to be connected as a Module in an Android project

Create a flutter module

Go to the current Android project and run the following command in the root directory:

flutter create -t module my_flutter
Copy the code

Create a flutter module named my_flutter

Run after

cd my_flutter
cd .android/
./gradlew flutter:assembleDebug
Copy the code

Also, make sure to add the following code to your Android project directory app/build.gradle:

android { compileSdkVersion 28 defaultConfig { ... } buildTypes { ... } //flutter related declarations compileOptions {sourceCompatibility 1.8 targetCompatibility 1.8}}Copy the code

Next, add the following code to settings.gradle under the root of your Android project

include ':app'
setBinding(new Binding([gradle: this]))
evaluate(new File(
        rootDir.path + '/my_flutter/.android/include_flutter.groovy'
))
Copy the code

Finally, my_flutter needs to be introduced into app/build.gradle under the Android project

dependencies { ... // Import flutter implementation project(':flutter')}Copy the code

At this point, you are basically ready to access the content of Flutter

If your Android project has migrated to Android X, you may encounter the following problems

This problem is apparently due to the fact that The moudle created by Flutter does not convert to AndroidX because the command to create the moudle does not support AndroidX

Check out the issue below

Generated Flutter Module Files Do Not Use AndroidX

So let’s start solving this problem

Fix problems with androidx

First of all, if your original Android project has migrated to Android X, then grale.properties in the root directory must have the following

# indicates using AndroidX
android.useAndroidX=true
# indicates the migration of third-party libraries to AndroidX
android.enableJetifier=true
Copy the code

Below into the my_flutter directory, in your android project/my_flutter/android/Flutter/build. Dependence on library part of gradle

If the default content is as follows:

dependencies {
    testImplementation 'junit: junit: 4.12'
    implementation 'com. Android. Support: support - v13:27.1.1'
    implementation 'com. Android. Support: support - annotations: 27.1.1'
}
Copy the code

Change all dependencies to androidX version:

dependencies {
    testImplementation 'junit: junit: 4.12'
    implementation 'androidx. Legacy: legacy support - v13:1.0.0'
    implementation 'androidx. Annotation: the annotation: 1.0.0'
}
Copy the code

After clicking Sync Now on Android Studio to synchronize

Again into the directory below your android project/my_flutter/android/Flutter/SRC/main/Java/IO/Flutter/facade/directory, Modify Flutter. Java and FlutterFragment.java, respectively

Modify FlutterFragment. Java

The original dependencies are as follows

Replace the error part with the androidX version

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
Copy the code

Modify the Flutter. Java

The original dependencies are as follows

Replace the error part with the androidX version

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
Copy the code

Now that androidx’s problems have been solved, it’s time to get ready to officially connect to Flutter

Edit entry in Flutter

Dart is the default counter page that should be added to the system. We changed some of them:

void main() => runApp(getRouter(window.defaultRouteName));

Widget getRouter(String name) {
  switch (name) {
    case 'route1':
      return MyApp();
    default:
      return Center(
        child: Text('Unknown route: $name', textDirection: TextDirection.ltr), ); }}Copy the code

Change the entry to enter by naming “route1”

The next step is to do it in Android

Add Flutter to Android

To enter the Android project, in the MainActivity, we do the following:

        bt_flutter.setOnClickListener {
            val flutterView = Flutter.createView(
                this@MainActivity,
                lifecycle,
                "route1"
            )
            val layout = ConstraintLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            layout.leftMargin = 0
            layout.bottomMargin = 26
            addContentView(flutterView, layout)

        }
Copy the code

As you can see from the code above, we display the counter page of flutter through a button click event. The actual effect is as follows:

Then android access to flutter ends. Here is how to access Android in Flutter

Access the Android interface in Flutter

We can create a Flutter project to test this example

Because kotin is used, use the following command

flutter create -a kotlin counter_native
Copy the code

Once the project has been created, we can start. Before we start, we can know how to get android data in Flutter

Getting Android data

So, how do you get the data, mostly with MethodChannel

Take a look at the code for MainActivity in Android

class MainActivity: FlutterActivity() {

  private val channelName = "samples.flutter.io/counter_native";

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
    MethodChannel(flutterView, channelNameTwo).setMethodCallHandler { methodCall, result ->
      when(methodCall.method){
        "getCounterData" -> {
          result.success(getCounterData())
        }
        else -> {
          result.notImplemented();
        }
      }
    }
  }

  private fun getCounterData():Int{
    return100; }}Copy the code

In the result callback for MethodChannel, we filter and return 100 if the method name is getCounterData

Next write the following code in Flutter:

static const platform =
      const MethodChannel('samples.flutter.io/counter_native');
      
void getCounterData() async {
    int data;
    try {
      final int result = await platform.invokeMethod('getCounterData');
      data = result;
    } on PlatformException catch (e) {
      data = -999;
    }
    setState(() {
      counterData = data;
    });
  }
Copy the code

The effect is as follows:

So much for getting Android data, here is the page to get Android

Get the android layout

Getting the android layout is much more complicated than data

Creating an Android View

In the Android project, we create a layout that we want to display in flutter. Here, we create the layout with an XML file. The R file cannot be found using XML.

class CounterView(context: Context, messenger: BinaryMessenger, id: Int)
    : PlatformView, MethodChannel.MethodCallHandler {
    
    private var methodChannel: MethodChannel =
            MethodChannel(messenger, "samples.flutter.io/counter_view_$id")
    private var counterData: CounterData = CounterData()
    private var view: View = LayoutInflater.from(context).inflate(R.layout.test_layout, null);
    private var myText: TextView


    init {
        methodChannel.setMethodCallHandler(this)
        myText = view.findViewById(R.id.tv_counter)
    }


    override fun getView(): View {
        return view
    }

    override fun dispose() {

    }

    override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
        when (methodCall.method) {
            "increaseNumber" -> {
                counterData.counterData++
                myText.text = "The current Android Text value is:${counterData.counterData}"
                result.success(counterData.counterData)
            }
            "decreaseNumber" -> {
                counterData.counterData--
                myText.text = "The current Android Text value is:${counterData.counterData}"
                result.success(counterData.counterData)
            }
            "decreaseSize"- > {if(myText.textSize > 0){
                    val size = myText.textSize
                    myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size-1)
                    result.success(myText.textSize)
                } else{
                    result.error("Error"."The size couldn't be smaller!", null)
                }
            }
            "increaseSize"- > {if(myText.textSize < 100){
                    val size = myText.textSize
                    myText.setTextSize(TypedValue.COMPLEX_UNIT_PX,size+1)
                    result.success(myText.textSize)
                } else{
                    result.error("Error"."Size couldn't be bigger!", null)
                }
            }
            "setText" -> {
                myText.text = (methodCall.arguments as String)
                result.success(myText.text)
            }
            else-> { result.notImplemented(); }}}}Copy the code

The above CounterData class is a class for storing data creation:

class CounterData(var counterData: Int = 0) {
}
Copy the code

Next, we create a CounterViewFactory class to retrieve the layout:

class CounterViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { override fun create(context: Context, id: Int, o: Any?) : PlatformView {return CounterView(context, messenger, id)
    }
}
Copy the code

Finally, create a counterViewplugin.kt file that registers the view and initializes the entry

class CounterViewPlugin{
    fun registerWith(registrar: Registrar) {
        registrar.platformViewRegistry().registerViewFactory("samples.flutter.io/counter_view", CounterViewFactory(registrar.messenger()))
    }
}
Copy the code

Once created, register the view in MainActivity:

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    CounterViewPlugin().registerWith(flutterView.pluginRegistry.registrarFor("CounterViewPlugin"))... }Copy the code

Next, there are some things that need to be done in Flutter

Get the Android view in Flutter

To get the Android view in Flutter, you need to get it from AndroidView

  Widget build(BuildContext context) {
    if (Platform.isAndroid) {
      return AndroidView(
        viewType: 'samples.flutter.io/counter_view',
        onPlatformViewCreated: _onPlatformViewCreated,
      );
    }
    return Text(
        '$defaultTargetPlatform does not support this layout yet ');
  }
Copy the code

In the onPlatformViewCreated method, we need to create a MethodChannel that calls methods written in Android. We can encapsulate a Controller to handle this logic:

  final CounterController counterController;

  void _onPlatformViewCreated(int id) {
    if (widget.counterController == null) {
      return;
    }
    widget.counterController.setId(id);
  }
Copy the code

The following is CounterController

typedef void CounterViewCreatedCallBack(CounterController controller);


class CounterController {
  MethodChannel _channel;


  void setId(int id){
    _channel = new MethodChannel('samples.flutter.io/counter_view_$id');
    print("id");
  }

  Future increaseNumber() async {
    final int result = await _channel.invokeMethod(
      'increaseNumber',);print("result:${result}");
  }

  Future decreaseNumber() async {
    final int result = await _channel.invokeMethod(
      'decreaseNumber',); } Future increaseSize() async { final result = await _channel.invokeMethod('increaseSize',); } Future decreaseSize() async { final result = await _channel.invokeMethod('decreaseSize',); } FuturesetText(String text) async {
    final  result = await _channel.invokeMethod(
      'setText',text, ); }}Copy the code

The effect is as follows:


The appendix

A todo-list project perfect for getting started with Flutter:

Todo-List-App