By learning the basics of this article, you can avoid many pitfalls and easily integrate Flutter into your Android project.

Introduction to the

1. Kotlin

Kotlin, launched by JetBrains in July 2011, is a statically typed programming language for JVMS running on Java virtual machines.

Compared to Java, it can statically detect many traps, such as the common null pointer, so development is more efficient.

Furthermore, it supports variable type inference, higher-order functions, extension functions, closures,

Mixins and first-class Delegation, making it more compact than Java. While it’s not compatible with Java syntax,

But Kotlin can interact with Java code. And more importantly,

At Goofle I/O in 2017, Kotlin was also announced as the official development language for Android.

Github address: Kotlin

2. MVP

I won’t go into MVP here, which I covered in detail in my last article.

The demo is the Kotlin version, but the implementation principle is the same.

Links below interested points:

From zero to one, take you through the mysteries of MVP and make it happen yourself!

3. Flutter

Flutter, a mobile UI framework launched by Google in February 2018,

You can quickly build high quality native user interfaces on Android and iOS.

The advantages of Flutter, I won’t say any more here. It’s all available on Flutter Chinese.

There are many advantages, and of course there are many disadvantages! Although cross-platform, but for the adaptation problem, still need to be optimized and solved.

Performance is related, there is often some lag phenomenon, and for the implementation of animation, is not so ideal.

Of course, there are many other problems. After all, this is only a beta release, so these problems will be solved.

Ok, let’s get to the point, how we use Flutter in our project.

doubt

On the basis of Android’s original project to integrate and use Flutter, there must be some questions.

  1. How to display the Flutter interface on native?

  2. How does a native transmit data to a Flutter? How does the Flutter receive?

  3. How does Flutter call the native method? By what?

  4. We know that in Flutter, there is only one main entrancevoid main().

    If you’re in native interface A, you want to display oneListView. In the native interface B, one is displayedwebView.

    So what do we use in Flutter to determine what I’m loadingListVieworwebView?

implementation

Ps: If you have not installed Flutter on your computer, you are advised to install Flutter first.

Flutter download installation address

1. How to integrate Flutter within the Android native project base

  1. Open your project and findTerminal, enter the terminal command:flutter channel

    The default branch should be beta, now we need to switch to the Master branch.

    Continue with the terminal command: flutter channel master,

    After the execution, we successfully switch to the master branch. Why switch to the Master branch?

    Because when we install Flutter, we install the beta version by default.

    This version does not currently support the integration of the Flutter Module functionality into existing projects.

    Flutter create -t Module Specifies the name of the library that you want to create, flutter create -t Module

    It will prompt you “Module” is not an allowed value for option “template”.

  2. Run the terminal command to create your Flutter Library: Flutter create -t module flutter_library.

    Wait for execution, after the successful creation, the following information is displayed:

    Pay attention to: commandflutter_library“Is my name for Flutter Library. You can replace it with your name.

  3. Add Flutter_library to the Android project

    Go to the Project layer setting.gradle file and open it and add the following code:

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

    Build. Gradle in your app directory and add dependencies:

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

Now we have added the Flutter Module to the Android project successfully. Isn’t that easy? skr skr skr ……

2. How to display the Flutter interface on a native level?

Open MainActivity in our app directory and add the following code:

addContentView(Flutter.createView(this, lifecycle, "route1"),
                FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
Copy the code

The above code creates a FlutterView that fills the screen with width and height. You can think of the FlutterView as a container for displaying Flutter widgets.

What the hell is route1? I’ll explain that later, but you don’t have to worry about that right now. Now run the code and you should see something like this:

Now, we have successfully displayed the Flutter interface natively.

3. How does a native transmit data to Flutter? How does the Flutter receive?

In this case, we need EventChannel.

The purpose of this class is simply to push data from native to Flutter.

The modified Activity code looks like this:

class MainActivity : AppCompatActivity() {

    companion object {

        val GET_NAME_CHANNEL = "sample.flutter.io/get_name"

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val flutterView = Flutter.createView(this, lifecycle, "route1") addContentView(flutterView, FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); EventChannel(flutterView, GET_NAME_CHANNEL).setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(p0: Any? , events: EventChannel.EventSink?) { events? .success(getName()) } override fun onCancel(p0: Any?) { } }) } fun getName(): String? ="flutter_library"

}
Copy the code

