chrome_extension 0.4.0 copy "chrome_extension: ^0.4.0" to clipboard
chrome_extension: ^0.4.0 copied to clipboard

Platformweb

A library for accessing the `chrome.*` APIs available in Chrome extensions.

chrome_extension.dart #

pub package Build Status

A library for accessing the chrome.* APIs available in Chrome extensions.

This allows to build Chrome extension with Dart & Flutter and to interop with the native APIs easily with a high-level type-safe interface.

The JS interop is build on top of dart:js_interop (static interop) which make it ready for future WASM compilation.

Buy Me A Coffee

Using the library #

Example #

chrome.tabs

import 'package:chrome_extension/tabs.dart';

void main() async {
  var tabs = await chrome.tabs.query(QueryInfo(
    active: true,
    currentWindow: true,
  ));
  print(tabs.first.title);
}

chrome.alarms

import 'package:chrome_extension/alarms.dart';

void main() async {
  await chrome.alarms.create('MyAlarm', AlarmCreateInfo(delayInMinutes: 2));

  var alarm = await chrome.alarms.get('MyAlarm');
  print(alarm!.name);
}

chrome.power

import 'package:chrome_extension/power.dart';

void main() {
  chrome.power.requestKeepAwake(Level.display);
}

chrome.runtime

import 'dart:js_interop';
import 'package:chrome_extension/runtime.dart';

void main() async {
  chrome.runtime.onInstalled.listen((e) {
    print('OnInstalled: ${e.reason}');
  });

  chrome.runtime.onMessage.listen((e) {
    e.sendResponse.callAsFunction(null, {'the_response': 1}.jsify());
  });
}

chrome.storage

import 'package:chrome_extension/storage.dart';

void main() async {
  await chrome.storage.local.set({'mykey': 'value'});
  var values = await chrome.storage.local.get(null /* all */);
  print(values['mykey']);
}

Available APIs #

Documentation #

Tips to build Chrome extensions with Flutter #

Here are some personal tips to build Chrome extension using the Flutter UI framework.

Develop the app using Flutter Desktop

In order to develop in a comfortable environment with hot-reload, most of the app (the UI part) should be developed using Flutter desktop.

This requires an abstraction layer between the UI and the chrome_extension APIs.

In the Desktop entry point, a fake implementation of this abstraction layer is used, like this:

// lib/main_desktop.dart
void main() {
  // Inject a fake service that doesn't use the real chrome_extension package.
  var service = FakeBookmarkService();
  runApp(MyExtensionPopup(service));
}

abstract class BookmarkService {
  Future<List<Bookmark>> getBookmarks();
}

class FakeBookmarkService implements BookmarkService {
  @override
  Future<List<Bookmark>> getBookmarks() async => [Bookmark()];
}

Launch this entry point in desktop with
flutter run -t lib/main_desktop.dart -d macos|windows|linux

And the real entry point (the one used in the actual compiled extension) looks like:

// lib/main.dart
void main() {
  var service = ChromeBookmarkService();
  runApp(MyExtensionPopup(service));
}

class ChromeBookmarkService implements BookmarkService {
  @override
  Future<List<Bookmark>> getBookmarks() async {
    // Real implementation using chrome.bookmarks
    return (await chrome.bookmarks.getTree()).map(Bookmark.new).toList();
  }
}

Build script

web/manifest.json

{
  "manifest_version": 3,
  "name": "my_extension",
  "permissions": [
    "activeTab"
  ],
  "options_page": "options.html",
  "background": {
    "service_worker": "background.dart.js"
  },
  "action": {
    "default_popup": "index.html",
    "default_icon": {
      "16": "icons-16.png"
    }
  },
  "content_security_policy": {
    "extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
  }
}
// tool/build.dart
void main() async {
  await _process.runProcess([
    'flutter',
    'build',
    'web',
    '-t',
    'web/popup.dart',
    '--csp',
    '--web-renderer=canvaskit',
    '--no-web-resources-cdn',
  ]);
  for (var script in [
    'background.dart',
    'content_script.dart',
    'options.dart'
  ]) {
    await _process.runProcess([
      Platform.resolvedExecutable,
      'compile',
      'js',
      'web/$script',
      '--output',
      'build/web/$script.js',
    ]);
  }
}

It builds the flutter app and compiles all the other Dart scripts (for example: options.dart.js, popup.dart.js, background.dart.js)

Testing

Write tests for the extension using puppeteer-dart.

import 'package:collection/collection.dart';
import 'package:puppeteer/puppeteer.dart';

void main() async {
  // Compile the extension
  var extensionPath = '...';

  var browser = await puppeteer.launch(
    headless: false,
    args: [
      '--disable-extensions-except=$extensionPath',
      '--load-extension=$extensionPath',
      // Allow to connect to puppeteer from inside your extension if needed for the testing
      '--remote-allow-origins=*',
    ],
  );

  // Find the background page target
  var targetName = 'service_worker';
  var backgroundPageTarget =
      browser.targets.firstWhereOrNull((t) => t.type == targetName);
  backgroundPageTarget ??=
      await browser.waitForTarget((target) => target.type == targetName);
  var worker = (await backgroundPageTarget.worker)!;

  var url = Uri.parse(worker.url!);
  assert(url.scheme == 'chrome-extension');
  var extensionId = url.host;

  // Go to the popup page
  await (await browser.pages)
      .first
      .goto('chrome-extension://$extensionId/popup.html');

  // Etc...
}
51
likes
160
points
3.27k
downloads

Publisher

verified publisherxaha.dev

Weekly Downloads

A library for accessing the `chrome.*` APIs available in Chrome extensions.

Repository (GitHub)
View/report issues

Documentation

API reference

Funding

Consider supporting this project:

www.buymeacoffee.com
github.com

License

BSD-2-Clause (license)

More

Packages that depend on chrome_extension