How to Add Native Code to a Flutter App using Platform Views on iOS
Tags: flutter, android
Published: 2019-07-23 12:36:20

---
title: How to Add Native Code to a Flutter App using Platform Views on iOS
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-ios.html
cover_image: https://60devs.com/img/flutter-ios-native.png
---

In this tutorial, we learn how to add native components to a Flutter app on iOS. For the Android guide, see [the previus post of mine](https://60devs.com/how-to-add-native-code-to-flutter-app-using-platform-views-android.html).


## 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. Note that the code is exactly the same as for Android except for an extra branch to support iOS.

```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,
      );
    } else if (defaultTargetPlatform == TargetPlatform.iOS) {
      return UiKitView(
        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 iOS, and create an `UiKitView`. `viewType` is needed to be able to connect the native code to this `UiKitView`. 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 `ios` -> `Runner`. This is where we place our native code. First, we add a registration call to `AppDelegate.swift`:

```swift
let controller = window?.rootViewController as! FlutterViewController
let webviewFactory = WebviewFactory(controller: controller)

registrar(forPlugin: "webview").register(webviewFactory, withId: "webview")
```

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.swift`:

```swift
import Foundation

public class WebviewFactory : NSObject, FlutterPlatformViewFactory {
    let controller: FlutterViewController
    
    init(controller: FlutterViewController) {
        self.controller = controller
    }
    
    public func create(
        withFrame frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?
    ) -> FlutterPlatformView {
        let channel = FlutterMethodChannel(
            name: "webview" + String(viewId),
            binaryMessenger: controller
        )
        return MyWebview(frame, viewId: viewId, channel: channel, args: args)
    }
}

```

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

```swift

import Foundation
import UIKit
import WebKit

public class MyWebview: NSObject, FlutterPlatformView, WKScriptMessageHandler, WKNavigationDelegate {
    let frame: CGRect
    let viewId: Int64
    let channel: FlutterMethodChannel
    let webview: WKWebView
    
    init(_ frame: CGRect, viewId: Int64, channel: FlutterMethodChannel, args: Any?) {
        self.frame = frame
        self.viewId = viewId
        self.channel = channel
        
        let config = WKWebViewConfiguration()
        let webview = WKWebView(frame: frame, configuration: config)

        self.webview = webview

        super.init()
        
        channel.setMethodCallHandler({
            (call: FlutterMethodCall, result: FlutterResult) -> Void in
            if (call.method == "loadUrl") {
                let url = call.arguments as! String
                webview.load(URLRequest(url: URL(string: url)!))
            }
        })
    }
    
    public func view() -> UIView {
        return self.webview
    }

```

In this class, we create the native `WKWebView` provided by iOS 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.

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-ios.html*