FlutterでInAppWebViewを使う

Flutter InAppWebView Pluginの使い方について。

Flutter InAppWebView PluginはFlutterの多機能なWebViewのプラグイン。

要件

Flutter InAppWebView Pluginを使うための要件。

  • Android
    • minSdkVersion 17
    • support for androidx
  • iOS
    • –ios-language swift
    • Xcode version >= 12

インストール

flutter pub add flutter_inappwebview

設定

AndroidのminSdkVersionの設定

android/app/build.gradleを編集し、android.defaultConfig.minSdkVersionを17以上にする。

android {
    defaultConfig {
        // minSdkVersion flutter.minSdkVersion
        minSdkVersion 17

Androidのネットワークの権限の設定

android/app/src/main/AndroidManifest.xmlを編集する。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.my_inappwebview_sample">

    <uses-permission android:name="android.permission.INTERNET"/> // 追加

Androidのnet::ERR_CLEARTEXT_NOT_PERMITTEDエラー対策

開発サーバーはhttpなので、httpでアクセスできるようにする。

android/app/src/main/AndroidManifest.xmlを編集する。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.my_inappwebview_sample">
    <application
        android:label="my_inappwebview_sample"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        android:usesCleartextTraffic="true"> // 追加

iOSのATS対応

開発サーバーはhttpなので、httpでアクセスできるようにする。

ios/Runner/Info.plistに以下を追加する。

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key><true/>
</dict>

InAppWebViewを使う

InAppWebViewのパッケージをインポートする。

import 'package:flutter_inappwebview/flutter_inappwebview.dart';

最初にWidgetsFlutterBinding.ensureInitialized()を呼ぶ。

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

ホームページを表示するシンプルなアプリの例。

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: "Flutter InAppWebView Sample",
      home: SafeArea(
        child: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        InAppWebView(
          initialUrlRequest:
            URLRequest(url: Uri.parse("https://www.gesource.jp/weblog/")),
        ),
      ],
    );
  }
}

UserAgentを設定する

InAppWebView(
  initialUrlRequest:
      URLRequest(url: Uri.parse("https://www.gesource.jp/weblog/")),
  initialOptions: InAppWebViewGroupOptions(
      crossPlatform: InAppWebViewOptions(
    userAgent: "Flutter InAppWebView Sample",
  )),
),

ディープリンク(Custom URL Scheme)でアプリを起動する

WebViewのshouldOverrideUrlLoadingイベントを取得できるように、useShouldOverrideUrlLoadingをtrueにする。

initialOptions: InAppWebViewGroupOptions(
  crossPlatform: InAppWebViewOptions(
    useShouldOverrideUrlLoading: true,
  ),

shouldOverrideUrlLoading()で、URLを判定する。

次のサンプルコードでは、URLがtest://youtueのとき、ブラウザを起動してYouTubeを開く。

URLを開くためにurl_launcherパッケージを使用している。

url_launcherのインストール

flutter pub add url_launcher

url_launcherのインポート

import 'package:url_launcher/url_launcher.dart';

サンプルコード

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        InAppWebView(
          initialUrlRequest:
              URLRequest(url: Uri.parse("https://www.gesource.jp/weblog/")),
          initialOptions: InAppWebViewGroupOptions(
            crossPlatform: InAppWebViewOptions(
              useShouldOverrideUrlLoading: true,
            ),
          ),
          shouldOverrideUrlLoading: (controller, navigationAction) async {
            var uri = navigationAction.request.url!;
            if ((uri.scheme == 'test') && (uri.host == "youtube")) {
              await launchUrl(Uri.parse('https://www.youtube.com'), mode: LaunchMode.externalApplication);
              return NavigationActionPolicy.CANCEL;
            }
            return NavigationActionPolicy.ALLOW;
          },
        ),
      ],
    );
  }
}

inputタグで画像を選択する

「<input type=”file” accept=”image/*”>」で画像を選択する。

permission_handlerを使用する。

permission_handlerのインストール。

flutter pub add permission_handler

permission_handlerのインポート。

import 'package:permission_handler/permission_handler.dart';
import 'dart:async';
import 'package:permission_handler/permission_handler.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Permission.camera.request();
  runApp(const MyApp());
}

Androidの設定

android/app/build.gradleを編集し、compileSdkVersionを33にする。

android {
//    compileSdkVersion flutter.compileSdkVersion
    compileSdkVersion 33

android/app/src/main/AndroidManifest.xmlを編集する。

<manifest >
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA" /> // 追加

    <application>
        // ここから追加
        <provider
            android:name="com.pichillilorenzo.flutter_inappwebview.InAppWebViewFileProvider"
            android:authorities="${applicationId}.flutter_inappwebview.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
        // ここまで追加

iOSの設定

ios/Runner/Info.plistに以下を追加する。

<key>NSCameraUsageDescription</key>
<string>Flutter requires acess to camera.</string>

備考

Androidでは、アクティビティが破棄されるとファイルを選択できない。

ページ遷移(戻る、進む、指定のURLを表示)

ページ遷移には、InAppWebViewControllerを使用する。

サンプルコード

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Permission.camera.request();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: "Flutter InAppWebView Sample",
      home: SafeArea(
        child: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  InAppWebViewController? webViewController;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            ElevatedButton(
                onPressed: () {
                  webViewController?.goBack();
                },
                child: const Text("Back")),
            ElevatedButton(
                onPressed: () {
                  webViewController?.goForward();
                },
                child: const Text("Forward")),
            ElevatedButton(
                onPressed: () {
                  final url = URLRequest(
                      url: Uri.parse("https://www.gesource.jp/weblog/"));
                  webViewController?.loadUrl(urlRequest: url);
                },
                child: const Text("Blog")),
          ],
        ),
        Expanded(
            child: InAppWebView(
          initialUrlRequest:
              URLRequest(url: Uri.parse("https://www.gesource.jp/weblog/")),
          onWebViewCreated: (controller) {
            webViewController = controller;
          },
        )),
      ],
    );
  }
}

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください