How to Add Native Code to a Flutter App using Platform Views on Android
Tags: flutter, android
Published: 2019-07-23 12:31:22

---
title: How to Add Native Code to a Flutter App using Platform Views on Android
published: true
description: Step-by-step guide about how to add native code and native components to a Flutter app
tags: flutter, android
canonical_url: https://60devs.com/how-to-add-native-code-to-flutter-app-using-platform-views-android.html
cover_image: https://60devs.com/img/flutter-android-native.png
---

In this tutorial, we learn how to add native components to a Flutter app on Android. For iOS the process is similar, but this tutorial does not cover it. We are going to add a native WebView component as an example, and we write code in Kotlin.


## Step 1. Define a wrapper component in Flutter

The component will be used throughout your app and the communication with native code will be encapsulated within. In this tutorial, we create a `WebView` component as follows:

```dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

typedef void WebViewCreatedCallback(WebViewController controller);

class WebView extends StatefulWidget {
  const WebView({
    Key key,
    this.onWebViewCreated,
  }) : super(key: key);

  final WebViewCreatedCallback onWebViewCreated;

  @override
  State<StatefulWidget> createState() => WebViewState();
}

class WebViewState extends State<WebView> {
  @override
  Widget build(BuildContext context) {
    if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
        viewType: 'webview',
        onPlatformViewCreated: _onPlatformViewCreated,
      );
    }
    // TODO add other platforms
    return Text(
        '$defaultTargetPlatform is not yet supported by the map view plugin');
  }

  void _onPlatformViewCreated(int id) {
    if (widget.onWebViewCreated == null) {
      return;
    }
    widget.onWebViewCreated(new WebViewController(id));
  }
}

class WebViewController {
  WebViewController(int id) {
    this._channel = new MethodChannel('webview$id');
  }

  MethodChannel _channel;

  Future<void> loadUrl(String url) async {
    return _channel.invokeMethod('loadUrl', url);
  }
}

```

In this code, we declare `WebView` as a stateful widget. The state of the widget is defined in `WebViewState`. In the `build` method we check that the platform is Android, and create an `AndroidView`. `viewType` is needed to be able to connect the native code to this `AndroidView`. Once the view is created, we instantiate `WebViewController` and to give control over the view to the parent code.

In `WebViewController` we create a method channel which has a unique name for every instance of the component. Communication between native code and Flutter happens via bidirectional async method channels. In `loadUrl` we send a message to a channel to invoke the `loadUrl` method of the native component and give a URL to load.

## Step 2. Define native components

Go to the `android` -> `app` -> `src` -> `main` -> `kotlin` -> `com` -> `yourPackageName`. This is where we place our native code. First, we define a native plugin in `WebViewPlugin.kt`:

```kotlin
import io.flutter.plugin.common.PluginRegistry.Registrar

object WebViewPlugin {
    fun registerWith(registrar: Registrar) {
        registrar
                .platformViewRegistry()
                .registerViewFactory(
                        "webview", WebViewFactory(registrar.messenger()))
    }
}
```

The string `webview` here should match the `viewType` used in the previous step. This code tells the app that `WebViewFactory` is used to create instances for this view type.

Next, we define the factory in `WebViewFactory.kt`:

```kotlin
import android.content.Context
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory

class WebViewFactory(private val messenger: BinaryMessenger) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {

    override fun create(context: Context, id: Int, o: Any?): PlatformView {
        return MyWebView(context, messenger, id)
    }
}
```

The factory creates instances of `MyWebView` which is our native implementation of the component. We define it in `MyWebView.kt`:

```kotlin
import android.content.Context
import android.view.View
import android.widget.TextView
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.platform.PlatformView
import android.webkit.*
import android.net.http.SslError
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.graphics.Bitmap
import android.webkit.WebViewClient

class MyWebView internal constructor(context: Context, messenger: BinaryMessenger, id: Int) : PlatformView, MethodCallHandler {
    private val webView: WebView
    private val methodChannel: MethodChannel

    override fun getView(): View {
        return webView
    }

    init {
        webView = WebView(context)

        methodChannel = MethodChannel(messenger, "webview$id")
        methodChannel.setMethodCallHandler(this)
    }

    override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) {
        when (methodCall.method) {
            "loadUrl" -> loadUrl(methodCall, result)
            else -> result.notImplemented()
        }
    }

    private fun loadUrl(methodCall: MethodCall, result: Result) {
        val url = methodCall.arguments as String
        webView.loadUrl(url)
        result.success(null)
    }

    override fun dispose() {
      // TODO dispose actions if needed
    }
}
```

In this class, we create the native `WebView` provided by Android and a method channel. We use the same name for the method channel so that messages are routed to the correct Flutter instance.

We also dispatch different calls and handle `loadUrl`. This class is the place where you can implement additional features. For example, configure the webview for your needs.

## Step 3. Register native code

The final step is to modify `android` -> `app` -> `src` -> `main` -> `kotlin` -> `com` -> `yourPackageName` -> `MainActivity.kt`. We add a registration call to register our plugin:

```kotlin
import android.os.Bundle

import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    GeneratedPluginRegistrant.registerWith(this)
    WebViewPlugin.registerWith(this.registrarFor("com.yourPackageName"))
  }
}
```

Now the plugin is complete, and you can use it in your Flutter code.

*Originally published at https://60devs.com/how-to-add-native-code-to-flutter-app-using-platform-views-android.html on 14.05.2019*