Make your Flutter app the Google Chrome Extension
Tags: flutter, dart, webdev, showdev
Published: 2022-05-23 05:54:38

## Introduction

Lately I've been trying to emulate and create an application similar to the [**daily.dev**](https://daily.dev) application. If you don't know about **daily.dev**, in short, it's a platform that features news and articles about personalized technology for developers.

The last few years I learned and got used to building apps using Flutter & Dart. In this journey of mine, I was inspired by the **daily.dev** platform. Therefore I tried to make a Flutter application similar to **daily.dev** and make the Flutter application run as a Chrome extension application.

...

## How do I do that? 🤔


### Create your Flutter project or Use an existing Flutter project

You can create a Flutter project from scratch or use a Flutter project that you created earlier. Of course, to build a chrome extension with Flutter, **the Flutter project must support the Web platform**.

I myself have created a simple Flutter application similar to **daily.dev**. This Flutter app is made using **Flutter 2.0**

You can see on the Github repository below,


{% embed https://github.com/sahibul-nf/daily %}


### Setup manifest.json

In the **manifest.json** file located in the **/web** folder, I made changes to the file something like the following.


```json
{
  "name": "daily",
  "short_name": "daily",
  "start_url": ".",
  "display": "standalone",
  "background_color": "#0175C2",
  "theme_color": "#0175C2",
  "description": "A new Flutter project.",
  "orientation": "portrait-primary",
  "prefer_related_applications": false,
  "chrome_url_overrides": {
    "newtab": "index.html"
  },
  "version": "1.0.0",
  "content_security_policy": {
    "extension_pages": "script-src 'self' ; object-src 'self'"
  },
  "action": {
    "default_popup": "index.html",
    "default_icon": "/icons/Icon-192.png"
  },
  "manifest_version": 3
}
``` 
...


### Setup index.html

Next in the **index.html** file, I made some changes namely,

- set style **height** and **width** in the HTML tag for the size of the application display that appears when the chrome extension is clicked.


```html
<html style="height: 650px; width: 350px;">
``` 

- Next, inside the **body** tag I add the following **script** tag. And comment or delete **script** tags that are available by default there.


```html
<script src="main.dart.js" type="application/javascript"></script>
``` 

More or less the **index.html** file looks like the following.


```html
<!DOCTYPE html>
<html style="height: 650px; width: 350px;">
<head>
  <!--
    If you are serving your web app in a path other than the root, change the
    href value below to reflect the base path you are serving from.
    The path provided below has to start and end with a slash "/" in order for
    it to work correctly.
    For more details:
    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
    This is a placeholder for base href that will be replaced by the value of
    the `--base-href` argument provided to `flutter build`.
  -->
  <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.">

  <!-- iOS meta tags & icons -->
  <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="daily">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/png" href="favicon.png"/>

  <title>daily</title>
  <link rel="manifest" href="manifest.json">
</head>
<body>
  <!-- This script installs service_worker.js to provide PWA functionality to
       application. For more information, see:
       https://developers.google.com/web/fundamentals/primers/service-workers -->
  <!-- <script>
    var serviceWorkerVersion = null;
    var scriptLoaded = false;
    function loadMainDartJs() {
      if (scriptLoaded) {
        return;
      }
      scriptLoaded = true;
      var scriptTag = document.createElement('script');
      scriptTag.src = 'main.dart.js';
      scriptTag.type = 'application/javascript';
      document.body.append(scriptTag);
    }
    if ('serviceWorker' in navigator) {
      // Service workers are supported. Use them.
      window.addEventListener('load', function () {
        // Wait for registration to finish before dropping the <script> tag.
        // Otherwise, the browser will load the script multiple times,
        // potentially different versions.
        var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
        navigator.serviceWorker.register(serviceWorkerUrl)
          .then((reg) => {
            function waitForActivation(serviceWorker) {
              serviceWorker.addEventListener('statechange', () => {
                if (serviceWorker.state == 'activated') {
                  console.log('Installed new service worker.');
                  loadMainDartJs();
                }
              });
            }
            if (!reg.active && (reg.installing || reg.waiting)) {
              // No active web worker and we have installed or are installing
              // one for the first time. Simply wait for it to activate.
              waitForActivation(reg.installing || reg.waiting);
            } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
              // When the app updates the serviceWorkerVersion changes, so we
              // need to ask the service worker to update.
              console.log('New service worker available.');
              reg.update();
              waitForActivation(reg.installing);
            } else {
              // Existing service worker is still good.
              console.log('Loading app from service worker.');
              loadMainDartJs();
            }
          });
        // If service worker doesn't succeed in a reasonable amount of time,
        // fallback to plaint <script> tag.
        setTimeout(() => {
          if (!scriptLoaded) {
            console.warn(
              'Failed to load app from service worker. Falling back to plain <script> tag.',
            );
            loadMainDartJs();
          }
        }, 4000);
      });
    } else {
      // Service workers not supported. Just drop the <script> tag.
      loadMainDartJs();
    }
  </script> -->
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
``` 
...


### Build an app and let’s get a try! 🚀

After doing some of the steps above, then I build the app with the following command.

```
flutter build web --web-renderer html --csp
``` 

After the application build is complete, then open Google Chrome and go to **chrome://extensions/**

> And enable Developer mode.

![Load extension](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4vzyqfmdiw046p3cw1wp.png)  

Next, click **Load unpacked** and select the **build/web** folder which is the result of the Flutter Web application that has been built. After that, the chrome extension application will appear as shown in the image below.

![Extension already to use](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lv8unvf069fnsofkx5uy.png)
... 


### Result

Now I can run Chrome extension apps created using Flutter and it runs fine. 
Awesome 🤩

{% embed https://www.loom.com/share/47c05a4247014ec8a51426f9fc86cf9c %}

...

### Demo App

See a more complete video demo via the following video.

{% embed https://youtu.be/_DsGRS8b-rI %}

...

## Ending

This was a fantastic learning experience for me 🚀.

And finally, I can make an application that is similar to [**daily.dev**](https://daily.dev) by providing chrome extension support in the daily.dev application cloning project that I created using Flutter. Awesome 😁 🤩