【簡単】FlutterでChrome拡張機能を作ってみた

【簡単】FlutterでChrome拡張機能を作ってみた

今回は、Flutterで拡張機能を作ってみようと思います!

僕が過去に、チャットワークの一括既読という拡張機能を作ったのですが、具体的な開発方法は載せていなかったので、改めてまとめてみました!

※リリースからしばらく経っているのとGyazoの有料プラン解約して画像も消えてしまいました…今使えるかは分かりません (*_ _)՞՞

Flutterプロジェクト作成してローカル起動

Chrome拡張機能なのでプラットフォームはwebでOK!

% fvm flutter create --platforms web chrome_extension_demo

以下コマンドでローカル起動してみます。

% flutter run -d chrome

いつものFlutterの初期カウンターアプリがwebで表示されました。

FLutterカウンターアプリ

manifest.jsonで拡張機能の基本設定

web/manifest.jsonを以下のように修正します。

{
  "manifest_version": 3,
  "name": "デモ拡張機能",
  "description": "デモ拡張機能です。",
  "version": "1.0.0",
  "content_security_policy": {
    "extension_pages": "script-src 'self' ; object-src 'self'"
  },
  "action": {
    "default_popup": "index.html",
    "default_icon": "/icons/sample.png"
  },
  "icons": {
    "16": "/icons/sample16.png",
    "32": "/icons/sample32.png",
    "48": "/icons/sample48.png",
    "128": "/icons/sample128.png"
  },
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["http://*/*", "https://*/*"],
      "js": ["content_script.js"]
    }
  ],
  "permissions": ["activeTab", "tabs", "scripting", "storage"],
  "host_permissions": ["http://*/*", "https://*/*"]
}

基本的な項目だけ簡単に説明します。

  • name
    • Chromeウェブストアでユーザーに表示される拡張機能の名前です。
  • description
    • 拡張機能の説明で、Chromeウェブストアにも表示されます。
  • icons
    • Chromeウェブストアなどのアイコンとして使用され、16x16px, 32x32px, 48x48px, 128x128pxのアイコンを用意する必要があります。

簡単なChrome拡張機能を実装してみる

今回は、現在表示しているタブのURLを取得して表示するという拡張機能を作ってみます。

完成系のイメージはこちら↓

現在位表示しているタブのURL取得実演

Flutterにjsを読み込む

Flutter上でjsを使用するために以下のパッケージを使用します。

このパッケージを使うことで、dart側からJavascirptの処理を直接呼び出すサポートをしてくれます。

dependencies:
  flutter:
    sdk: flutter
  js: ^0.6.5

index.htmlの整理

拡張機能を表示するhtmlの基本設定です。

content_script.jsの読み込みと、拡張機能ポップアップのサイズ(<html style="height: 500px; width: 450px">)を指定しておきましょう。

<!DOCTYPE html>
<html style="height: 500px; width: 450px">
<head>
  <base href="$FLUTTER_BASE_HREF">
  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="chrome_extension_demo">
  <link rel="apple-touch-icon" href="icons/sample.png">
  <link rel="icon" type="image/png" href="icons/sample.png" />
  <title>chrome_extension_demo</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <script type="application/javascript" src="main.dart.js"></script>
  <script type="application/javascript" src="content_script.js"></script>
</body>
</html>

Chrome APIを使用してタブの情報を取得

タブのURLを取得するにあたり、chrome.tabs API を使用してブラウザのタブシステムを操作します。

https://developer.chrome.com/docs/extensions/reference/api/tabs?hl=ja#get-the-current-tab

この API を使用すると、ブラウザのタブを作成、変更、並べ替えなどができます。

web/content_script.js

async function getUrl() {
  let queryOptions = { active: true, lastFocusedWindow: true };
  let [tab] = await chrome.tabs.query(queryOptions);
  return tab.url;
}

main.dartでアプリUIと機能作成

lib/main.dart

import 'package:flutter/material.dart';
import './content_script.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String currentUrl = 'URL未取得';

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () async {
                var url = await getUrl();
                setState(() {
                  currentUrl = url;
                });
              },
              child: const Text('このページのURLを取得・表示'),
            ),
            const SizedBox(height: 32),
            Text(
              currentUrl,
              style: const TextStyle(fontSize: 16, color: Colors.black),
            ),
          ],
        ),
      ),
    );
  }
}

lib/content_script.dart

ここでは、jsパッケージを使用してjsの呼び出しをしています。

@JS()
library chrome_api;

import 'package:js/js.dart';
import 'package:js/js_util.dart';

@JS('getUrl')
external Object _getUrl();

Future<String> getUrl() async {
  return promiseToFuture<String>(_getUrl());
}

ここまでできたらあとはビルドするだけです。

ちなみに、flutter runで表示の確認はできますが、Chrome APIの実行はローカルでできませんので注意してください。

ビルドする

拡張機能としてビルドします。

%  flutter build web --web-renderer html --csp                                                                                                           13:02:15

Compiling lib/main.dart for the Web...
✓ Built build/web

Chrome拡張機能として読み込む

chrome://extensions/ ページを開きます。

右上の「デベロッパーモード」をオンにしておきましょう。

Chrome拡張機能

そして、「パッケージ化されていない拡張機能を読み込む」を選択すると、ファイル選択のポップアップが表示されるので、ソースコードの「buildフォルダ」内にある「webフォルダ」を選択して決定してください。

フォルダ

以下のように追加されていればOKです。

デモ拡張機能アプリを読み込み

実際に拡張機能を使ってみる

実際に確認してみます。

拡張機能の表示確認

ボタンをクリックすると…

現在位表示しているタブのURL取得実演

URLが取得できていますね!

まとめ

いかがでしたか?

Flutterでも意外と簡単に拡張機能を作ることができました!

Flutter信者はぜひ試してみてください!

番外編:Chromeウェブストアへリリースもできる

Chromeウェブストアのデベロッパーアカウントを作成($5の費用がかかります)してリリースすることもできます。

今回はそこまでやりませんが、興味ある方はぜひ。

Anycloudではプロダクト開発の支援を行っています

プロダクト開発をお考えの方はぜひAnycloudにご相談ください。

まずは相談する

記事を書いた人

Matsuura

エンジニア

Matsuura

Twitter

Anycloudでエンジニアしてます!主にFlutterやWebフロントをやっていて、最近はバックエンドやインフラに挑戦・苦戦中。