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.
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']);
}
package:chrome_extension/accessibility_features.dart
(API reference)package:chrome_extension/action.dart
(API reference)package:chrome_extension/alarms.dart
(API reference)package:chrome_extension/audio.dart
(API reference)package:chrome_extension/bookmarks.dart
(API reference)package:chrome_extension/browser_action.dart
(API reference)package:chrome_extension/browsing_data.dart
(API reference)package:chrome_extension/certificate_provider.dart
(API reference)package:chrome_extension/commands.dart
(API reference)package:chrome_extension/content_settings.dart
(API reference)package:chrome_extension/context_menus.dart
(API reference)package:chrome_extension/cookies.dart
(API reference)package:chrome_extension/debugger.dart
(API reference)package:chrome_extension/declarative_content.dart
(API reference)package:chrome_extension/declarative_net_request.dart
(API reference)package:chrome_extension/desktop_capture.dart
(API reference)package:chrome_extension/devtools_inspected_window.dart
(API reference)package:chrome_extension/devtools_network.dart
(API reference)package:chrome_extension/devtools_panels.dart
(API reference)package:chrome_extension/devtools_recorder.dart
(API reference)package:chrome_extension/document_scan.dart
(API reference)package:chrome_extension/dom.dart
(API reference)package:chrome_extension/downloads.dart
(API reference)package:chrome_extension/enterprise_device_attributes.dart
(API reference)package:chrome_extension/enterprise_hardware_platform.dart
(API reference)package:chrome_extension/enterprise_networking_attributes.dart
(API reference)package:chrome_extension/enterprise_platform_keys.dart
(API reference)package:chrome_extension/events.dart
(API reference)package:chrome_extension/extension.dart
(API reference)package:chrome_extension/extension_types.dart
(API reference)package:chrome_extension/file_browser_handler.dart
(API reference)package:chrome_extension/file_system_provider.dart
(API reference)package:chrome_extension/font_settings.dart
(API reference)package:chrome_extension/gcm.dart
(API reference)package:chrome_extension/history.dart
(API reference)package:chrome_extension/i18n.dart
(API reference)package:chrome_extension/identity.dart
(API reference)package:chrome_extension/idle.dart
(API reference)package:chrome_extension/input_ime.dart
(API reference)package:chrome_extension/instance_id.dart
(API reference)package:chrome_extension/login_state.dart
(API reference)package:chrome_extension/management.dart
(API reference)package:chrome_extension/notifications.dart
(API reference)package:chrome_extension/offscreen.dart
(API reference)package:chrome_extension/omnibox.dart
(API reference)package:chrome_extension/page_action.dart
(API reference)package:chrome_extension/page_capture.dart
(API reference)package:chrome_extension/permissions.dart
(API reference)package:chrome_extension/platform_keys.dart
(API reference)package:chrome_extension/power.dart
(API reference)package:chrome_extension/printer_provider.dart
(API reference)package:chrome_extension/printing.dart
(API reference)package:chrome_extension/printing_metrics.dart
(API reference)package:chrome_extension/privacy.dart
(API reference)package:chrome_extension/processes.dart
(API reference)package:chrome_extension/proxy.dart
(API reference)package:chrome_extension/runtime.dart
(API reference)package:chrome_extension/scripting.dart
(API reference)package:chrome_extension/search.dart
(API reference)package:chrome_extension/sessions.dart
(API reference)package:chrome_extension/side_panel.dart
(API reference)package:chrome_extension/storage.dart
(API reference)package:chrome_extension/system_cpu.dart
(API reference)package:chrome_extension/system_display.dart
(API reference)package:chrome_extension/system_memory.dart
(API reference)package:chrome_extension/system_network.dart
(API reference)package:chrome_extension/system_storage.dart
(API reference)package:chrome_extension/tab_capture.dart
(API reference)package:chrome_extension/tab_groups.dart
(API reference)package:chrome_extension/tabs.dart
(API reference)package:chrome_extension/top_sites.dart
(API reference)package:chrome_extension/tts.dart
(API reference)package:chrome_extension/tts_engine.dart
(API reference)package:chrome_extension/types.dart
(API reference)package:chrome_extension/vpn_provider.dart
(API reference)package:chrome_extension/wallpaper.dart
(API reference)package:chrome_extension/web_authentication_proxy.dart
(API reference)package:chrome_extension/web_navigation.dart
(API reference)package:chrome_extension/web_request.dart
(API reference)package:chrome_extension/windows.dart
(API reference)package:chrome_extension/usb.dart
(API reference)Here are some personal tips to build Chrome extension using the Flutter UI framework.
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();
}
}
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)
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...
}