Zelf
Zelf

Reputation: 2260

Force cache refresh on flutter v3.0.1 web

In the current version of flutter 3.0.1 we have a service worker in index.html. So far, I cannot find any documentation on flutter.dev for how to force a cache refresh when screens or code has updated. The only way to refresh is using browser refresh buttons, etc.

There is a lot of outdated suggestions on SO to change the version number manually by appending something here or there, but that does not apply to the serviceworker. The service worker when running flutter build web automatically gets a new version number each time you build. This does not, however, force a cache refresh in the browser.

By default this service worker js is listening for a statechange and then calling console.log('Installed new service worker.'); This is only called when you click the browser's refresh button, so it is not helpful to prompt or force a refresh for new content.

I tried using flutter build web --pwa-strategy=none and this does not affect caching either.

Any bright ideas out there to force a refresh? In other websites I've built it was very easy to version css/js files, which would force a refresh without any user interaction, but I'm not seeing any clear paths with flutter build web.

This is the serviceWorker js in web/index.html.

After running flutter build web the var serviceWorkerVersion = null will become something like var serviceWorkerVersion = '1767749895'; when deployed to hosting and live on the web. However, this serviceWorkerVersion update does not force a refresh of content.

  <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>

Upvotes: 20

Views: 9655

Answers (2)

Vitor
Vitor

Reputation: 61

You can use a Firestore listener to a specific document or Firebase Remote Config in real time, to check if needs to clear the cache.

Once it needs to clear the webpage cache, simply run:

import 'dart:html' as html show window;
html.window.location.reload();

https://firebase.google.com/docs/remote-config

Upvotes: 4

Sunn
Sunn

Reputation: 902

Have you tried this? Adding the no-cache in the index.html. Seems like working on Flutter 3.0. Keep in mind that by doing this your pwa will not work in offline mode.

<head>
...
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="pragma" content="no-cache" />
...
</head>

Upvotes: 3

Related Questions