Look at the code received by the Flutter side:

class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, this.title}) : super(key: key);

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const EventChannel eventChannel =
      EventChannel('sample.flutter.io/get_name');

  String _name = 'unknown';

  void _receiveData() {}

  @override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

  void _onEvent(Object event) {
    setState(() {
      _name = event.toString();
    });
  }

  void _onError(Object error) {
    setState(() {
      _name = 'Battery status: unknown.';
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            new Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                new Text('Flutter', key: const Key('Battery level label'), new Padding(Padding: const edgeinsets.all (16.0), child: new RaisedButton(child: const Text)'Refresh'),
                    onPressed: _receiveData,
                  ),
                ),
              ],
            ),
            new Text('Data from native push:'+ _name), ], ), ), ); }}Copy the code

Note: When creating the EventChannel object, the name passed in,

It must correspond to the name you passed in native, otherwise it will not be received. This is easy to understand.

4. How does Flutter call the native method? By what?

MethodChannel:

This class is needed when a Flutter calls a method or fetches data to the native.

Next, look at the Android implementation code, modified as follows:

class MainActivity : AppCompatActivity() {

    companion object {

        val PUSH_CHANNEL = "sample.flutter.io/push"
        val PULL_CHANNEL = "sample.flutter.io/pull"

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val flutterView = Flutter.createView(this, lifecycle, "route1") addContentView(flutterView, FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); EventChannel(flutterView, PUSH_CHANNEL).setStreamHandler(object : EventChannel.StreamHandler { override fun onListen(p0: Any? , events: EventChannel.EventSink?) { events? .success(getName()) } override fun onCancel(p0: Any?) { } }) MethodChannel(flutterView, PULL_CHANNEL).setMethodCallHandler { methodCall, result -> run {if (methodCall.method.equals("refresh")) {
                    refresh()
                    result.success("")}else {
                    result.notImplemented()
                }
            }
        }

    }

    fun getName(): String? = "flutter_library"

    fun refresh() {
        showShort("refresh")}}Copy the code

When the Flutter calls the refresh method, the Android side calls the refresh() method, which implements a simple toast and returns an empty string.

Of course, you can also do other things, such as jump to the page, animate, retrieve data, and so on.

5. Determine different routes and load different interfaces

When MainActivity loads the FlutterView, we pass in a parameter “route1”.

When you click on the createView source code, you will see this comment:

The default initialRoute is "/".
Copy the code

Looking at the source code, the default value for initialRoute is “/”. Since there is only one entry: void main(),

So that’s where the logic to load the different interfaces should be. See code implementation for details:

void main() => runApp(new MyApp(window.defaultRouteName));

class MyApp extends StatelessWidget {
  final String route;

  MyApp(this.route);

  @override
  Widget build(BuildContext context) {
    switch (route) {
      case "route1":
        return new MaterialApp(
          title: "Android-Flutter-Demo",
          home: new MyHomePage(title: 'Android-Flutter-Demo'));break;
      default:
        return Center(
          child:
              Text('Unknown route: $route', textDirection: TextDirection.ltr), ); }}}Copy the code

How about that? It’s easy, right? At this point, we have solved all four problems mentioned at the beginning of the article.

Let’s talk about my demo implementation, get the interface data on the Android side, and convert it to JSON format,

This is displayed in a list through the invocation of the Flutter side. The final effect is as follows:

Code implementation in demo, without considering actual requirements.

Just to prove that the android and Flutter co-development approach works.

Finally, here is the github demo address:

Android-Flutter-Demo

If you like, you can dot star