FlutterのWebViewでマップアプリケーションを表示してみた

![sample01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/3821e38b-9a52-8b69-ceb4-5381bda1d3cd.gif)

<br>

![flutter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/af1ed8a9-335f-834f-60e8-2e5e0cecab11.png)

![mapboxgljs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/bec5aacd-170c-fc33-2b2b-e875f9a4985c.png)


### FlutterのWebViewでマップアプリケーションを表示してみました :tada:

<br>

事前準備

- Flutterの環境準備
  - [Flutterインストール](https://flutter.dev/docs/get-started/install)

- 各バージョン
  - Flutter v1.20.2
  - Dart v2.9.1
  - Xcode v11.5
  - Android SDK v30.0.1
  - Android Studio v4.0
  - Visual Studio Code v1.48.1
  - Flutter extension v3.13.2

<br>

Flutterの環境確認

```bash
flutter doctor -v
```

![try-043_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/b88fd3a7-328c-e795-e0f4-1a2ee7f3dde4.png)

<br>

はじめに、Flutterのプロジェクトを新規作成します。

```bash
flutter create map_app
```

![try-043_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/b307eb3b-8a38-e4ae-f919-0be039dc374d.png)

<br>

Flutterでのメインコードは、基本的に「lib/main.dart」を利用します。

全体構成
![try-043_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/25ca57fb-ccfd-de3b-2665-fdf4b32dd6fe.png)
 
<br>

プロジェクト作成後、WebViewの設定をしていきます。

- ローカルファイルの準備
- WebViewプラグインのインストール

<br>

##### ローカルファイルの準備

今回は、Mapbox GL JSを手軽に始めるビルド環境の[mapboxgljs-starter](https://github.com/dayjournal/mapboxgljs-starter)をローカルファイルとして利用します。

スターターの「dist」ディレクトリ内のファイルを、「assets」ディレクトリを新規作成し格納します。

![try-043_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/bab5051d-c2c2-d245-6ecb-15e032b8b654.png)

<br>

##### WebViewプラグインのインストール

FlutterのWebViewプラグインは複数存在するのですが、今回はGoogle公式の[webview_flutter](https://pub.dev/packages/webview_flutter)をインストールします。合わせてローカルファイルの格納パスも一緒に設定します。

#### pubspec.yaml

```yaml
name: map_app
description: A new Flutter project.

publish_to: 'none'

version: 1.0.0+1

environment:
    sdk: '>=2.7.0 <3.0.0'

dependencies:
    flutter:
        sdk: flutter

    cupertino_icons: ^0.1.3

    # webview_flutterプラグイン追加
    webview_flutter: ^0.3.22+1

dev_dependencies:
    flutter_test:
        sdk: flutter

flutter:
    uses-material-design: true

    # HTMLディレクトリ指定
    assets:
        - assets/
        - assets/index.html
        - assets/app.js
```

<br>

プラグインのインストールは、Visual Studio Codeで拡張機能をいれると自動インストールされますが、下記コマンドで手動インストールも可能です。

```bash
flutter pub get
```

![try-043_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/bc9cdc72-78dd-a7fb-8d97-8f5a893f7137.png)

<br>

iOSで動作確認したい場合は、下記の設定追記が必要です。

/ios/Runner
#### Info.plist

```text
<key>io.flutter.embedded_views_preview</key>
<true/>
```

<br>

最後に、メインコードを書いていきます。

/lib
#### main.dart

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

void main() {
  runApp(MapApp());
}

// WebViewコントローラー設定
WebViewController _controller;

class MapApp extends StatefulWidget {
  @override
  _MapAppState createState() => new _MapAppState();
}

class _MapAppState extends State<MapApp> {
  String _title = 'map_app';
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'map_app',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text(_title),
        ),
        body: WebView(
          // JavaScriptを適用
          javascriptMode: JavascriptMode.unrestricted,
          // WebView生成時の処理
          onWebViewCreated: (WebViewController webViewController) async {
            _controller = webViewController;
            // HTMLファイル読み込み
            await _loadHtmlFromAssets();
          },
        ),
      ),
    );
  }

  // HTMLファイル読み込み
  Future _loadHtmlFromAssets() async {
    // ローカルファイルを指定
    String fileText = await rootBundle.loadString('assets/index.html');
    String jsCode = await rootBundle.loadString('assets/app.js');
    // HTML指定
    _controller.loadUrl(Uri.dataFromString(fileText,
            mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
        .toString());
    // JavaScript指定
    await _controller.evaluateJavascript(jsCode);
  }
}
```

<br>

webview_flutterプラグインを読み込みます。

```dart
import 'package:webview_flutter/webview_flutter.dart';
```

<br>

WebViewのコントローラーを設定します。

```dart
// WebViewコントローラー設定
WebViewController _controller;
```

<br>

画面にWebViewを読み込みます。

```dart
body: WebView(
  // JavaScriptを適用
  javascriptMode: JavascriptMode.unrestricted,
  // WebView生成時の処理
  onWebViewCreated: (WebViewController webViewController) async {
    _controller = webViewController;
    // HTMLファイル読み込み
    await _loadHtmlFromAssets();
  },
),
```

<br>

ローカルファイルを読み込みます。

```dart
Future _loadHtmlFromAssets() async {
  // ローカルファイルを指定
  String fileText = await rootBundle.loadString('assets/index.html');
  String jsCode = await rootBundle.loadString('assets/app.js');
  // HTML指定
  _controller.loadUrl(Uri.dataFromString(fileText,
          mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
      .toString());
  // JavaScript指定
  await _controller.evaluateJavascript(jsCode);
}
```

<br>

Visual Studio Codeで「実行とデバッグ」を実行し、動作確認をします。
![try-043_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/1c3a82c0-a059-af12-b4c3-8079cd25eca9.png)

<br>

実行するとマップアプリケーションが表示されます。今回はiOSの実機で表示してみました！
![sample01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60444/3821e38b-9a52-8b69-ceb4-5381bda1d3cd.gif)

<br>

### FlutterのWebViewでマップアプリケーションの表示ができました :tada:

<br>

Flutterは、GoogleのUIツールキットで、iOSやAndroid向けのアプリケーションの開発に利用されています。

FlutterでWebViewを利用するとマップアプリケーションに限らず、他のWebアプリケーションもiOSとAndroidのネイティブアプリケーションに導入できるのでゼヒ試してみて頂ければと思います！

ただ、現状ではWebViewでのマップアプリケーションとしては、動作がいまいちあやしいので実用的ではないかもしれません。ネイティブでの導入方法も今後調べてみようと思います :grinning:

<br>
<br>

Mapbox GL JSについて、他にも記事を書いています。よろしければぜひ。
<a href="https://memo.dayjournal.dev/tags/mapboxgljs" target="_blank">tags - Mapbox GL JS</a>

<br>
<br>

<a href="https://dayjournal.booth.pm" target="_blank"><img src="https://memo.dayjournal.dev/images/etc/book.png" alt="book"></a>